pax_global_header00006660000000000000000000000064123175035740014521gustar00rootroot0000000000000052 comment=3b6d3e741ec9ce68774ce7fa70398829ad26a064 ganglia-web-3.6.1/000077500000000000000000000000001231750357400137055ustar00rootroot00000000000000ganglia-web-3.6.1/.gitignore000066400000000000000000000001031231750357400156670ustar00rootroot00000000000000conf.php conf_default.php ganglia-web.spec version.php apache.conf ganglia-web-3.6.1/AUTHORS000066400000000000000000000002231231750357400147520ustar00rootroot00000000000000Federico Sacerdoti Matt Massie Vladimir Vuksan Jeff Buchbinder Dave Rawks ganglia-web-3.6.1/COPYING000066400000000000000000000030231231750357400147360ustar00rootroot00000000000000Copyright (c) 2001-2011, The Regents of the University of California All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the University of California nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ganglia-web-3.6.1/Makefile000066400000000000000000000052741231750357400153550ustar00rootroot00000000000000########################################################## # User configurables: ########################################################## # Location where gweb should be installed to (excluding conf, dwoo dirs). GDESTDIR = /usr/share/ganglia-webfrontend # Location where default apache configuration should be installed to. GCONFDIR = /etc/ganglia-web # Gweb statedir (where conf dir and Dwoo templates dir are stored) GWEB_STATEDIR = /var/lib/ganglia-web # Gmetad rootdir (parent location of rrd folder) GMETAD_ROOTDIR = /var/lib/ganglia APACHE_USER = www-data ########################################################## # Gweb version GWEB_VERSION = 3.6.1 DIST_NAME = ganglia-web DIST_DIR = $(DIST_NAME)-$(GWEB_VERSION) DIST_TARBALL = $(DIST_DIR).tar.gz TARGETS = conf_default.php ganglia-web.spec version.php apache.conf all: default default: $(TARGETS) clean: rm -rf $(TARGETS) $(DIST_DIR) $(DIST_TARBALL) rpmbuild conf_default.php: conf_default.php.in sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebstatedir@|$(GWEB_STATEDIR)|g" conf_default.php.in > conf_default.php ganglia-web.spec: ganglia-web.spec.in sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ -e "s|@vargwebdir@|$(GWEB_STATEDIR)|" -e "s|@varapacheuser@|$(APACHE_USER)|g" -e "s|@etcdir@|$(GCONFDIR)|g" ganglia-web.spec.in > ganglia-web.spec version.php: version.php.in sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ version.php.in > version.php apache.conf: apache.conf.in sed -e "s|@GDESTDIR@|$(GDESTDIR)|g" apache.conf.in > apache.conf dist-dir: default rsync --exclude "rpmbuild" --exclude "*.gz" --exclude "Makefile" --exclude "*debian*" --exclude "$(DIST_DIR)" --exclude ".git*" --exclude "*.in" --exclude "*~" --exclude "#*#" --exclude "ganglia-web.spec" --exclude "apache.conf" -a . $(DIST_DIR) install: dist-dir mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/compiled && \ mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/cache && \ mkdir -p $(DESTDIR)/$(GWEB_STATEDIR) && \ rsync -a $(DIST_DIR)/conf $(DESTDIR)/$(GWEB_STATEDIR) && \ mkdir -p $(DESTDIR)/$(GDESTDIR) && \ rsync --exclude "conf" -a $(DIST_DIR)/* $(DESTDIR)/$(GDESTDIR) && \ chown -R $(APACHE_USER):$(APACHE_USER) $(DESTDIR)/$(GWEB_STATEDIR) dist-gzip: dist-dir if [ -f $(DIST_TARBALL) ]; then \ rm -rf $(DIST_TARBALL) ;\ fi ;\ tar -czf $(DIST_TARBALL) $(DIST_DIR)/* rpm: dist-gzip ganglia-web.spec apache.conf rm -rf rpmbuild mkdir rpmbuild mkdir rpmbuild/SOURCES mkdir rpmbuild/BUILD mkdir rpmbuild/RPMS mkdir rpmbuild/SRPMS cp $(DIST_TARBALL) rpmbuild/SOURCES cp apache.conf rpmbuild/SOURCES rpmbuild --define '_topdir $(PWD)/rpmbuild' --define 'custom_web_prefixdir $(GDESTDIR)' -bb ganglia-web.spec uninstall: rm -rf $(DESTDIR)/$(GDESTDIR) $(DESTDIR)/$(GWEB_STATEDIR) ganglia-web-3.6.1/README000066400000000000000000000002561231750357400145700ustar00rootroot00000000000000This is an attempt to make the Ganglia UI more usable. Installation instructions can be found here http://sourceforge.net/apps/trac/ganglia/wiki/ganglia-web-2#Installation ganglia-web-3.6.1/TODO000066400000000000000000000034221231750357400143760ustar00rootroot00000000000000General - Add back the Ganglia logo - Provide an easy method for user to add their organization's logo - Centralize sanitization of user input (Extract this from get_context.php and graph.php, no other scripts should use $_GET directly) - Make indentation consistent - mixed usage of 2 spaces, 3 spaces, 4 spaces, or tabs makes code harder to read - In Cluster Report, when one clicks on the text box besides "Metric", it should bring up a dropbox with a list of paginated metrics and continue to show metric name hints as you type Graphs Views - Support regex expressions for metrics in regex views Suggestions from Ben Hartshorne. - Color code by host or metric, so as to easily differentiate - Do something else to sort by host or metric, such as all metrics from a specific host in a row - group by metric or group by host - right now it lists all hosts metric A, then all hosts metric B (when using the regex) Caching - Figure out a better way to cache metrics - Avoid race conditions Mobile - Add ability to specify arbitrary time ranges - Add support for multi-touch enlarging of graphs - Allow hosts to be named the same in multiple clusters (right now they are filtered out) - Mobile browser detection code to automatically use mobile version - Clicking on a metric search result should bring up the host page anchoring at the metric graph in question - Add ability to add metrics to views - Implement "refresh" for pages to prevent stale data from being displayed via ajax - Fix XSS vuln. in input handling (See general 'Centralize sanitization' task above) Graphite - Add polish to Graphite integration - Fix CPU report for Cluster summary. Needs to be "scaled" by number of nodes Documentation - Add user guide - Add installation guide (expand on README) - Add templating guide ganglia-web-3.6.1/actions.php000066400000000000000000000057521231750357400160670ustar00rootroot00000000000000
Host regular expression
Metric regular expression
Hostname
Metric/Report

Add graph to view:
$value ) { if ( is_array($value) ) { foreach ( $value as $index => $value2 ) { print ''; } } else { print ''; } } } else { // If hostname is not set we assume we are dealing with aggregate graphs print ""; $metric_name=$_GET['metric_name']; print ""; print ""; if (isset($_GET['vl']) && ($_GET['vl'] !== '')) print ""; if (isset($_GET['ti']) && ($_GET['ti'] !== '')) print ""; print "
Optional thresholds to displayWarning
Critical
"; } ?>
ganglia-web-3.6.1/aggregate_graphs.php000066400000000000000000000133171231750357400177150ustar00rootroot00000000000000

Create aggregate graphs

Title:
Vertical (Y-Axis) label:
LimitsUpper:Lower:
Host Regular expression e.g. web-[0,4], web or (web|db):
Metric Regular expression (not a report e.g. load_one, bytes_(in|out)):
Graph Type:
Legend options:
ganglia-web-3.6.1/apache.conf.in000066400000000000000000000002071231750357400164010ustar00rootroot00000000000000Alias /ganglia @GDESTDIR@ AllowOverride All Order allow,deny Allow from all Deny from none ganglia-web-3.6.1/api/000077500000000000000000000000001231750357400144565ustar00rootroot00000000000000ganglia-web-3.6.1/api/.htaccess000066400000000000000000000001721231750357400162540ustar00rootroot00000000000000RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME}\.php -f RewriteRule ^(.*)$ $1.php ganglia-web-3.6.1/api/events.php000066400000000000000000000072741231750357400165050ustar00rootroot00000000000000 this is freeform as we'll see the PHP strtotime function to // convert to unix time stamp. You can also specify now // description => Event description // host_regex => Host regular expression ie. web or web-0[2,4,5] //////////////////////////////////////////////////////////////////////////////////// // Make timestamp, description and host_regex have been supplied before proceeding header("Content-Type: text/plain"); $conf['gweb_root'] = dirname(dirname(__FILE__)); include_once $conf['gweb_root'] . "/eval_conf.php"; include_once $conf['gweb_root'] . "/functions.php"; include_once $conf['gweb_root'] . "/lib/common_api.php"; if ( ! $conf['overlay_events'] ) { api_return_error( "Events API is DISABLED. Please set \$conf['overlay_events'] = true to enable." ); } if ( $conf['auth_system'] == 'readonly' ) { api_return_error( "Events are readonly. Please set \$conf['auth_system'] to something other than readonly." ); } # If events_auth_token is specified in conf.php use that. if ( isset($conf['events_auth_token']) ) { if ( ! ( isset($_REQUEST['token']) && $conf['events_auth_token'] == $_REQUEST['token'] ) ) { api_return_error( "Error: Events Auth Token is invalid. Please check token" ); } } if ( !isset($_REQUEST['action']) ) { api_return_error( "Error: You need to specify an action at a minimum" ); } switch ( $_REQUEST['action'] ) { case "add": if ( ! isset($_REQUEST['start_time']) || ! isset($_REQUEST['summary']) || ! isset($_REQUEST['host_regex']) ) { api_return_error( "Error: You need to supply start_time, summary, host_regex at a minimum" ); } // If the time is now just insert the current time stamp. Otherwise use strtotime // to convert if ( $_REQUEST['start_time'] == "now" ) $start_time = time(); else if ( is_numeric($_REQUEST['start_time']) ) $start_time = $_REQUEST['start_time']; else $start_time = strtotime($_REQUEST['start_time']); $grid = isset($_REQUEST['grid']) ? sanitize($_REQUEST['grid']) : "*"; $cluster = isset($_REQUEST['cluster']) ? sanitize($_REQUEST['cluster']) : "*"; $description = isset($_REQUEST['description']) ? sanitize($_REQUEST['description']) : ""; // Generate a unique event ID. This is so we can reference it later $event_id = uniqid(); $event = array( "event_id" => $event_id, "start_time" => $start_time, "summary" => sanitize($_REQUEST['summary']), "grid" => $grid, "cluster" => $cluster, "host_regex" => $_REQUEST['host_regex'], "description" => $description ); if ( isset($_REQUEST['end_time']) ) { if ( $_REQUEST['end_time'] == "now" ) $end_time = time(); else if ( is_numeric($_REQUEST['end_time']) ) $end_time = $_REQUEST['end_time']; else $end_time = strtotime($_REQUEST['end_time']); $event['end_time'] = $end_time; } $message = ganglia_events_add( $event ); break; case "edit": $message = ganglia_event_modify( $_REQUEST ); break; case "remove": case "delete": if ( !isset( $_REQUEST['event_id'] ) ) { api_return_error( "No event_id has been supplied." ); } $message = ganglia_event_delete( $_REQUEST['event_id'] ); break; case "list": $message = ganglia_events_get(); break; default: api_return_error( "No valid action specified" ); break; } // end of switch ( $_REQUEST['action'] ) { print json_encode($message); ?> ganglia-web-3.6.1/api/host.php000066400000000000000000000156321231750357400161530ustar00rootroot00000000000000 $v) { if ($v != null) { $a[] = $k . "=" . urlencode($v); } } return ( !empty($conf['external_location']) ? $conf['external_location'] . '/' : "" ) . $page . "?" . join("&", $a); } switch ( $_GET['action'] ) { case 'list': $rrd_dir = $conf['rrds']; $rrd_escaped = str_replace('/', '\/', $rrd_dir); $cmd = "find " . escapeshellarg($rrd_dir) . " -type d | grep -v __SummaryInfo__ | sed -e 's/^${rrd_escaped}\///'"; $l = explode( "\n", `$cmd` ); $clusters = array(); $hosts = array(); foreach ($l AS $v) { if ($v == $rrd_dir) { continue; // skip base directory } if (strpos($v, "/") === false) { continue; // skip clusters } if (strpos($v, ";") !== false) { continue; // skip weird invalid directories } list( $_cluster, $_host ) = split( '/', $v ); $hosts[$_host]['clusters'][] = $_cluster; $clusters[$_cluster][] = $_host; } api_return_ok(array( 'clusters' => $clusters , 'hosts' => $hosts )); break; // end list case 'get': retrieve_metrics_cache(); if ($debug == 1) { //print "
"; print_r($metrics); print "
"; } $r = array('graph' => array()); $default_reports = array("included_reports" => array(), "excluded_reports" => array()); if ( is_file($conf['conf_dir'] . "/default.json") ) { $default_reports = array_merge($default_reports, json_decode(file_get_contents($conf['conf_dir'] . "/default.json"), TRUE)); } $host_file = $conf['conf_dir'] . "/host_" . $hostname . ".json"; $override_reports = array("included_reports" => array(), "excluded_reports" => array()); if ( is_file($host_file) ) { $override_reports = array_merge($override_reports, json_decode(file_get_contents($host_file), TRUE)); } // Merge arrays $reports["included_reports"] = array_merge( $default_reports["included_reports"] , $override_reports["included_reports"]); $reports["excluded_reports"] = array_merge($default_reports["excluded_reports"] , $override_reports["excluded_reports"]); // Remove duplicates $reports["included_reports"] = array_unique($reports["included_reports"]); $reports["excluded_reports"] = array_unique($reports["excluded_reports"]); $additional_cluster_img_html_args = array(); $additional_cluster_img_html_args['h'] = $hostname; $additional_cluster_img_html_args['st'] = $cluster[LOCALTIME]; $additional_cluster_img_html_args['m'] = $metricname; $additional_cluster_img_html_args['r'] = $range; $additional_cluster_img_html_args['s'] = $sort; if ($jobrange and $jobstart) { $additional_cluster_img_html_args['jr'] = $jobrange; $additional_cluster_img_html_args['js'] = $jobstart; } if ($cs) { $additional_cluster_img_html_args['cs'] = $cs; } if ($ce) { $additional_cluster_img_html_args['ce'] = $ce; } if ( isset($conf['zoom_support']) && $conf['zoom_support'] === true ) { $additional_cluster_img_html_args['class'] = "cluster_zoomable"; } foreach ( $reports["included_reports"] AS $index => $report_name ) { if ( ! in_array( $report_name, $reports["excluded_reports"] ) ) { $graph = array(); // Form image URL $graph_arguments = $additional_cluster_img_html_args; $graph_arguments['z'] = 'medium'; $graph_arguments['c'] = $cluster_url; $graph_arguments['g'] = $report_name; $graph['graph_image'] = array ( 'script' => 'graph.php' , 'params' => $graph_arguments ); // Form page URL $graph_arguments = $additional_cluster_img_html_args; $graph_arguments['z'] = 'large'; $graph_arguments['c'] = $cluster_url; $graph_arguments['g'] = $report_name; $graph['graph_page'] = array ( 'script' => 'graph_all_periods.php' , 'params' => $graph_arguments ); $graph['graph_url'] = form_image_url ( 'graph.php', $graph_arguments ); // Add graph $r['graph'][] = $graph; } // if ! excluded } // end foreach included reports // Pull all rrds $rrd_cmd = 'ls -1 ' . escapeshellarg( $conf['rrds'] . DIRECTORY_SEPARATOR . $cluster_url . DIRECTORY_SEPARATOR . $hostname . DIRECTORY_SEPARATOR ) . "*.rrd"; $rrds_raw = explode( "\n", `$rrd_cmd` ); foreach ($rrds_raw AS $v) { $rrd = str_replace(".rrd", "", basename( $v )); $size = isset($clustergraphsize) ? $clustergraphsize : 'default'; $size = $size == 'medium' ? 'default' : $size; // set to 'default' to preserve old behavior $graph_arguments = array(); $graph_arguments['h'] = $hostname; $graph_arguments['c'] = $cluster_url; $graph_arguments['v'] = $metrics[$cluster_url][VAL]; $graph_arguments['m'] = $rrd; $graph_arguments['r'] = $range; $graph_arguments['z'] = $size; $graph_arguments['jr'] = $jobrange; $graph_arguments['js'] = $jobstart; $graph_arguments['st'] = $cluster[LOCALTIME]; # Adding units to graph 2003 by Jason Smith . if ($v['UNITS']) { $graph_arguments['vl'] = $metrics[$cluster_url]['UNITS']; } if (isset($v['TITLE'])) { $graph_arguments['ti'] = $metrics[$cluster_url]['TITLE']; } $graph['description'] = isset($metrics[$cluster_url]['DESC']) ? $metrics[$cluster_url]['DESC'] : ''; $graph['title'] = isset($metrics[$cluster_url]['TITLE']) ? $metrics[$cluster_url]['TITLE'] : $rrd; # Setup an array of groups that can be used for sorting in group view if ( isset($metrics[$name]['GROUP']) ) { $groups = $metrics[$name]['GROUP']; } else { $groups = array(""); } $graph['graph_image'] = array ( 'script' => 'graph.php' , 'params' => $graph_arguments ); $graph['graph_url'] = form_image_url ( 'graph.php', $graph_arguments ); $r['graph'][] = $graph; } // end foreach metrics if ($debug) { print "
"; print_r($r); die("
"); } api_return_ok($r); break; // end get default: api_return_error("Invalid action."); break; // bad action } // end case action ?> ganglia-web-3.6.1/api/metrics.php000066400000000000000000000055511231750357400166430ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $host_found = 0; # Find a FQDN of a supplied server name. for ( $i = 0 ; $i < sizeof($ganglia_hosts_array) ; $i++ ) { if ( !strcasecmp( $ganglia_hosts_array[$i], $host ) ) { $fqdn = $ganglia_hosts_array[$i]; $host_found = 1; break; } } # Host has been found in the Ganglia tree if ( $host_found == 1 ) { # Check for the existence of a metric if ( isset($metrics[$fqdn][$metric_name]['VAL']) ) { $metric_value = $metrics[$fqdn][$metric_name]['VAL']; } else { api_return_error($metric_name . " - Invalid metric request for this host. Please check metric exists."); exit(3); } $ganglia_units = $metrics[$fqdn][$metric_name]['UNITS']; api_return_ok(array( 'metric_value' => $metric_value , 'units' => $ganglia_units )); } else { api_return_error($metric_name . " - Hostname info not available. Likely invalid hostname"); } ?> ganglia-web-3.6.1/api/metrics_autocomplete.php000066400000000000000000000061161231750357400214220ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } unset($metrics); file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); $metrics = $new_metrics; unset($new_metrics); } if ( isset($_GET['term']) ) { $term = $_GET['term']; if (count($metrics)) { foreach ($metrics as $firsthost => $bar) { foreach ($metrics[$firsthost] as $m => $foo) $context_metrics[$m] = $m; } foreach ($reports as $r => $foo) $context_metrics[] = $r; } if (is_array($context_metrics)) { $picker_metrics = array(); # Find all the optional reports if ($handle = opendir($conf['gweb_root'] . '/graph.d')) { // If we are using RRDtool reports can be json or PHP suffixes if ( $conf['graph_engine'] == "rrdtool" ) $report_suffix = "php|json"; else $report_suffix = "json"; while (false !== ($file = readdir($handle))) { if ( preg_match("/(.*)(_report)\.(" . $report_suffix .")/", $file, $out) ) { if ( ! in_array($out[1] . "_report", $context_metrics) ) $context_metrics[] = $out[1] . "_report"; } } closedir($handle); } sort($context_metrics); $c = 0; foreach ($context_metrics as $key) { $url = rawurlencode($key); if (stripos($key, $term) !== false) { if ($c > 30) { break; } $picker_metrics[] = array( 'value' => $url, 'label' => $key, 'id' => $key ); $c++; } } } api_return_ok($picker_metrics); } else { api_return_error("No valid search provided"); } ?> ganglia-web-3.6.1/api/rundeck.php000066400000000000000000000032121231750357400166200ustar00rootroot00000000000000 $metric_array ) { if ( isset($metric_array['os_name']['VAL']) ) { print "$node: description: Rundeck node hostname: $node nodename: $node osArch: " . $metric_array['machine_type']['VAL'] . " osFamily: unix osName: " . $metric_array['os_name']['VAL'] . " osVersion: " . $metric_array['os_release']['VAL'] . " tags: '' username: root "; } } ?>ganglia-web-3.6.1/api/search.php000066400000000000000000000017521231750357400164410ustar00rootroot00000000000000 ganglia-web-3.6.1/api/views.php000066400000000000000000000147611231750357400163350ustar00rootroot00000000000000 $view ) { if ( $view['view_name'] == $_GET['view_name'] ) { $found_view = true; } } if (!$found_view) { api_return_error("That view does not exist."); } $view_suffix = str_replace(" ", "_", $_GET['view_name']); $view_filename = $conf['views_dir'] . "/view_" . $view_suffix . ".json"; $this_view = json_decode(file_get_contents($view_filename), TRUE); api_return_ok($this_view); break; // end get case 'list': $views = get_available_views(); $view_list = array(); foreach ($views AS $k => $view) { if ($view['view_name'] != '') $view_list[] = $view['view_name']; } api_return_ok($view_list); break; // end list case 'create_view': if( ! checkAccess( GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf ) ) { api_return_error("You do not have access to edit views."); } else { // Check whether the view name already exists $view_exists = 0; $available_views = get_available_views(); foreach ( $available_views as $view_id => $view ) { if ( $view['view_name'] == $_GET['view_name'] ) { $view_exists = 1; } } if ( $view_exists == 1 ) { api_return_error("View with the name ".$_GET['view_name']." already exists."); } else { $empty_view = array ( "view_name" => $_GET['view_name'], "items" => array() ); $view_suffix = str_replace(" ", "_", $_GET['view_name']); $view_filename = $conf['views_dir'] . "/view_" . $view_suffix . ".json"; $json = json_encode($empty_view); if ( file_put_contents($view_filename, json_prettyprint($json)) === FALSE ) { api_return_error("Can't write to file $view_filename. Perhaps permissions are wrong."); } else { api_return_ok("View has been created successfully."); } // end of if ( file_put_contents($view_filename, $json) === FALSE ) } // end of if ( $view_exists == 1 ) } break; // end create_view ////////////////////////////////////////////////////////////////////////////////////////////////////// // Delete view ////////////////////////////////////////////////////////////////////////////////////////////////////// case 'delete_view': if( ! checkAccess( GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf ) ) { api_return_error("You do not have access to edit views."); } else { // Check whether the view name already exists $view_exists = 0; $available_views = get_available_views(); foreach ( $available_views as $view_id => $view ) { if ( $view['view_name'] == $_GET['view_name'] ) { $view_exists = 1; } } if ( $view_exists != 1 ) { api_return_error("View with the name ".$_GET['view_name']." does not exist."); } else { $view_suffix = str_replace(" ", "_", $_GET['view_name']); $view_filename = $conf['views_dir'] . "/view_" . $view_suffix . ".json"; if ( unlink($view_filename) === FALSE ) { api_return_error("Can't remove file $view_filename. Perhaps permissions are wrong."); } else { api_return_ok("View has been successfully removed."); } } } break; // end delete_view ////////////////////////////////////////////////////////////////////////////////////////////////////// // Add to view ////////////////////////////////////////////////////////////////////////////////////////////////////// case 'add_to_view': if( ! checkAccess( GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf ) ) { api_return_error("You do not have access to edit views."); } else { $view_exists = 0; // Check whether the view name already exists $available_views = get_available_views(); foreach ( $available_views as $view_id => $view ) { if ( $view['view_name'] == $_GET['view_name'] ) { $view_exists = 1; break; } } if ( $view_exists == 0 ) { api_return_error("View ".$_GET['view_name']." does not exist. This should not happen."); } else { // Read in contents of an existing view $view_filename = $view['file_name']; // Delete the file_name index unset($view['file_name']); # Check if we are adding an aggregate graph if ( isset($_GET['aggregate']) ) { foreach ( $_GET['mreg'] as $key => $value ) $metric_regex_array[] = array("regex" => $value); foreach ( $_GET['hreg'] as $key => $value ) $host_regex_array[] = array("regex" => $value); $item_array = array( "aggregate_graph" => "true", "metric_regex" => $metric_regex_array, "host_regex" => $host_regex_array, "graph_type" => $_GET['gtype'], "vertical_label" => $_GET['vl'], "title" => $_GET['title']); if ( isset($_GET['x']) && is_numeric($_GET['x'])) { $item_array["upper_limit"] = $_GET['x']; } if ( isset($_GET['n']) && is_numeric($_GET['n'])) { $item_array["lower_limit"] = $_GET['n']; } $view['items'][] = $item_array; unset($item_array); } else { if ( $_GET['type'] == "metric" ) { $items = array( "hostname" => $_GET['host_name'], "metric" => $_GET['metric_name'] ); if (isset($_GET['vertical_label'])) $items["vertical_label"] = $_GET['vertical_label']; if (isset($_GET['title'])) $items["title"] = $_GET['title']; $view['items'][] = $items; } else $view['items'][] = array( "hostname" => $_GET['host_name'], "graph" => $_GET['metric_name']); } $json = json_encode($view); if ( file_put_contents($view_filename, json_prettyprint($json)) === FALSE ) { api_return_error("Can't write to file $view_filename. Perhaps permissions are wrong."); } else { api_return_ok("View has been updated successfully."); } // end of if ( file_put_contents($view_filename, $json) === FALSE ) } // end of if ( $view_exists == 1 ) } break; // end add_to_view } // end case action ?> ganglia-web-3.6.1/autorotation.php000066400000000000000000000130761231750357400171550ustar00rootroot00000000000000 Rotate graphs every seconds.

Go back to Ganglia
We are sleeping since it's off hours.

Adjust $office_hour_min and $office_hour_max if this makes you unhappy

"; } // end of if (!isset($_GET['view_name'] ?> ganglia-web-3.6.1/calendar.php000066400000000000000000000043751231750357400162000ustar00rootroot00000000000000 function ganglia_submit(clearonly) { document.getElementById("cs").value = ""; document.getElementById("ce").value = ""; if (! clearonly) document.ganglia_form.submit(); } '; if ( ! is_readable('./jscalendar-1.0/calendar.js') ) { $calendar = ''; } else { $calendar_head .= ' '; $calendar = ' '; } ?> ganglia-web-3.6.1/cluster_legend.html000066400000000000000000000022561231750357400175770ustar00rootroot00000000000000 Ganglia Cluster Toolkit:: Cluster Image Legend
Ganglia Cluster Image Legend
Cluster ImageMeaning
Red Over 100% Utilization. Utilization is: (1 min load) / (number of CPUs in cluster) * 100%.
Orange75-100%
Yellow50-74%
Green25-49%
Blue0-24%
GreyA private cluster.

Back

ganglia-web-3.6.1/cluster_view.php000066400000000000000000000601551231750357400171400ustar00rootroot00000000000000There was an error initializing the Dwoo PHP Templating Engine: " . $e->getMessage() . "

The compile directory should be owned and writable by the apache user."; exit; } } function get_picker_metrics($metrics, $reports, $gweb_root, $graph_engine) { $context_metrics = ""; if (count($metrics)) { foreach ($metrics as $host_metrics) { foreach ($host_metrics as $metric_name => $metric_value) { $context_metrics[$metric_name] = rawurldecode($metric_name); } } foreach ($reports as $report_name => $report_value) $context_metrics[] = $report_name; } if (!is_array($context_metrics)) return NULL; $picker_metrics = array(); // Find all the optional reports if ($handle = opendir($gweb_root . '/graph.d')) { // If we are using RRDtool reports can be json or PHP suffixes if ( $graph_engine == "rrdtool" ) $report_suffix = "php|json"; else $report_suffix = "json"; while (false !== ($file = readdir($handle))) { if (preg_match("/(.*)(_report)\.(" . $report_suffix .")/", $file, $out)) { if (!in_array($out[1] . "_report", $context_metrics)) $context_metrics[] = $out[1] . "_report"; } } closedir($handle); } sort($context_metrics); foreach ($context_metrics as $metric) { $url = rawurlencode($metric); $picker_metrics[] = ""; } return $picker_metrics; } function get_load($host, $metrics) { if (isset($metrics[$host]["cpu_num"]['VAL']) and $metrics[$host]["cpu_num"]['VAL'] != 0 ) { $cpus = $metrics[$host]["cpu_num"]['VAL']; } else { $cpus = 1; } if (isset($metrics[$host]["load_one"]['VAL']) ){ $load_one = $metrics[$host]["load_one"]['VAL']; } else { $load_one = 0; } $load = ((float) $load_one) / $cpus; return $load; } function get_load_pie($showhosts, $hosts_up, $hosts_down, $user, $conf, $metrics, $cluster, $name, $data) { if ($showhosts != 0) { $percent_hosts = array(); foreach ($hosts_up as $host => $val) { // If host_regex is defined if (isset($user['host_regex']) && ! preg_match("/" .$user['host_regex'] . "/", $host)) continue; $load = get_load($host, $metrics); if (isset($percent_hosts[load_color($load)])) { $percent_hosts[load_color($load)] += 1; } else { $percent_hosts[load_color($load)] = 1; } } foreach ($hosts_down as $host => $val) { $load = -1.0; if (isset($percent_hosts[load_color($load)])) { $percent_hosts[load_color($load)] += 1; } else { $percent_hosts[load_color($load)] = 1; } } // Show pie chart of loads $pie_args = "title=" . rawurlencode("Cluster Load Percentages"); $pie_args .= "&size=250x150"; foreach ($conf['load_colors'] as $name => $color) { if (!array_key_exists($color, $percent_hosts)) continue; $n = $percent_hosts[$color]; $name_url = rawurlencode($name); $pie_args .= "&$name_url=$n,$color"; } $data->assign("pie_args", $pie_args); } else { // Show pie chart of hosts up/down $pie_args = "title=" . rawurlencode("Host Status"); $pie_args .= "&size=250x150"; $up_color = $conf['load_colors']["25-50"]; $down_color = $conf['load_colors']["down"]; $pie_args .= "&Up=$cluster[HOSTS_UP],$up_color"; $pie_args .= "&Down=$cluster[HOSTS_DOWN],$down_color"; $data->assign("pie_args", $pie_args); } } function get_host_metric_graphs($showhosts, $hosts_up, $hosts_down, $user, $conf, $metrics, $metricname, $sort, $clustername, $get_metric_string, $cluster, $always_timestamp, $reports_metricname, $clustergraphsize, $range, $start, $end, $cs, $ce, $vlabel, $data) { $sorted_hosts = array(); $down_hosts = array(); if ($showhosts == 0) return; foreach ($hosts_up as $host => $val) { // If host_regex is defined if (isset($user['host_regex']) && ! preg_match("/" .$user['host_regex'] . "/", $host)) continue; $load = get_load($host, $metrics); $host_load[$host] = $load; if ($metricname == "load_one") $sorted_hosts[$host] = $load; else if (isset($metrics[$host][$metricname])) $sorted_hosts[$host] = $metrics[$host][$metricname]['VAL']; else $sorted_hosts[$host] = ""; } // foreach hosts_up foreach ($hosts_down as $host => $val) { $down_hosts[$host] = -1.0; } $data->assign("node_legend", 1); if (!is_array($hosts_up)) return; switch ($sort) { case "descending": arsort($sorted_hosts); break; case "by name": uksort($sorted_hosts, "strnatcmp"); break; default: case "ascending": asort($sorted_hosts); break; } $sorted_hosts = array_merge($down_hosts, $sorted_hosts); if (isset($user['max_graphs'])) $max_graphs = $user['max_graphs']; else $max_graphs = $conf['max_graphs']; // First pass to find the max value in all graphs for this // metric. The $start,$end variables comes from get_context.php, // included in index.php. // Do this only if person has not selected a maximum set of graphs to display if ($max_graphs == 0 && $showhosts == 1) { $cs = $user['cs']; if ($cs and (is_numeric($cs) or strtotime($cs))) $start = $cs; $ce = $user['ce']; if ($ce and (is_numeric($ce) or strtotime($ce))) $end = $ce; list($min, $max) = find_limits($clustername, $sorted_hosts, $metricname, $start, $end, $metrics, $conf, $rrd_options); } // Second pass to output the graphs or metrics. $i = 1; // Initialize overflow list $overflow_list = array(); $overflow_counter = 1; $cluster_url = rawurlencode($clustername); $size = isset($clustergraphsize) ? $clustergraphsize : 'small'; if ($conf['hostcols'] == 0) // enforce small size in multi-host report $size = 'small'; // set host zoom class based on the size of the graph shown if (isset($conf['zoom_support']) && $conf['zoom_support'] === true) $additional_host_img_html_args = "class=host_${size}_zoomable"; foreach ($sorted_hosts as $host => $value) { if (isset($hosts_down[$host]) and $hosts_down[$host] && isset($conf['cluster_hide_down_hosts']) && $conf['cluster_hide_down_hosts']) { // If we're hiding DOWN hosts, we skip to next iteration of the loop. continue; } $host_url = rawurlencode($host); $host_link="\"?c=$cluster_url&h=$host_url&$get_metric_string\""; $textval = ""; //echo "$host: $value, "; if (isset($hosts_down[$host]) and $hosts_down[$host]) { $last_heartbeat = $cluster['LOCALTIME'] - $hosts_down[$host]['REPORTED']; $age = $last_heartbeat > 3600 ? uptime($last_heartbeat) : "${last_heartbeat}s"; $class = "down"; $textval = "down
 Last heartbeat $age ago"; } else { if (isset($metrics[$host][$metricname])) $val = $metrics[$host][$metricname]; else $val = NULL; $class = "metric"; if ($val['TYPE']=="timestamp" or (isset($always_timestamp[$metricname]) and $always_timestamp[$metricname])) { $textval = date("r", $val['VAL']); } elseif ($val['TYPE']=="string" or $val['SLOPE']=="zero" or (isset($always_constant[$metricname]) and $always_constant[$metricname] or ($max_graphs > 0 and $i > $max_graphs))) { if (isset($reports_metricname) and $reports_metricname) // No "current" values available for reports $textval = "N/A"; else $textval = "$val[VAL]"; if (isset($val['UNITS'])) $textval .= " $val[UNITS]"; } } $graphargs = "z=$size&c=$cluster_url&h=$host_url"; if (isset($host_load[$host])) { $load_color = load_color($host_load[$host]); $graphargs .= "&l=$load_color&v=$val[VAL]"; } $graphargs .= "&r=$range&su=1&st=$cluster[LOCALTIME]"; if ($cs) $graphargs .= "&cs=" . rawurlencode($cs); if ($ce) $graphargs .= "&ce=" . rawurlencode($ce); // If we want scaling to be the same in clusterview we need to set // $max and $min values if ($showhosts == 1 && $max_graphs == 0 ) $graphargs .= "&x=$max&n=$min"; if (isset($vlabel)) $graphargs .= "&vl=" . urlencode($vlabel); if ($textval) { $cell = "" . "$host
" . "$metricname: $textval"; } else { $cell = "
$host
"; } if ($conf['hostcols'] == 0) { $pre = ""; $cell .= $pre . "load_report" . $post; $cell .= $pre . "mem_report" . $post; $cell .= $pre . "cpu_report" . $post; $cell .= $pre . "network_report" . $post; } // Check if max_graphs is set. // If it put cells in an overflow list since that one is hidden by default if ($max_graphs > 0 and $i > $max_graphs ) { $overflow_list[$host]["metric_image"] = $cell; if (! ($overflow_counter++ % $conf['hostcols']) ) { $overflow_list[$host]["br"] = ""; } else { $overflow_list[$host]["br"] = ""; } } else { $sorted_list[$host]["metric_image"] = $cell; if (! ($i++ % $conf['hostcols']) ) { $sorted_list[$host]["br"] = ""; } else { $sorted_list[$host]["br"] = ""; } } // end of if ($max_graphs > 0 and $i > $max_graphs ) { } // foreach sorted_hosts $data->assign("sorted_list", $sorted_list); // If there is an overflow list. These are hosts for which we don't show graphs // just names if (sizeof($overflow_list) > 0) { $data->assign("overflow_list_header", '

Show more hosts (' . ($overflow_counter - 1) .')

"); } else { $data->assign("overflow_list_header", ""); $data->assign("overflow_list_footer", ""); } $data->assign("overflow_list", $overflow_list); } function get_cluster_overview($showhosts, $metrics, $cluster, $range, $clustername, $data) { $cpu_num = !$showhosts ? $metrics["cpu_num"]['SUM'] : cluster_sum("cpu_num", $metrics); $data->assign("cpu_num", $cpu_num); if (isset($cluster['HOSTS_UP'])) { $data->assign("num_nodes", intval($cluster['HOSTS_UP'])); } else { $data->assign("num_nodes", 0); } if (isset($cluster['HOSTS_DOWN'])) { $data->assign("num_dead_nodes", intval($cluster['HOSTS_DOWN'])); } else { $data->assign("num_dead_nodes", 0); } $load_one_sum = !$showhosts ? $metrics["load_one"]['SUM'] : cluster_sum("load_one", $metrics); $load_five_sum = !$showhosts ? $metrics["load_five"]['SUM'] : cluster_sum("load_five", $metrics); $load_fifteen_sum = !$showhosts ? $metrics["load_fifteen"]['SUM'] : cluster_sum("load_fifteen", $metrics); if (!$cpu_num) $cpu_num = 1; $cluster_load15 = sprintf("%.0f", ((double) $load_fifteen_sum / $cpu_num) * 100); $cluster_load5 = sprintf("%.0f", ((double) $load_five_sum / $cpu_num) * 100); $cluster_load1 = sprintf("%.0f", ((double) $load_one_sum / $cpu_num) * 100); $data->assign("cluster_load", "$cluster_load15%, $cluster_load5%, $cluster_load1%"); $avg_cpu_num = find_avg($clustername, "", "cpu_num"); if ($avg_cpu_num == 0) $avg_cpu_num = 1; $cluster_util = sprintf("%.0f", ((double) find_avg($clustername, "", "load_one") / $avg_cpu_num ) * 100); $data->assign("cluster_util", "$cluster_util%"); $data->assign("range", $range); } function get_cluster_optional_reports($conf, $clustername, $get_metric_string, $localtime, $data) { $cluster_url = rawurlencode($clustername); $graph_args = "c=$cluster_url&$get_metric_string&st=$localtime"; $optional_reports = ""; // If we want zoomable support on graphs we need to add correct zoomable // class to every image $additional_cluster_img_html_args = ""; if (isset($conf['zoom_support']) && $conf['zoom_support'] === true) $additional_cluster_img_html_args = "class=cluster_zoomable"; $data->assign("additional_cluster_img_html_args", $additional_cluster_img_html_args); ############################################################################### # Let's find out what optional reports are included # First we find out what the default (site-wide) reports are then look # for host specific included or excluded reports ############################################################################### $default_reports = array("included_reports" => array(), "excluded_reports" => array()); if (is_file($conf['conf_dir'] . "/default.json")) { $default_reports = array_merge( $default_reports, json_decode(file_get_contents($conf['conf_dir'] . "/default.json"), TRUE)); } $cluster_file = $conf['conf_dir'] . "/cluster_" . str_replace(" ", "_", $clustername) . ".json"; $override_reports = array("included_reports" => array(), "excluded_reports" => array()); if (is_file($cluster_file)) { $override_reports = array_merge($override_reports, json_decode(file_get_contents($cluster_file), TRUE)); } # Merge arrays $reports["included_reports"] = array_merge($default_reports["included_reports"], $override_reports["included_reports"]); $reports["excluded_reports"] = array_merge($default_reports["excluded_reports"], $override_reports["excluded_reports"]); # Remove duplicates $reports["included_reports"] = array_unique($reports["included_reports"]); $reports["excluded_reports"] = array_unique($reports["excluded_reports"]); $cluster_url = rawurlencode($clustername); foreach ($reports["included_reports"] as $index => $report_name ) { if (! in_array( $report_name, $reports["excluded_reports"])) { $optional_reports .= " "; } } $data->assign("optional_reports", $optional_reports); $data->assign("graph_args", $graph_args); if (!isset($conf['optional_graphs'])) $conf['optional_graphs'] = array(); $optional_graphs_data = array(); foreach ($conf['optional_graphs'] as $g) { $optional_graphs_data[$g]['name'] = $g; $optional_graphs_data[$g]['graph_args'] = $graph_args; } $data->assign('optional_graphs_data', $optional_graphs_data); } function get_load_heatmap($hosts_up, $host_regex, $metrics, $data) { foreach ($hosts_up as $host => $val) { // If host_regex is defined if (isset($host_regex) && ! preg_match("/" . $host_regex . "/", $host)) continue; $load = get_load($host, $metrics); $host_load[$host] = $load; } $num_hosts = count($host_load); if ($num_hosts == 0) return; $num_cols = ceil(sqrt($num_hosts)); $col_index = 0; $row_index = 0; $heatmap = '['; foreach ($host_load as $host => $load) { if ($col_index == 0) { if ($row_index > 0) $heatmap .= ','; $heatmap .= '['; } if ($col_index > 0) $heatmap .= ','; $heatmap .= "{host:\"$host\",load:$load}"; if ($col_index == $num_cols - 1) { $heatmap .= ']'; $col_index = 0; $row_index++; } else $col_index++; } if ($col_index != 0) { for ($i = 0; $i < ($num_cols * $num_cols - $num_hosts); $i++) { $heatmap .= ",{host:\"unused\",load:0}"; } $heatmap .= ']'; } $heatmap .= ']'; $data->assign("heatmap_data", $heatmap); } $fn = "cluster_" . ($refresh ? "refresh" : "view") . ".tpl"; $tpl = new Dwoo_Template_File(template($fn)); $data = new Dwoo_Data(); if (! $refresh) { $data->assign("php_gd", (function_exists('imagegif') or function_exists('imagepng'))); $data->assign("extra", template("cluster_extra.tpl")); $data->assign("user_may_edit", checkAccess( $clustername, GangliaAcl::EDIT, $conf ) ); $data->assign("graph_engine", $conf['graph_engine']); } $data->assign("cluster", $clustername); $data->assign("localtimestamp", $cluster['LOCALTIME']); $data->assign("localtime", date("Y-m-d H:i", $cluster['LOCALTIME'])); get_cluster_overview($showhosts, $metrics, $cluster, $range, $clustername, $data); $user_metricname = $user['metricname']; if (!$showhosts) { if (array_key_exists($user_metricname, $metrics)) $units = $metrics[$user_metricname]['UNITS']; } else { if (array_key_exists($user_metricname, $metrics[key($metrics)])) if (isset($metrics[key($metrics)][$user_metricname]['UNITS'])) $units = $metrics[key($metrics)][$user_metricname]['UNITS']; else $units = ''; } if (isset($units)) $vlabel = $units; if (! $refresh) { get_cluster_optional_reports($conf, $clustername, $get_metric_string, $cluster[LOCALTIME], $data); ////////////////////////////////////////////////////////////////////////////// // Begin Host Display Controller ////////////////////////////////////////////////////////////////////////////// // Correctly handle *_report cases and blank (" ") units if (isset($units)) { if ($units == " ") $units = ""; else $units = $units ? "($units)" : ""; } else { $units = ""; } $data->assign("metric","{$user['metricname']} $units"); $data->assign("metric_name","{$user['metricname']}"); $data->assign("sort", $sort); $data->assign("range", $range); $showhosts_levels = array(1 => array('checked'=>'', 'name'=>'Auto'), 2 => array('checked'=>'', 'name'=>'Same'), 0 => array('checked'=>'', 'name'=>'None'), ); $showhosts_levels[$showhosts]['checked'] = 'checked'; $data->assign("showhosts_levels", $showhosts_levels); if ($showhosts) { $data->assign("columns_size_dropdown", 1); $data->assign("cols_menu", $cols_menu); $data->assign("size_menu", $size_menu); } if (isset($user['host_regex']) && $user['host_regex'] != "") $set_host_regex_value = "value='" . htmlentities($user['host_regex'], ENT_QUOTES) . "'"; else $set_host_regex_value = ""; // In some clusters you may have thousands of hosts which may load // for a long time. For those cases we have the ability to display // only the max amount of graphs and put place holders for the rest ie. // it will say only print host name without an image $max_graphs_options = array(1000,500,200,100,50,25,20,15,10); if (isset($user['max_graphs']) && is_numeric($user['max_graphs'])) $max_graphs = $user['max_graphs']; else $max_graphs = $conf['max_graphs']; $max_graphs_values = ""; foreach ($max_graphs_options as $value) { if ($max_graphs == $value) $max_graphs_values .= ""; else $max_graphs_values .= ""; } $data->assign("additional_filter_options", ""); if ($showhosts) { $data->assign("additional_filter_options", 'Show only nodes matching ' . '' . '
Max graphs to show
' . '
' . make_sort_menu($context, $sort) . '
'); } ////////////////////////////////////////////////////////////////////////////// // End Host Display Controller ////////////////////////////////////////////////////////////////////////////// } if ((count($hosts_up) == 0) || !(isset($conf['heatmaps_enabled']) and $conf['heatmaps_enabled'] == 1)) get_load_pie($showhosts, $hosts_up, $hosts_down, $user, $conf, $metrics, $cluster, $name, $data); if ($showhosts != 0) get_host_metric_graphs($showhosts, $hosts_up, $hosts_down, $user, $conf, $metrics, $user['metricname'], $sort, $clustername, $get_metric_string, $cluster, $always_timestamp, $reports[$user['metricname']], $clustergraphsize, $range, $start, $end, $cs, $ce, $vlabel, $data); /////////////////////////////////////////////////////////////////////////////// // Creates a heatmap /////////////////////////////////////////////////////////////////////////////// if (isset($conf['heatmaps_enabled']) and $conf['heatmaps_enabled'] == 1 and (count($hosts_up) > 0)) get_load_heatmap($hosts_up, $user['host_regex'], $metrics, $data); $data->assign("showhosts", $showhosts); // No reason to go on if we are not displaying individual hosts if (!is_array($hosts_up) or !$showhosts) { $dwoo->output($tpl, $data); return; } /////////////////////////////////////////////////////////////////////////////// // Show stacked graphs /////////////////////////////////////////////////////////////////////////////// if (isset($conf['show_stacked_graphs']) and $conf['show_stacked_graphs'] == 1 and ! preg_match("/_report$/", $user['metricname'])) { $cluster_url = rawurlencode($clustername); $stacked_args = "m={$user['metricname']}&c=$cluster_url&r=$range&st=$cluster[LOCALTIME]"; if (isset($user['host_regex'])) $stacked_args .= "&host_regex=" . $user['host_regex']; $data->assign("stacked_graph_args", $stacked_args); } if ($conf['picker_autocomplete'] == true) { $data->assign('picker_autocomplete', true); } else { $picker_metrics = get_picker_metrics($metrics, $reports, $conf['gweb_root'], $conf['graph_engine']); if ($picker_metrics != NULL) $data->assign("picker_metrics", join("", $picker_metrics)); } $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/compare_hosts.php000066400000000000000000000053561231750357400172750ustar00rootroot00000000000000assign("range",$range); $size = isset($clustergraphsize) ? $clustergraphsize : 'default'; //set to 'default' to preserve old behavior $size = $size == 'medium' ? 'default' : $size; retrieve_metrics_cache(); $matches = array(); if (array_key_exists('hreg', $_GET)) { foreach ( $_GET['hreg'] as $key => $query ) { if ($query != '') { foreach ( $index_array['hosts'] as $key => $host_name ) { if ( preg_match("/$query/i", $host_name ) ) { // We can have same hostname in multiple clusters foreach ($index_array['cluster'][$host_name] AS $clustername) { $matches[] = array ("hostname" => $host_name, "clustername" => $clustername); } } } } } } #print "
";print_r($index_array['metrics']);

$host_metrics = array();
$host_cluster = array();
foreach ( $matches as $index => $match ) {
  $hostname = $match['hostname'];
  $host_cluster[] = $match['hostname'] . "|" . $match['clustername'];
  foreach ( $index_array['metrics'] as $metric_name => $hosts ) {
    if ( array_search( $hostname , $hosts ) !== FALSE && 
         ! isset($host_metrics[$metric_name]) ) {
      $host_metrics[$metric_name] = 1; 
    }
  }
}

# Join the hosts in a list into a string which we pass to graphs
$host_list = join(",", $host_cluster);

ksort($host_metrics);
#print "
";print_r($host_metrics);

$hmetrics = array();
foreach ( $host_metrics as $name => $value )
  $hmetrics[] = $name;


$hreg = "";
if (array_key_exists('hreg', $_GET)) {
  foreach ( $_GET['hreg'] as $index => $arg ) {
    $hreg .= "&hreg[]=" . rawurlencode($arg);
  }
}

if ( isset($_GET['hreg']) ) {
  $data->assign("hreg_arg", htmlspecialchars($_GET['hreg'][0]) );
} else {
  $data->assign("hreg_arg", "");
}

$size = isset($clustergraphsize) ? $clustergraphsize : 'default';
//set to 'default' to preserve old behavior
$size = $size == 'medium' ? 'default' : $size; 

$additional_host_img_css_classes = "";
if ( isset($conf['zoom_support']) && $conf['zoom_support'] === true )
    $additional_host_img_css_classes = "host_${size}_zoomable";

$data->assign("additional_host_img_css_classes", $additional_host_img_css_classes);

$graphargs = "&r=" . $range;
if ($cs)
   $graphargs .= "&cs=" . rawurlencode($cs);
if ($ce)
   $graphargs .= "&ce=" . rawurlencode($ce);

$data->assign("hreg", $hreg);
$data->assign("graphargs", $graphargs);
$data->assign("host_list", $host_list);
$data->assign("host_metrics", $hmetrics);
$data->assign("number_of_metrics", sizeof($hmetrics));

$data->assign('GRAPH_BASE_ID', $GRAPH_BASE_ID);
$data->assign('SHOW_EVENTS_BASE_ID', $SHOW_EVENTS_BASE_ID);

$dwoo->output($tpl, $data);

?>
ganglia-web-3.6.1/conf/000077500000000000000000000000001231750357400146325ustar00rootroot00000000000000ganglia-web-3.6.1/conf/default.json000066400000000000000000000001241231750357400171460ustar00rootroot00000000000000{
	"included_reports": ["load_report","mem_report","cpu_report","network_report"]
}
ganglia-web-3.6.1/conf/event_color.json000066400000000000000000000000031231750357400200350ustar00rootroot00000000000000{}
ganglia-web-3.6.1/conf/events.json000066400000000000000000000000031231750357400170220ustar00rootroot00000000000000{}
ganglia-web-3.6.1/conf/sql/000077500000000000000000000000001231750357400154315ustar00rootroot00000000000000ganglia-web-3.6.1/conf/sql/ganglia.mysql000066400000000000000000000004321231750357400201210ustar00rootroot00000000000000
CREATE TABLE IF NOT EXISTS overlay_events (
    `event_id` SERIAL
  , `description` VARCHAR(250)
  , `summary` VARCHAR(250)
  , `grid` VARCHAR(100)
  , `cluster` VARCHAR(100)
  , `host_regex` VARCHAR(100)
  , `start_time` BIGINT DEFAULT NULL
  , `end_time` BIGINT DEFAULT NULL
);

ganglia-web-3.6.1/conf/view_default.json000066400000000000000000000000721231750357400202020ustar00rootroot00000000000000{"view_name":"default","items":[],"view_type":"standard"}
ganglia-web-3.6.1/conf_default.php.in000066400000000000000000000323601231750357400174600ustar00rootroot00000000000000 "ff634f",
   "75-100" =>"ffa15e",
   "50-75" => "ffde5e",
   "25-50" => "caff98",
   "0-25" => "e2ecff",
   "down" => "515151"
);

#
# Load scaling
#
$conf['load_scale'] = 1.0;

#
# Default color for single metric graphs
#
$conf['default_metric_color'] = "555555";

#
# Default metric 
#
$conf['default_metric'] = "load_one";

#
# remove the domainname from the FQDN hostnames in graphs
# (to help with long hostnames in small charts)
#
$conf['strip_domainname'] = false;

#
# hide down hosts from cluster view
#
$conf['cluster_hide_down_hosts'] = false;

#
# Optional summary graphs
# This function is deprecated. Please configure included reports
# in the UI or default.json. Please do not use 
#$conf['optional_graphs'] = array('packet');

# Enable Zoom support on graphs
$conf['zoom_support'] = true;


# 
# Time ranges
# Each value is the # of seconds in that range.
#
$conf['time_ranges'] = array(
   'hour'=>3600,
   '2hr'=>7200,
   '4hr'=>14400,
   'day'=>86400,
   'week'=>604800,
   'month'=>2419200,
   'year'=>31449600,
   # Needs to be an entry here to support 'r=job' in the query args to graph.php
   'job'=>0
);

# this key must exist in $conf['time_ranges']
$conf['default_time_range'] = 'hour';

# Graph Engine to use
$conf['graph_engine'] = "rrdtool";

#$conf['graph_engine'] = "graphite";
$conf['graphite_url_base'] = "http://127.0.0.1/render";
$conf['graphite_rrd_dir'] = "/opt/graphite/storage/rrd";
# Don't forget a trailing "." when specifying a prefix.
# Default is empty.
$conf['graphite_prefix'] = "";

# One of the bottlenecks is that to get individual metrics we query gmetad which
# returns every single host and all the metrics. If you have lots of hosts and lots of 
# checks this may be quite heavy so you may want to cache data
$conf['cachedata'] = 1;
$conf['cachemodule'] = 'Json';
$conf['cachefile'] = $conf['conf_dir'] . "/ganglia_metrics.cache";
$conf['cachetime'] = 1200; // How long to cache the data in seconds

# Different settings for Nagios
$conf['nagios_cache_enabled'] = 1;
$conf['nagios_cache_file'] = $conf['conf_dir'] . "/nagios_ganglia.cache";
# Cache data for how many seconds
$conf['nagios_cache_time'] = 45;

#
# Graph sizes
#
$conf['graph_sizes'] = array(
   'small'=>array(
     'height'=>65,
     'width'=>200,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   ),
   'medium'=>array(
     'height'=>95,
     'width'=>300,
     'fudge_0'=>0,
     'fudge_1'=>14,
     'fudge_2'=>28
   ),

  'large'=>array(
     'height'=>150,
     'width'=>480,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   ),

   'xlarge'=>array(
     'height'=>300,
     'width'=>650,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   ),

   'xxlarge'=>array(
     'height'=>700,
     'width'=>1150,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   ),

   'mobile'=>array(
     'height'=>95,
     'width'=>220,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   ),

   # this was the default value when no other size was provided.
   'default'=>array(
     'height'=>100,
     'width'=>400,
     'fudge_0'=>0,
     'fudge_1'=>0,
     'fudge_2'=>0
   )

);
$conf['default_graph_size'] = 'default';
$conf['graph_sizes_keys'] = array_keys( $conf['graph_sizes'] );

# sets a default graph size separate from the regular default graph size. You
# can also set this default on a per-view basis by setting 'default_size' in 
# the view .json file.
$conf['default_view_graph_size'] = 'medium';

# sets a default graph size for optional graphs separate from the regular
# default graph size.
$conf['default_optional_graph_size'] = 'medium';

# The API can serve up graphs, but the URLs should be fully qualified.
# The default value is a hack to serve up the called hostname when
# possible. It is best to define this manually.
$conf['external_location'] = "http://localhost/ganglia-2";

# In earlier versions of gmetad, hostnames were handled in a case
# sensitive manner
# If your hostname directories have been renamed to lower case,
# set this option to 0 to disable backward compatibility.
# From version 3.2, backwards compatibility will be disabled by default.
# default: true  (for gmetad < 3.2)
# default: false (for gmetad >= 3.2)
$conf['case_sensitive_hostnames'] = true;

# The following property controls whether the graphs contained in metric
# groups are initially displayed or collapsed
$conf['metric_groups_initially_collapsed'] = false;

# The following property controls whether opening a metric group is
# remembered in your sesssion
$conf['remember_open_metric_groups'] = true;

# Overlay events on graphs. Those are defined by specifying all the events
# in events.json
$conf['overlay_events'] = true;

# Settings for allowing the Nagios API to be used as an additional
# event source. Will only be enabled if overlay_nagios_events is true.
$conf['overlay_nagios_events'] = false;
$conf['overlay_nagios_base_url'] = 'http://localhost/nagios';

# If you have periodic events that happen often e.g. they will make the
# graph poorly. By default we exclude events on monthly and yearly graphs
$conf['overlay_events_exclude_ranges'] = array("month", "year");

# Overlay events line can be either dashed or solid. Use "solid" for solid
# lines, anything else (including blanks) means a dashed line.
$conf['overlay_events_line_type'] = "dashed";

# What is the provider use to provide events.
# Examples: "json", "mdb2"
$conf['overlay_events_provider'] = "json";
# Where is the Overlay events file stored
$conf['overlay_events_file'] = $conf['conf_dir'] . "/events.json";

# If using MDB2, connection string:
$conf['overlay_events_dsn'] = "mysql://dbuser:dbpassword@localhost/ganglia";

$conf['overlay_events_color_map_file'] = $conf['conf_dir'] . "/event_color.json";

# For event shading.  Value in hex, 'FF' = 100% opaque.
# the _shade_ value should be less than _tick_
$conf['overlay_events_tick_alpha']  = '30';
$conf['overlay_events_shade_alpha'] = '20';

# Colors to use e.g. in graph_colors
$conf['graph_colors'] = array("0000A3", "FF3300", "FFCC33", "00CC66", "B88A00", "33FFCC", "809900", "FF3366", "FF33CC", "CC33FF", "CCFF33", "FFFF66", "33CCFF");

# Are heatmaps enabled
$conf['heatmaps_enabled'] = 1;

# Decorated titles are of the form cluster/host, graph_title/metric, 
# time range. By setting this attribute to false the clsuter/host and 
# time range items are excluded.
$conf['decorated_graph_title'] = true;

# If set to yes, will display stacked graph (aggregated metric across hosts) in cluster view
$conf['show_stacked_graphs'] = 1;

# If set to false the grid view under the main tab will be displayed only if 
# the grid name is not "unspecified", or a parent grid has been defined.
# If set to true the grid view will always be displayed even when only a
# single cluster has been defined.
$conf['always_display_grid_view'] = true;

# In the host view, should we include/exclude optional cluster graphs?
$conf['optional_cluster_graphs_for_host_view'] = true;

# Color for the timeshift line
$conf['timeshift_line_color'] = "#FFD17F";

# Color for trend line
$conf['trend_line_color'] = "#53E2FF";

# Control whether arguments can be passed into php reports
$conf['enable_pass_in_arguments_to_optional_graphs'] = false;

# Control wether present graphs with 'bytes', 'Bytes', 'bytes/s', 'Bytes/s', 'kB', 'MB', 'GB', 'bits', 'Bits', 'bits/s', 'Bits/s'
# as vertical label have their base value set to 1024
$conf['rrdtool_base_1024'] = false;

# Metrics autocompletion picker. This loads all metrics asynchronously
# during search, rather than pre-loading at page load.
$conf['picker_autocomplete'] = false;

# Allow views to be generated on the fly by passing the json object
# along with the 'ad-hoc-view' parameter.  While there are no known
# vulnerabilities, passing a complex json object on a url may be an
# attack vector for malicious users so ad-hoc-views are disabled by
# default.
$conf['ad-hoc-views'] = false;

# Configure memcache server(s) for any memcached capabilities.
$conf['memcached_servers'] = array ( '127.0.0.1:11211' );

# Choose between tabular or calendar views
$conf['display_events_using_calendar'] = false;

# Organize views using a tree. A new attribute "parent" has been
# added to the json definition of a view that is used to specify
# the path from the root node to its desired folder, e.g. 
# "Exchange Servers/Throughtput". Individual path entries are
# separated by a forward slash.
$conf['display_views_using_tree'] = true;

# Array of pathnames that define view tree nodes that should be opened
# when the tree is first created in a browser session. Path entries are 
# separated using "--"
#$conf['view_tree_nodes_initially_open'] = array();
?>
ganglia-web-3.6.1/contrib/000077500000000000000000000000001231750357400153455ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/README.txt000066400000000000000000000005561231750357400170510ustar00rootroot00000000000000

If adding any minified third party JavaScript to
this repository, please keep a copy of the source/unminified
version here.

This is a mandatory for inclusion of this project in other
free software products and distributions.  It ensures
that it is always possible for people to modify any part
of the source or trace the origins of any component
in the project.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/000077500000000000000000000000001231750357400200145ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/AUTHORS.txt000066400000000000000000000217211231750357400217050ustar00rootroot00000000000000Authors ordered by first contribution
A list of current team members is available at http://jqueryui.com/about

Paul Bakaus 
Richard Worth 
Yehuda Katz 
Sean Catchpole 
John Resig 
Tane Piper 
Dmitri Gaskin 
Klaus Hartl 
Stefan Petre 
Gilles van den Hoven 
Micheil Bryan Smith 
Jörn Zaefferer 
Marc Grabanski 
Keith Wood 
Brandon Aaron 
Scott González 
Eduardo Lundgren 
Aaron Eisenberger 
Joan Piedra 
Bruno Basto 
Remy Sharp 
Bohdan Ganicky 
David Bolter 
Chi Cheng 
Ca-Phun Ung 
Ariel Flesler 
Maggie Costello Wachs 
Scott Jehl 
Todd Parker 
Andrew Powell 
Brant Burnett 
Douglas Neiner 
Paul Irish 
Ralph Whitbeck 
Thibault Duplessis 
Dominique Vincent 
Jack Hsu 
Adam Sontag 
Carl Fürstenberg 
Kevin Dalman 
Alberto Fernández Capel 
Jacek Jędrzejewski (http://jacek.jedrzejewski.name)
Ting Kuei 
Samuel Cormier-Iijima 
Jon Palmer 
Ben Hollis 
Justin MacCarthy 
Eyal Kobrigo 
Tiago Freire 
Diego Tres 
Holger Rüprich 
Ziling Zhao 
Mike Alsup 
Robson Braga Araujo 
Pierre-Henri Ausseil 
Christopher McCulloh 
Andrew Newcomb 
Lim Chee Aun 
Jorge Barreiro 
Daniel Steigerwald 
John Firebaugh 
John Enters 
Andrey Kapitcyn 
Dmitry Petrov 
Eric Hynds 
Chairat Sunthornwiphat 
Josh Varner 
Stéphane Raimbault 
Jay Merrifield 
J. Ryan Stinnett 
Peter Heiberg 
Alex Dovenmuehle 
Jamie Gegerson 
Raymond Schwartz 
Phillip Barnes 
Kyle Wilkinson 
Khaled AlHourani 
Marian Rudzynski 
Jean-Francois Remy 
Doug Blood 
Filippo Cavallarin 
Heiko Henning 
Aliaksandr Rahalevich 
Mario Visic 
Xavi Ramirez 
Max Schnur 
Saji Nediyanchath 
Corey Frang 
Aaron Peterson 
Ivan Peters 
Mohamed Cherif Bouchelaghem 
Marcos Sousa 
Michael DellaNoce 
George Marshall 
Tobias Brunner 
Martin Solli 
David Petersen 
Dan Heberden 
William Kevin Manire 
Gilmore Davidson 
Michael Wu 
Adam Parod 
Guillaume Gautreau 
Marcel Toele 
Dan Streetman 
Matt Hoskins 
Giovanni Giacobbi 
Kyle Florence 
Pavol Hluchý 
Hans Hillen 
Mark Johnson 
Trey Hunner 
Shane Whittet 
Edward A Faulkner 
Adam Baratz 
Kato Kazuyoshi 
Eike Send 
Kris Borchers 
Eddie Monge 
Israel Tsadok 
Carson McDonald 
Jason Davies 
Garrison Locke 
David Murdoch 
Benjamin Scott Boyle 
Jesse Baird 
Jonathan Vingiano 
Dylan Just 
Hiroshi Tomita 
Glenn Goodrich 
Tarafder Ashek-E-Elahi 
Ryan Neufeld 
Marc Neuwirth 
Philip Graham 
Benjamin Sterling 
Wesley Walser 
Kouhei Sutou 
Karl Kirch 
Chris Kelly 
Jay Oster 
Alexander Polomoshnov 
David Leal 
Igor Milla 
Dave Methvin 
Florian Gutmann 
Marwan Al Jubeh 
Milan Broum 
Sebastian Sauer 
Gaëtan Muller 
Michel Weimerskirch 
William Griffiths 
Stojce Slavkovski 
David Soms 
David De Sloovere 
Michael P. Jung 
Shannon Pekary 
Matthew Edward Hutton 
James Khoury 
Rob Loach 
Alberto Monteiro 
Alex Rhea 
Krzysztof Rosiński 
Ryan Olton 
Genie <386@mail.com>
Rick Waldron 
Ian Simpson 
Lev Kitsis 
TJ VanToll 
Justin Domnitz 
Douglas Cerna 
Bert ter Heide 
Jasvir Nagra 
Petr Hromadko 
Harri Kilpiö 
Lado Lomidze 
Amir E. Aharoni 
Simon Sattes 
Jo Liss 
Guntupalli Karunakar 
Shahyar Ghobadpour 
Lukasz Lipinski 
Timo Tijhof 
Jason Moon 
Martin Frost 
Eneko Illarramendi 
EungJun Yi 
Courtland Allen 
Viktar Varvanovich 
Danny Trunk 
Pavel Stetina 
Michael Stay 
Steven Roussey 
Michael Hollis 
Lee Rowlands 
Timmy Willison 
Karl Swedberg 
Baoju Yuan 
Maciej Mroziński 
Luis Dalmolin 
Mark Aaron Shirley 
Martin Hoch 
Jiayi Yang 
Philipp Benjamin Köppchen 
Sindre Sorhus 
Bernhard Sirlinger 
Jared A. Scheel 
Rafael Xavier de Souza 
John Chen 
Dale Kocian 
Mike Sherov 
Andrew Couch 
Marc-Andre Lafortune 
Nate Eagle 
David Souther 
Mathias Stenbom 
Sergey Kartashov 
Avinash R 
Ethan Romba 
Cory Gackenheimer 
Juan Pablo Kaniefsky 
Roman Salnikov 
Anika Henke 
Samuel Bovée 
Fabrício Matté 
Viktor Kojouharov 
Pawel Maruszczyk 
Pavel Selitskas 
Bjørn Johansen 
Matthieu Penant 
Dominic Barnes 
David Sullivan 
Thomas Jaggi 
Vahid Sohrabloo 
Travis Carden 
Bruno M. Custódio 
Nathanael Silverman 
Christian Wenz 
Steve Urmston 
Zaven Muradyan 
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/Gruntfile.js000066400000000000000000000221011231750357400223050ustar00rootroot00000000000000module.exports = function( grunt ) {

"use strict";

var
	// files
	coreFiles = [
		"jquery.ui.core.js",
		"jquery.ui.widget.js",
		"jquery.ui.mouse.js",
		"jquery.ui.draggable.js",
		"jquery.ui.droppable.js",
		"jquery.ui.resizable.js",
		"jquery.ui.selectable.js",
		"jquery.ui.sortable.js",
		"jquery.ui.effect.js"
	],

	uiFiles = coreFiles.map(function( file ) {
		return "ui/" + file;
	}).concat( expandFiles( "ui/*.js" ).filter(function( file ) {
		return coreFiles.indexOf( file.substring(3) ) === -1;
	})),

	allI18nFiles = expandFiles( "ui/i18n/*.js" ),

	cssFiles = [
		"core",
		"accordion",
		"autocomplete",
		"button",
		"datepicker",
		"dialog",
		"menu",
		"progressbar",
		"resizable",
		"selectable",
		"slider",
		"spinner",
		"tabs",
		"tooltip",
		"theme"
	].map(function( component ) {
		return "themes/base/jquery.ui." + component + ".css";
	}),

	// minified files
	minify = {
		options: {
			preserveComments: false
		},
		main: {
			options: {
				banner: createBanner( uiFiles )
			},
			files: {
				"dist/jquery-ui.min.js": "dist/jquery-ui.js"
			}
		},
		i18n: {
			options: {
				banner: createBanner( allI18nFiles )
			},
			files: {
				"dist/i18n/jquery-ui-i18n.min.js": "dist/i18n/jquery-ui-i18n.js"
			}
		}
	},

	minifyCSS = {
		options: {
			keepSpecialComments: 0
		},
		main: {
			options: {
				keepSpecialComments: '*'
			},
			src: "dist/jquery-ui.css",
			dest: "dist/jquery-ui.min.css"
		}
	},

	compareFiles = {
		all: [
			"dist/jquery-ui.js",
			"dist/jquery-ui.min.js"
		]
	};

function mapMinFile( file ) {
	return "dist/" + file.replace( /\.js$/, ".min.js" ).replace( /ui\//, "minified/" );
}

function expandFiles( files ) {
	return grunt.util._.pluck( grunt.file.expandMapping( files ), "src" ).map(function( values ) {
		return values[ 0 ];
	});
}

uiFiles.concat( allI18nFiles ).forEach(function( file ) {
	minify[ file ] = {
		options: {
			banner: createBanner()
		},
		files: {}
	};
	minify[ file ].files[ mapMinFile( file ) ] = file;
});

cssFiles.forEach(function( file ) {
	minifyCSS[ file ] = {
		options: {
			banner: createBanner()
		},
		src: file,
		dest: "dist/" + file.replace( /\.css$/, ".min.css" ).replace( /themes\/base\//, "themes/base/minified/" )
	};
});

uiFiles.forEach(function( file ) {
	// TODO this doesn't do anything until https://github.com/rwldrn/grunt-compare-size/issues/13
	compareFiles[ file ] = [ file,  mapMinFile( file ) ];
});

// grunt plugins
grunt.loadNpmTasks( "grunt-contrib-jshint" );
grunt.loadNpmTasks( "grunt-contrib-uglify" );
grunt.loadNpmTasks( "grunt-contrib-concat" );
grunt.loadNpmTasks( "grunt-contrib-qunit" );
grunt.loadNpmTasks( "grunt-contrib-csslint" );
grunt.loadNpmTasks( "grunt-contrib-cssmin" );
grunt.loadNpmTasks( "grunt-html" );
grunt.loadNpmTasks( "grunt-compare-size" );
grunt.loadNpmTasks( "grunt-git-authors" );
// local testswarm and build tasks
grunt.loadTasks( "build/tasks" );

function stripDirectory( file ) {
	return file.replace( /.+\/(.+?)>?$/, "$1" );
}

function createBanner( files ) {
	// strip folders
	var fileNames = files && files.map( stripDirectory );
	return "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " +
		"<%= grunt.template.today('isoDate') %>\n" +
		"<%= pkg.homepage ? '* ' + pkg.homepage + '\\n' : '' %>" +
		(files ? "* Includes: " + fileNames.join(", ") + "\n" : "")+
		"* Copyright <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>;" +
		" Licensed <%= _.pluck(pkg.licenses, 'type').join(', ') %> */\n";
}

grunt.initConfig({
	pkg: grunt.file.readJSON("package.json"),
	files: {
		dist: "<%= pkg.name %>-<%= pkg.version %>",
		cdn: "<%= pkg.name %>-<%= pkg.version %>-cdn",
		themes: "<%= pkg.name %>-themes-<%= pkg.version %>"
	},
	compare_size: compareFiles,
	concat: {
		ui: {
			options: {
				banner: createBanner( uiFiles ),
				stripBanners: {
					block: true
				}
			},
			src: uiFiles,
			dest: "dist/jquery-ui.js"
		},
		i18n: {
			options: {
				banner: createBanner( allI18nFiles )
			},
			src: allI18nFiles,
			dest: "dist/i18n/jquery-ui-i18n.js"
		},
		css: {
			options: {
				banner: createBanner( cssFiles ),
				stripBanners: {
					block: true
				}
			},
			src: cssFiles,
			dest: "dist/jquery-ui.css"
		}
	},
	uglify: minify,
	cssmin: minifyCSS,
	htmllint: {
		// ignore files that contain invalid html, used only for ajax content testing
		all: grunt.file.expand( [ "demos/**/*.html", "tests/**/*.html" ] ).filter(function( file ) {
			return !/(?:ajax\/content\d\.html|tabs\/data\/test\.html|tests\/unit\/core\/core\.html)/.test( file );
		})
	},
	copy: {
		dist: {
			src: [
				"AUTHORS.txt",
				"jquery-*.js",
				"MIT-LICENSE.txt",
				"README.md",
				"Gruntfile.js",
				"package.json",
				"*.jquery.json",
				"ui/**/*",
				"ui/.jshintrc",
				"demos/**/*",
				"themes/**/*",
				"external/**/*",
				"tests/**/*"
			],
			renames: {
				"dist/jquery-ui.js": "ui/jquery-ui.js",
				"dist/jquery-ui.min.js": "ui/minified/jquery-ui.min.js",
				"dist/i18n/jquery-ui-i18n.js": "ui/i18n/jquery-ui-i18n.js",
				"dist/i18n/jquery-ui-i18n.min.js": "ui/minified/i18n/jquery-ui-i18n.min.js",
				"dist/jquery-ui.css": "themes/base/jquery-ui.css",
				"dist/jquery-ui.min.css": "themes/base/minified/jquery-ui.min.css"
			},
			dest: "dist/<%= files.dist %>"
		},
		dist_min: {
			src: "dist/minified/**/*",
			strip: /^dist/,
			dest: "dist/<%= files.dist %>/ui"
		},
		dist_css_min: {
			src: "dist/themes/base/minified/*.css",
			strip: /^dist/,
			dest: "dist/<%= files.dist %>"
		},
		dist_units_images: {
			src: "themes/base/images/*",
			strip: /^themes\/base\//,
			dest: "dist/"
		},
		dist_min_images: {
			src: "themes/base/images/*",
			strip: /^themes\/base\//,
			dest: "dist/<%= files.dist %>/themes/base/minified"
		},
		cdn: {
			src: [
				"AUTHORS.txt",
				"MIT-LICENSE.txt",
				"ui/*.js",
				"package.json"
			],
			renames: {
				"dist/jquery-ui.js": "jquery-ui.js",
				"dist/jquery-ui.min.js": "jquery-ui.min.js",
				"dist/i18n/jquery-ui-i18n.js": "i18n/jquery-ui-i18n.js",
				"dist/i18n/jquery-ui-i18n.min.js": "i18n/jquery-ui-i18n.min.js"
			},
			dest: "dist/<%= files.cdn %>"
		},
		cdn_i18n: {
			src: "ui/i18n/jquery.ui.datepicker-*.js",
			strip: "ui/",
			dest: "dist/<%= files.cdn %>"
		},
		cdn_i18n_min: {
			src: "dist/minified/i18n/jquery.ui.datepicker-*.js",
			strip: "dist/minified",
			dest: "dist/<%= files.cdn %>"
		},
		cdn_min: {
			src: "dist/minified/*.js",
			strip: /^dist\/minified/,
			dest: "dist/<%= files.cdn %>/ui"
		},
		cdn_themes: {
			src: "dist/<%= files.themes %>/themes/**/*",
			strip: "dist/<%= files.themes %>",
			dest: "dist/<%= files.cdn %>"
		},
		themes: {
			src: [
				"AUTHORS.txt",
				"MIT-LICENSE.txt",
				"package.json"
			],
			dest: "dist/<%= files.themes %>"
		}
	},
	zip: {
		dist: {
			src: "<%= files.dist %>",
			dest: "<%= files.dist %>.zip"
		},
		cdn: {
			src: "<%= files.cdn %>",
			dest: "<%= files.cdn %>.zip"
		},
		themes: {
			src: "<%= files.themes %>",
			dest: "<%= files.themes %>.zip"
		}
	},
	md5: {
		dist: {
			src: "dist/<%= files.dist %>",
			dest: "dist/<%= files.dist %>/MANIFEST"
		},
		cdn: {
			src: "dist/<%= files.cdn %>",
			dest: "dist/<%= files.cdn %>/MANIFEST"
		},
		themes: {
			src: "dist/<%= files.themes %>",
			dest: "dist/<%= files.themes %>/MANIFEST"
		}
	},
	qunit: {
		files: expandFiles( "tests/unit/**/*.html" ).filter(function( file ) {
			// disabling everything that doesn't (quite) work with PhantomJS for now
			// TODO except for all|index|test, try to include more as we go
			return !( /(all|index|test|dialog|dialog_deprecated|tabs|tooltip)\.html$/ ).test( file );
		})
	},
	jshint: {
		ui: {
			options: {
				jshintrc: "ui/.jshintrc"
			},
			files: {
				src: "ui/*.js"
			}
		},
		grunt: {
			options: {
				jshintrc: ".jshintrc"
			},
			files: {
				src: [ "Gruntfile.js", "build/**/*.js" ]
			}
		},
		tests: {
			options: {
				jshintrc: "tests/.jshintrc"
			},
			files: {
				src: "tests/unit/**/*.js"
			}
		}
	},
	csslint: {
		// TODO figure out what to check for, then fix and enable
		base_theme: {
			src: expandFiles( "themes/base/*.css" ).filter(function( file ) {
				// TODO remove items from this list once rewritten
				return !( /(button|datepicker|core|dialog|theme)\.css$/ ).test( file );
			}),
			// TODO consider reenabling some of these rules
			options: {
				"adjoining-classes": false,
				"import": false,
				"outline-none": false,
				// especially this one
				"overqualified-elements": false,
				"compatible-vendor-prefixes": false
			}
		}
	}
});

grunt.registerTask( "default", [ "jshint", "csslint", "htmllint", "qunit" ] );
grunt.registerTask( "sizer", [ "concat:ui", "uglify:main", "compare_size:all" ] );
grunt.registerTask( "sizer_all", [ "concat:ui", "uglify", "compare_size" ] );
grunt.registerTask( "build", [ "concat", "uglify", "cssmin", "copy:dist_units_images" ] );
grunt.registerTask( "release", "clean build copy:dist copy:dist_min copy:dist_min_images copy:dist_css_min md5:dist zip:dist".split( " " ) );
grunt.registerTask( "release_themes", "release generate_themes copy:themes md5:themes zip:themes".split( " " ) );
grunt.registerTask( "release_cdn", "release_themes copy:cdn copy:cdn_min copy:cdn_i18n copy:cdn_i18n_min copy:cdn_themes md5:cdn zip:cdn".split( " " ) );

};
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/MANIFEST000066400000000000000000001415701231750357400211550ustar00rootroot00000000000000AUTHORS.txt d05bff4f1a0f3ef694e600b22bf998bf
Gruntfile.js f1808b0730acb0538d1b98c566a50b4a
MIT-LICENSE.txt 54ab1578b1fe12a7dcae71b4c14ee4a8
README.md badcd66248097732420a1f805ccc4691
demos/accordion/collapsible.html 89befdac36f9f19f5469b0ce944008c1
demos/accordion/custom-icons.html 3c98f1da58e313963e4d5ce996b7b31d
demos/accordion/default.html c7c68a65ee1355e073aef5bc3561e943
demos/accordion/fillspace.html dd71fbefb0e4c685613afb5c3d4c9100
demos/accordion/hoverintent.html 2be7277bb6462d326d21993191b010e0
demos/accordion/index.html 7ba8142840cf4ac7d848309cd3853ad1
demos/accordion/no-auto-height.html a530470787c8d1d0dafaa38723d76631
demos/accordion/sortable.html 90bdb3e59d2e334b832d1e0bfc797df1
demos/addClass/default.html 31b90cd812146e4ad1181661d13cc592
demos/addClass/index.html 1ecebffae58a5a73527c4376635669f2
demos/animate/default.html 1e2d9652ef6bdb3cb0f6d40f93229012
demos/animate/index.html 1ecebffae58a5a73527c4376635669f2
demos/autocomplete/categories.html ccb8e4cd35d5366b8c6ab65f6ed68d6c
demos/autocomplete/combobox.html 8365320a30d14d8d8d5a97be296f74fa
demos/autocomplete/custom-data.html 1feafe2307837291862d72a3a67b5818
demos/autocomplete/default.html 69ce86f2d629848ead673d483d39bea0
demos/autocomplete/folding.html e9addc7edfee65aef384e5d3bc1ebd65
demos/autocomplete/images/jquery_32x32.png 63b85354622b940ba1252cb946a1dfe1
demos/autocomplete/images/jqueryui_32x32.png b5907552b610ac23f514760d3e30db78
demos/autocomplete/images/sizzlejs_32x32.png 290e264d5641f5e22cf7eb4477e9d5c8
demos/autocomplete/images/transparent_1x1.png 5612a775b711def5af3b0ace26436983
demos/autocomplete/images/ui-anim_basic_16x16.gif 0ea023ac2a49a96c925c7751119aa343
demos/autocomplete/index.html 5a4790606b9ee69afc05abed0efd87aa
demos/autocomplete/london.xml 71b873703620337cee79d7bd7d637b23
demos/autocomplete/maxheight.html 59afdf20927ac2003de75d5208adaa41
demos/autocomplete/multiple-remote.html e3be5b379837cd38b6488948d237e69b
demos/autocomplete/multiple.html 7bb5fed44fad021d440d57e7a232147b
demos/autocomplete/remote-jsonp.html c2a52dd0d6359ff26990872b47741da0
demos/autocomplete/remote-with-cache.html 7547830179ccef0fda8b5944401d380e
demos/autocomplete/remote.html ba89ee9857c3f547fc4be27ecaacbaf1
demos/autocomplete/search.php e50d30ac4ff4d87fa0b76863ab2cd131
demos/autocomplete/xml.html 4d17a155ba3b9e420681619be6f35e84
demos/button/checkbox.html bccc88c3c83fe2477141a0f30472fcd0
demos/button/default.html b3535860453eb7f703403479132197a9
demos/button/icons.html 467323ddf30e59aa72ed197ea11d55f7
demos/button/index.html e1d150eeb2b171caed125974510bfe74
demos/button/radio.html 472470a0a8a5ef17f9a4b75a5987ab8c
demos/button/splitbutton.html 266b1263689345a4a6bc6a035608347f
demos/button/toolbar.html 501b414b21f627b9ae8f521e9081af54
demos/datepicker/alt-field.html 1c22e68ae68ed999456aeb8f6cce73b9
demos/datepicker/animation.html 2894d995e15db143a544e52c194234aa
demos/datepicker/buttonbar.html 85c9d98139d449c2e461b0bbfb0758ba
demos/datepicker/date-formats.html 91565f57e041dd2ac34d38a4763d74b4
demos/datepicker/date-range.html 068519f224f8e23832523c555619e35b
demos/datepicker/default.html a7194d0acdc6d19091ee30e2886daa3e
demos/datepicker/dropdown-month-year.html 6736ab64b39f8cf4c22fe4e7e477c97a
demos/datepicker/icon-trigger.html c2731335be819c5d3382e5da0dac236c
demos/datepicker/images/calendar.gif ecae0392680875218dde6eec7ba713ae
demos/datepicker/index.html 31141303fd50342ce57a275f51f1799b
demos/datepicker/inline.html 9b0ebc3ad329015fd82dd3e5b1a166f9
demos/datepicker/jquery.ui.datepicker-ar.js 942eaa8e2a371c9858a75e3f58ac27c0
demos/datepicker/jquery.ui.datepicker-fr.js 74ff0c1602ce756629885acbd13680b8
demos/datepicker/jquery.ui.datepicker-he.js a791d8f0ac08b0d2876bcd21e029d39a
demos/datepicker/jquery.ui.datepicker-zh-TW.js 443e52a6fae355caecbd60e63b5c72d3
demos/datepicker/localization.html 689e1554fc9baede24067bec25321f96
demos/datepicker/min-max.html f2220313321789e0c08902383f2ddcbc
demos/datepicker/multiple-calendars.html f05978aef245ea2375d0325c7f921579
demos/datepicker/other-months.html 8cdb9f0b290bc78e0149a2c978f35289
demos/datepicker/show-week.html 8727aebd5140067e4908a957939280a8
demos/demos.css c7fc917de57fba6b610ad6191fc1a28a
demos/dialog/animated.html eab0821066fa39b8124c6bfcd436ca2d
demos/dialog/default.html 6425b0222244c3ca125046582884ff4c
demos/dialog/index.html 204169799ab0d5b7ec5fd0bb7515c3b0
demos/dialog/modal-confirmation.html 72aca56bbb744f6169627f9f8a0ab5ab
demos/dialog/modal-form.html d2e20ace33771496253f8d995c3ad730
demos/dialog/modal-message.html 20735ccf826f5ef0372637c27f68e083
demos/dialog/modal.html ab42ad37046389236485db0606a8d61a
demos/draggable/constrain-movement.html ce7170e440c4588112f2ba29c3ebc664
demos/draggable/cursor-style.html f89f677fe5cda695fcabdd2d9ba7b920
demos/draggable/default.html 08ca2bf3e75e267a07615af4e1550687
demos/draggable/delay-start.html 7fe3dd7aaa618b2f2c470ee83ccadfd7
demos/draggable/events.html 6b0f92486cfc0f24cc0fc9c921f709d5
demos/draggable/handle.html 3ca2502a72bc697b945814631eeb2c1e
demos/draggable/index.html 76f8ebe28a28f6fe060ea5ca437fc6cb
demos/draggable/revert.html 4c6ebed5ef4eb98f5f4abccc82a39349
demos/draggable/scroll.html 462ed4bf66de7bfd862a61ff53b60f82
demos/draggable/snap-to.html 5d339165af453845093a1228cb0bff6a
demos/draggable/sortable.html 3c7db46e7618ddd436bb40c08f0ae13d
demos/draggable/visual-feedback.html a88512371b46d67ff25330c81a6545b4
demos/droppable/accepted-elements.html a3f00eaf2639c91f675805839f2fd096
demos/droppable/default.html 463a745ce50e2268e79c38bd1a4ab6af
demos/droppable/images/high_tatras.jpg 969449984d601e92df0e0521fe0adcda
demos/droppable/images/high_tatras2.jpg 05ab2eefd3ce77a097fe968ecfa79db6
demos/droppable/images/high_tatras2_min.jpg c81066bcfa835d52a4d9c0daa5a2448e
demos/droppable/images/high_tatras3.jpg 75829fca8936aeb8ac8934cfc6d96d47
demos/droppable/images/high_tatras3_min.jpg 2211533e3e3e7a0e5b9cd4a1b000da25
demos/droppable/images/high_tatras4.jpg 2f9736c9afa012359026ba5c4b382e30
demos/droppable/images/high_tatras4_min.jpg c2e09f1983e9ffb318c773892735a81f
demos/droppable/images/high_tatras_min.jpg 5ce7c7cf7fb648d44350ff612d07caa2
demos/droppable/index.html f236a2f25273c8e86d85eea26d2c2641
demos/droppable/photo-manager.html 1fa98003982c128f550aecbf35cd5f40
demos/droppable/propagation.html 52ed26c40b7f2eea49dc34ebdfde0ba2
demos/droppable/revert.html 162215ee5d0d237a42463935fe602598
demos/droppable/shopping-cart.html f35b89e3414590aaf2242c1016884d21
demos/droppable/visual-feedback.html 28854d434d67ffd10e9daf9b522a92a0
demos/effect/default.html 5bda8dd72829602136ad9411c01e7234
demos/effect/easing.html 095a64cb8aef470d13cf1a5f1910f71b
demos/effect/index.html 7670272a705926230cfcb3d53c37f69e
demos/hide/default.html b017919650d707818a4b198496505390
demos/hide/index.html 1ecebffae58a5a73527c4376635669f2
demos/images/calendar.gif ecae0392680875218dde6eec7ba713ae
demos/images/demo-config-on-tile.gif 53a1e40835829322d93d32d3772e7e37
demos/images/demo-config-on.gif bbbfaec5d74cbf00d14d7e37c6a729d8
demos/images/demo-spindown-closed.gif 3df09cf39f5ac14679a6b1e76e104059
demos/images/demo-spindown-open.gif 205c7b92a32e9a07dee036d4b07fb7ff
demos/images/icon-docs-info.gif bef5580a53a2ba7465a2a385022f670a
demos/images/pbar-ani.gif 6958801266c8f025fe28a928ba6cd992
demos/index.html ea28419d5769b2115f79bfd4243bc83e
demos/menu/default.html 266465186a79faa78b740ce89df724dc
demos/menu/icons.html 83e914c105c58ef7f8e498bdfd293143
demos/menu/index.html aba072191ea6e4f6a64da313995a6e94
demos/position/cycler.html 51da38ded2b4f547aa859319a9281d55
demos/position/default.html 99e29131d68b02a47cc80791922b7817
demos/position/images/earth.jpg 3b925e0b109f9d4d383c76eccf495b53
demos/position/images/flight.jpg fdcad09a5015ecdfe0355a8cfb345dd5
demos/position/images/rocket.jpg 775634773fa6564afd8257e8180c43e0
demos/position/index.html e76ea712f8894fd122cbd53c7a97fa8f
demos/progressbar/default.html bfd217f6a2142f13294768bf72a8a9e1
demos/progressbar/images/pbar-ani.gif 6958801266c8f025fe28a928ba6cd992
demos/progressbar/indeterminate.html 55bb8a985d5bbf1678cb6c27475b8c9a
demos/progressbar/index.html 4b63914f473c93a3e11ae8235eeb6ba1
demos/progressbar/label.html 0f628feae545d9a4c74c891ba453fd6e
demos/removeClass/default.html a3edf2cfd74306f32805065ba240ffd3
demos/removeClass/index.html 1ecebffae58a5a73527c4376635669f2
demos/resizable/animate.html c1275b8ae000bf0c06c08681518efd0f
demos/resizable/aspect-ratio.html 432acb92248e050ab2b1104317ae0a4c
demos/resizable/constrain-area.html 65515b619b4fb052af5af36ffca9d9d5
demos/resizable/default.html 267c4ebbae35aff76761f12d5edadddd
demos/resizable/delay-start.html 039cc6f5996e4d208a18d4b16e6e0098
demos/resizable/helper.html cbb2603c5e2f648af505628539348a62
demos/resizable/index.html 4e91089b11d4dc09aa0e70ceb5320a4b
demos/resizable/max-min.html 8895df64a3ab0ad39aeeb1709433b300
demos/resizable/snap-to-grid.html 04d03732aee9623092a949559a5c973c
demos/resizable/synchronous-resize.html dbf018c7723a41f2f68e98236cfc41f6
demos/resizable/textarea.html 3e80aec847ddfa6cd3df1eed3ee10414
demos/resizable/visual-feedback.html 00386bfa01c129f5ced8494fa899d62d
demos/selectable/default.html 3563fd88dc357028d9b52d9afc6f13e9
demos/selectable/display-grid.html 3c2125fa2422703944b03630477f5f79
demos/selectable/index.html daf7df35e4ed02fb093e92a1a95e63af
demos/selectable/serialize.html a612ec855c6aa78971350cd3f1d54332
demos/show/default.html 36f7b7cbf8eacdbb48d87e743973ab67
demos/show/index.html 1ecebffae58a5a73527c4376635669f2
demos/slider/colorpicker.html 6f773ae54a78c66a6bcafbdf55c38a4b
demos/slider/default.html 4c84f13083903df5f46d971ebedd15d2
demos/slider/hotelrooms.html ff1de2c2fc3a35cddc1a574364e70066
demos/slider/index.html 91c70e693a9748b67102ae54e3251773
demos/slider/multiple-vertical.html 6420876e84d50835d49989f72722333a
demos/slider/range-vertical.html 02bab970c613a24d3a5b4cb515a201cc
demos/slider/range.html fa6d770b6da18ebfa6ddbb502c0a28d7
demos/slider/rangemax.html 4cf4ad940665e7f39ebce972c2ec9857
demos/slider/rangemin.html eebd6337af71bcf3f2b01b8504bf2c31
demos/slider/side-scroll.html 21020e573185b5290711e22d40c2919e
demos/slider/slider-vertical.html 8fd7dc3eb8203379c85d544250dba8c7
demos/slider/steps.html 0af949dcc37a79dbf7af5f5be6fe1e61
demos/sortable/connect-lists-through-tabs.html d0613fe3b0b0f4787b09aa2b7114a93f
demos/sortable/connect-lists.html 44f1cca02907df5a60a2120e46f3726d
demos/sortable/default.html a86a60a93af2f7398934db050f56803a
demos/sortable/delay-start.html 2a21b71003cf8141ecf3e0bbf6903048
demos/sortable/display-grid.html 6762d264b8328285795cb191650a5e44
demos/sortable/empty-lists.html 2e82590e8069fd779fc34793bb3757b6
demos/sortable/index.html a68a64aecfd8ade1134982578d8a4855
demos/sortable/items.html 275aaac61433b254f39731b5042e172e
demos/sortable/placeholder.html e5d1cd62ff3ebb5bbea4faf415c51027
demos/sortable/portlets.html c3383c81ee00229485fa3dc044ea23c6
demos/spinner/currency.html e19fd3519f8aea2d7fbaf5b0078c3406
demos/spinner/decimal.html 7f9b0d39b8cabe77ca7ae6f84bbd4ab2
demos/spinner/default.html 9be0d72544e96907850010aad8cd9750
demos/spinner/index.html 44a4e022747a656aa42e615847452235
demos/spinner/latlong.html bf9739604cd7781e85a9e2a2be2f26ce
demos/spinner/overflow.html 38feadd133ac7d8f82c04fe572e1533c
demos/spinner/time.html 491b0a111bece7a834e0bce9f42ccac6
demos/switchClass/default.html 01282041834807f81440b5970c3ec37a
demos/switchClass/index.html 1ecebffae58a5a73527c4376635669f2
demos/tabs/ajax.html bcbded18824ecccf40563a02df2f7a95
demos/tabs/ajax/content1.html a057a52c505c537a9db040a84d25e640
demos/tabs/ajax/content2.html 1289bb96e74c38ed0f6c68507a41aec6
demos/tabs/ajax/content3-slow.php 1184ec5ad3b1bf81eb5f19cb6a233daa
demos/tabs/ajax/content4-broken.php feb9562749e8ae1d0408c257ca44b6e5
demos/tabs/bottom.html 237ca7c0700e402792352edc86703c27
demos/tabs/collapsible.html b5374183ecef2dbe0c90a68dfb133312
demos/tabs/default.html bd533ed7c9bd72c5282f6829fd3df218
demos/tabs/index.html 0c5de49104cbcaa52b4541c4e3ec1069
demos/tabs/manipulation.html a0abdde9bca185505cad2bd1607c1141
demos/tabs/mouseover.html cb7b7756603f43fa943b428c48b50ec9
demos/tabs/sortable.html 31a3b947d0d9f0c5003d41c12220be1e
demos/tabs/vertical.html a31b69f6cc78661c9c15f4a19a515385
demos/toggle/default.html 86b2e440bfebb230448a1eb0ec2fdc97
demos/toggle/index.html 1ecebffae58a5a73527c4376635669f2
demos/toggleClass/default.html df414a009c479ce2f12f9c0dfb668ac7
demos/toggleClass/index.html 1ecebffae58a5a73527c4376635669f2
demos/tooltip/ajax/content1.html 12a3151f0923a1b6289e8ab1a348eee2
demos/tooltip/ajax/content2.html afd4c4907b88dfa728d7d0773f348d31
demos/tooltip/custom-animation.html 015f241a590da9f9330d7dab194cd720
demos/tooltip/custom-content.html 640a20172a870a7e0daf7bdf0de6291e
demos/tooltip/custom-style.html 6746ba919b5f2bbfb64a19b1ee3172a8
demos/tooltip/default.html b36baf45c65ba84b1026c3423465b765
demos/tooltip/forms.html 275e70a5a647d393cd05c0eea1e3fd67
demos/tooltip/images/st-stephens.jpg 753aec4fa1c8c80bec3732ebf454a632
demos/tooltip/images/tower-bridge.jpg 66d36bf9a4ea5baf6a717e5b0ca06c40
demos/tooltip/index.html cdf3b5b7616d24ef087adcc7f05700da
demos/tooltip/tracking.html e4bf9836c8a695bf9cf89f628020cb33
demos/tooltip/video-player.html 2f2e3c9370e389202f477f768db2e5a7
demos/widget/default.html aa8edb8878d8ce1fa408c585d8d8b4f2
demos/widget/index.html dbd594416083998b2ba3462b64b96aae
external/globalize.culture.de-DE.js 7ca47099bde44c5c647b9ee859d38418
external/globalize.culture.ja-JP.js 0f4692b553b5ae2d9d5c6698e91664cc
external/globalize.js 8aa0b2b9858380f3f8f1d5c648fc02c2
external/jquery.mousewheel.js 99457f3dc7082452b9fdc702b3dfcbaf
external/jshint.js e25340f0196f65135f50877c0fe6efe2
external/qunit.css 37687050b4af752017145c24bfd9c2f4
external/qunit.js c88d05352e715c460e26f5db688c1d13
jquery-1.9.1.js 08c235d357750c657ac1db7d1cf656a9
package.json 946c3ca54f30e0af8295b875ea3e7661
tests/index.css 1975db2466c0425aa5bde34b58490513
tests/index.html d117582e9393b1bdf056d78c536d2d96
tests/index.js 5cb5a4eb5eefc483727fa6383b83e3b6
tests/jquery-1.6.1.js 4b65ea7175ec8007f79bf245cf909e05
tests/jquery-1.6.2.js fc1ebe9026579ec6d37993f1eaceb54a
tests/jquery-1.6.3.js 5761f94379fe831aba5129cbb4b8a257
tests/jquery-1.6.4.js e3cfaf7a2c029866cb5fc35c883907fb
tests/jquery-1.6.js 4d9bd9c1be819924dfec925a678077d2
tests/jquery-1.7.1.js 6f477fa74c5027c9ec6945f9235bdf35
tests/jquery-1.7.2.js 85d6bc8bb1725066ad866e17086dcdd3
tests/jquery-1.7.js ba1e8d241eb0be1a07a031377aebdcef
tests/jquery-1.8.0.js 15ea967efbc17ae4f37d9db743e712b5
tests/jquery-1.8.1.js 3fb547ed8dc2b18ab3ea88e4b8868f19
tests/jquery-1.8.2.js ef68beaa375acd83206bbc0cf92970e9
tests/jquery-1.8.3.js 2073df88a429ccbe5dca5e2c40e742b4
tests/jquery-1.9.0.js f3346149a7173e70d39e6f36bfb178a4
tests/jquery-1.9.1.js 08c235d357750c657ac1db7d1cf656a9
tests/jquery.js fb6f96f1c5db020866e96f1350c6fd64
tests/jquery.simulate.js 77a796da2b4544b1afc97a196e4ebcd4
tests/unit/accordion/accordion.html 56973698b4d67cd4f87976b99a995360
tests/unit/accordion/accordion_common.js 432f694f4c2ad29d0079753f42cbdb9b
tests/unit/accordion/accordion_core.js 72a2ec9b52cbc7143e61122ef2d9fcd0
tests/unit/accordion/accordion_events.js 51a5304f2fc4a2a246622bc4a0f51629
tests/unit/accordion/accordion_methods.js 6fb821c9d78d6357777fa0dd4cfdc667
tests/unit/accordion/accordion_options.js 021f0d27d1f9df9c857588cad812af15
tests/unit/accordion/accordion_test_helpers.js 54546f504f3af43033fd824135e28e90
tests/unit/accordion/all.html 8775074561676645f96f794cd4de4495
tests/unit/all.html 1f789501d47da74b6eb71c77b9be5fad
tests/unit/autocomplete/all.html 2d6302968bb965488fc9fedd83de0a3a
tests/unit/autocomplete/autocomplete.html 6b5a374713489139d283a1adc687ca76
tests/unit/autocomplete/autocomplete_common.js 41eb983cb553bac7f9304a516dda2344
tests/unit/autocomplete/autocomplete_core.js 2b432c45118931329fd71d66d4032b76
tests/unit/autocomplete/autocomplete_events.js 95eb26e267489afe3ab75a295cb392e2
tests/unit/autocomplete/autocomplete_methods.js ebae0a966b238bd89e4673307a2f77e3
tests/unit/autocomplete/autocomplete_options.js c61a3751033bf7245f31ebc067b1bb34
tests/unit/autocomplete/remote_object_array_labels.txt 47e4bc9f1926d7b98eb7e11991ab0961
tests/unit/autocomplete/remote_object_array_values.txt 5db4d5ca44a403a6d15c38076208c019
tests/unit/autocomplete/remote_string_array.txt 5a68e737a3ded6571ad5c79cad5c853f
tests/unit/button/all.html d765f4d9df831c4e9554fd8d52ec252f
tests/unit/button/button.html 32541d1ef4779bd336c6c23900e37370
tests/unit/button/button_common.js 57e70e1e0a4f6e9c62ba01622745d652
tests/unit/button/button_core.js 2369b177da7d1c79e9b3b66362bc6f93
tests/unit/button/button_events.js 34072c28effa6e5627222d5760b05526
tests/unit/button/button_methods.js b23a8882fb015beaa79601ba8916cacc
tests/unit/button/button_options.js 66991dd555d3f2c7989b718affed8cad
tests/unit/core/all.html 651c73f54a9fc5e7c6c1bfb26d6dc6c8
tests/unit/core/core.html 5773ad09c485b2a85aace0d3841710df
tests/unit/core/core.js 0eb343f0a17182d0d018d9cc110708b2
tests/unit/core/selector.js b039256d06c4209f1ba2a1d0b92e6795
tests/unit/datepicker/all.html 8c61e085b9a34fa119a4d1fe84b23947
tests/unit/datepicker/datepicker.html fc505c5cbebfb540b7460f70226368ba
tests/unit/datepicker/datepicker_common.js da2e216e7486a17640c152ccae9bce4e
tests/unit/datepicker/datepicker_core.js 7d9c456bf8194593c72863ba7d0af1b7
tests/unit/datepicker/datepicker_events.js 15fcde79f8dd3070476e6b0c703b0de2
tests/unit/datepicker/datepicker_methods.js 9339ed34c35a517c42b0422b35173098
tests/unit/datepicker/datepicker_options.js 3540c6a15c5ff5729cf7c40d45b2e082
tests/unit/datepicker/datepicker_test_helpers.js 97818ad5344858c7c0c59e5467ca5454
tests/unit/datepicker/images/calendar.gif ecae0392680875218dde6eec7ba713ae
tests/unit/dialog/all.html 644f225f593be502d894b33367356c64
tests/unit/dialog/dialog.html e30868d13c202212308b6dbb3a83d7da
tests/unit/dialog/dialog_common.js 8095d571d9ce39c1ba9b9ca3196565b7
tests/unit/dialog/dialog_core.js d83106c08d90eb74e73479f20b08a5be
tests/unit/dialog/dialog_deprecated.html a5132ea51e4d36c53248dba5135440fa
tests/unit/dialog/dialog_deprecated.js 92024860fdbf8f559aaec50f33679f8a
tests/unit/dialog/dialog_events.js a6ea96edb013d921cfc017dd3b466984
tests/unit/dialog/dialog_methods.js 34d85b6c909e6159eb72558617be575f
tests/unit/dialog/dialog_options.js 48537004ea6e461e6a2a4f698d983411
tests/unit/dialog/dialog_test_helpers.js 6bde838d6ed5e88e1c27e8de15b9fa4a
tests/unit/draggable/all.html 8a4736c7aecbe3981d1198f83f3ff7ed
tests/unit/draggable/draggable.html bc70338c67f0a7ebd53650ab419e112a
tests/unit/draggable/draggable_common.js def6c57bad222afec2d1168e1d9c04d7
tests/unit/draggable/draggable_core.js e0fb59987b2c40e13d3da5e309b8b249
tests/unit/draggable/draggable_events.js eb0d6dabeff78e72f20d32b14c23d996
tests/unit/draggable/draggable_methods.js 3821ed8f83fcc13f08b66dee2bd245ef
tests/unit/draggable/draggable_options.js 1727203cbce106fbc2cc28331fb82e04
tests/unit/draggable/draggable_test_helpers.js 0405f43250a5a8541e27004ea0dfb758
tests/unit/droppable/all.html de845ad3e7505d387b3173c4365c6da6
tests/unit/droppable/droppable.html d3bbc631b02e58516663272a366a191a
tests/unit/droppable/droppable_common.js 91882be881a84fea7b1e0ec45a8b2ca2
tests/unit/droppable/droppable_core.js de2a35afb3e459985b0d0dedb9b093ad
tests/unit/droppable/droppable_events.js eb29bb8950a648460586765019ca84c4
tests/unit/droppable/droppable_methods.js 0a6ee3b24d729eeb20964a83724af439
tests/unit/droppable/droppable_options.js e1271ee5a56bba24fa98cf52babc7dee
tests/unit/droppable/droppable_test_helpers.js b1ba5248d747e336434f3876da111981
tests/unit/effects/all.html 24212028e29ab86a63d5393a315d7c95
tests/unit/effects/effects.html e71d6f046ecfd20dc0342522656df4c7
tests/unit/effects/effects_core.js e3cda7655c9aa7eb3d5a514b8de6654c
tests/unit/effects/effects_scale.js 81271ea4c56fb82f9336fe87058404f2
tests/unit/images/jqueryui_32x32.png b5907552b610ac23f514760d3e30db78
tests/unit/index.html f3af22ddb0a418ce7c89a9d724cfca62
tests/unit/menu/all.html b612e6c9eba8abc1d6a77fb16e968c60
tests/unit/menu/menu.html 46fdf57c4b8ccceea2351a696e53585c
tests/unit/menu/menu_common.js 14ff1786dc6fd7b4bb36165748996fad
tests/unit/menu/menu_core.js a9bab987cbef99d52781d1189cfbc742
tests/unit/menu/menu_events.js d8b04f2daac5d88165807ff3cdde4906
tests/unit/menu/menu_methods.js fc26fe8740ff519d9695ef6992b26992
tests/unit/menu/menu_options.js 6239868707dd5e3aa3fbb5082581e07a
tests/unit/menu/menu_test_helpers.js 137f4cf6c1b2879f3e94066800de2669
tests/unit/position/all.html f173a685f94bce9a0f2afd004f8ace79
tests/unit/position/position.html df8fe227ff37d50f1f965b32f4b870fb
tests/unit/position/position_core.js f70b6c7bd33b163e23a67d6b268e33dc
tests/unit/progressbar/all.html 83627198459812ca449a9ee430ed3778
tests/unit/progressbar/progressbar.html fee63970c42b3ad1f5cb032ae2255020
tests/unit/progressbar/progressbar_common.js a23adf8bd501eb49aeeffbd24160e036
tests/unit/progressbar/progressbar_core.js 56e676fb918d441003f81702a5469c94
tests/unit/progressbar/progressbar_events.js 1fc602b409157c3cc5786c3c19843702
tests/unit/progressbar/progressbar_methods.js 41605c770a137a33535095110edc1d46
tests/unit/progressbar/progressbar_options.js f8edf4492fadef665d99b5b4f220ce6a
tests/unit/qunit-composite.css bf9a7cb34b4e8c0379e565e6d3ec487f
tests/unit/qunit-composite.js 2bafe0b898f476e0b51b119ea54939b2
tests/unit/resizable/all.html bf8539e10a1a4bcd62a2b408568fe9ae
tests/unit/resizable/images/test.jpg b8c459677bc8915cda93bf9efe86d72c
tests/unit/resizable/resizable.html a684be8dfcb79bf2230c44f27e96a355
tests/unit/resizable/resizable_common.js a5f05d5392c3ba34a4e8d5bc2b8c927e
tests/unit/resizable/resizable_core.js 4562c4a5cf4d0eb01734c56e365e34a4
tests/unit/resizable/resizable_events.js 243f20a2a751210cbb7f393df109d9eb
tests/unit/resizable/resizable_methods.js f71dbbbb8c72ea6ac6a3a41df2e64c30
tests/unit/resizable/resizable_options.js 4f82090dac67560e1f3de5d21b070d97
tests/unit/resizable/resizable_test_helpers.js a200da89ae7a74e1aca3e89b2975e529
tests/unit/selectable/all.html ead180397c3603a080121de587e6485c
tests/unit/selectable/selectable.html 30cda03229830e5fcd12a6092eadf916
tests/unit/selectable/selectable_common.js bcf3baf9883e6a9f9d70b4af19cbcfaa
tests/unit/selectable/selectable_core.js ae8129f4b08d9fc637223e6c1fe43125
tests/unit/selectable/selectable_events.js ee9bbcc2e5657887fc30511246fdfbd3
tests/unit/selectable/selectable_methods.js 96d128be3a52d04e60f6e1e82a319a75
tests/unit/selectable/selectable_options.js 60452417be0ea4bf73e9a350b440cb43
tests/unit/slider/all.html 26e883e27367e89ba8eec8945260b0e1
tests/unit/slider/slider.html 8aa77586769cea5bf0c9fa3c9ad140b4
tests/unit/slider/slider_common.js 8643772a233ae367e11dd9af0da6bb9e
tests/unit/slider/slider_core.js c9adabb41ef386717dd171fc3ae99c97
tests/unit/slider/slider_events.js 8357ab4f77be8e93335d56dd54cfbc69
tests/unit/slider/slider_methods.js 779bf0dc22701185e287d675e046ac30
tests/unit/slider/slider_options.js e721c187411b15b8dec429f8c2df86f5
tests/unit/sortable/all.html 2db13ed2fba3e1978c4eae2c2c8d830d
tests/unit/sortable/sortable.html c85d41aa72874bb3eba5e40fa8755a37
tests/unit/sortable/sortable_common.js 07629ea5d09768cd576c8a6bc3052ee5
tests/unit/sortable/sortable_core.js 13094091aa0e6e1cb69fed5aae89ed73
tests/unit/sortable/sortable_events.js 23fb859505697fbcbaa6256517980b3b
tests/unit/sortable/sortable_methods.js fb44be61d747a5de33b7d722d4635625
tests/unit/sortable/sortable_options.js 4814226bb5e61adb5c4429980d589daf
tests/unit/sortable/sortable_test_helpers.js b0dda6cfb320d1f37fd5b0e46ea306c0
tests/unit/spinner/all.html 9a5e18b7f80a598fc93c2bab7cd602a6
tests/unit/spinner/spinner.html 0c281720fc0bc501527a8f2bb060e5e3
tests/unit/spinner/spinner_common.js e0084a518f2e54c222d14247cbe6436b
tests/unit/spinner/spinner_core.js 244452f97d0c6db8386a1b731f6d8784
tests/unit/spinner/spinner_events.js 975dde87c8ecf8548630ce3f2d6d2300
tests/unit/spinner/spinner_methods.js 3753c26a2f6bf1dfcdec1774e1ce00d6
tests/unit/spinner/spinner_options.js 03f527bba5cfa16ca49bc102eaae3e23
tests/unit/spinner/spinner_test_helpers.js 2f9d7a1a54bbebaed1b3982fcbc77878
tests/unit/subsuite.js 557df000a529ea3e8f921cb2da10a0b3
tests/unit/swarminject.js edd2bc76548cce6675263e6cd43145e8
tests/unit/tabs/all.html e2127e6673f9f17d3da585bc7aaa60a4
tests/unit/tabs/data/test.html 94382cbd6df9df6b2d8dbebb59740dd3
tests/unit/tabs/tabs.html 54ef396a25ff7695bb6904f83674e19a
tests/unit/tabs/tabs_common.js 907cb2cd2b9a4acad75dbbf7f7a3d865
tests/unit/tabs/tabs_core.js 19f32b66c465dabd264869287d4935da
tests/unit/tabs/tabs_events.js 67802c3855ad9dced2ac32bf7d663d29
tests/unit/tabs/tabs_methods.js 4c0e4a27edad8ae65b5d6f302e9cdf0a
tests/unit/tabs/tabs_options.js c47cb447d292a312c886f17e77a5cc07
tests/unit/tabs/tabs_test_helpers.js 3fa3b964947f3bfb767a0d5809b52100
tests/unit/testsuite.js 1431871eddc51984c3b3e56c3d558698
tests/unit/tooltip/all.html 9b17b49a2c09c30247ad6de8e783beeb
tests/unit/tooltip/tooltip.html b8c2f7eaf5a19bde14bf0ff5c032c6ad
tests/unit/tooltip/tooltip_common.js 905ecb3445955642ff8ff8bd6f9b84c9
tests/unit/tooltip/tooltip_core.js 8999ce3f66047c39a436e8d2c7776ea8
tests/unit/tooltip/tooltip_events.js 7f6992ca8273f9145b9d94b60461c592
tests/unit/tooltip/tooltip_methods.js aed9d6fc6acbe0aeb9ecf10c0729a6de
tests/unit/tooltip/tooltip_options.js caaba4e2825877ce574038ed20d710c5
tests/unit/widget/all.html e9f5b985bcc01e70291b9ad630067737
tests/unit/widget/widget.html b56a5deb1de2d1cdced819870149a7d4
tests/unit/widget/widget_animation.js 38a8438ccb896155f00739bb0a5cc24d
tests/unit/widget/widget_core.js ae981cb65d7970509cdc1735d68cda0e
tests/unit/widget/widget_extend.js b0ca0e5aa6c74b6d0332808f8fb390c2
tests/visual/accordion/icons.html 8cd10ced65e5bdfa521398e4cc70f8a8
tests/visual/addClass/queue.html 2869e0d2a88c2bfd53aac525fa3b0e3c
tests/visual/button/button.html 7ccd19161de9bbd08559ffea5ead7b34
tests/visual/button/performance.html f4985d5ca903a84ffdcc09824c0452cc
tests/visual/compound/accordion_tabs.html 3374f17d716322f00fc022d42746fb6d
tests/visual/compound/datepicker_dialog.html 2690053ee95929462eb359724fe10fc9
tests/visual/compound/dialog_widgets.html 60afb8d0513473f76838330e1b7bf95f
tests/visual/compound/draggable_accordion.html 98b522b10bd19b051d939a426b0fd30f
tests/visual/compound/draggable_accordion_accordion_tabs_draggable.html 7df45cb369c47b18953c00fa1ef8ec64
tests/visual/compound/sortable_accordion_sortable_tabs.html 22dcb429dad7efa991978f9889ca2a7b
tests/visual/compound/tabs_tabs.html 5067e5e7f13b2fa4f1b6d8438a13cf47
tests/visual/compound/tabs_tooltips.html a3df03e16cd1fbbf6b8fbc2dfec8482a
tests/visual/dialog/animated.html a16fb382675223032d89dbdf83fccca8
tests/visual/dialog/complex-dialogs.html c1891232cf635796413aadbbe8b05acd
tests/visual/dialog/form.html ca53bfb938d252aea432f4317a07c42b
tests/visual/dialog/performance.html e1843ef032881e7cd7354e02b6deea3a
tests/visual/effects/all.html e9f958801563b33aca311dca99393b7f
tests/visual/effects/effects.css e9f39ec2b841172f489d672ce57e6c3f
tests/visual/effects/effects.js cf826d29cefa363e5974fad875637927
tests/visual/effects/scale.html 687244e30f4ede62a520017043506f8e
tests/visual/index.html 702256273945179cc9ad8d196382e940
tests/visual/menu/menu.html 3292872484b7a6fb53dd7b2717c12d65
tests/visual/position/position.html ee3486b9f13517c448a0c12082301432
tests/visual/position/position_feedback.html 90a740c34db3579b98b9ec9dda1de7bc
tests/visual/theme.html 58badf1abfa77505303d98fe1b6ec1c1
tests/visual/tooltip/animations.html 0714bf60494d825d65f8f9e1056eb330
tests/visual/tooltip/tooltip.html a7fc5b1bf9df9dafb50478f4533f6cf2
tests/visual/visual.css da740540ef7976839bc9611338de0181
themes/base/images/animated-overlay.gif 7b1d52ef7bdaa05f5948dc3a1f75d1e3
themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png 4b5c132cf83d2d3e48e5a559739709ee
themes/base/images/ui-bg_flat_75_ffffff_40x100.png 1bbf3e17a3d20b23c665b78994c93f0a
themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png 472c9a50dc526afcfeaf739ff67816cd
themes/base/images/ui-bg_glass_65_ffffff_1x400.png 6fed187efb9afef2679ff9a46dd6bef7
themes/base/images/ui-bg_glass_75_dadada_1x400.png 49c75ede4c8c0a97752cc2bd34c36e3f
themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png a4b7af89c12cc2b0c24a6b06c6ee4c46
themes/base/images/ui-bg_glass_95_fef1ec_1x400.png 0fdd53e72a8bded481b24b70797b8373
themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png e2a1926fe6fcb1f025c300fb36b76a6f
themes/base/images/ui-icons_222222_256x240.png cac9343b455ba1077614c1bb583cbe18
themes/base/images/ui-icons_2e83ff_256x240.png 559b15504e19e757f34ac93c35893408
themes/base/images/ui-icons_454545_256x240.png 619efe8651a5e9c49aed92368fa9dff3
themes/base/images/ui-icons_888888_256x240.png 993a8340d509467ebd9dfcb49ed5daa0
themes/base/images/ui-icons_cd0a0a_256x240.png e76f396c14ecad218b72a59abe670a9c
themes/base/jquery-ui.css b962e1237bb42ad22fa843ed4982afeb
themes/base/jquery.ui.accordion.css 9ddd930550251b2cb69c7fcb610374e7
themes/base/jquery.ui.all.css 9922a4e75a530fbeee865949c19d3b84
themes/base/jquery.ui.autocomplete.css 0cceb6c55b3e4a2a66fd836c490967d1
themes/base/jquery.ui.base.css 11c6e1f18c8e4627a5ad88111d3abdf8
themes/base/jquery.ui.button.css 649d0f6f191c0f656200e7ced02b7cf6
themes/base/jquery.ui.core.css dab215fe7260e976363e6703d83e0057
themes/base/jquery.ui.datepicker.css 8054479530368059b34e12a158dc82af
themes/base/jquery.ui.dialog.css 6ed9cc6617bc5c689690df31867a30d3
themes/base/jquery.ui.menu.css dd08e549c31229b4547a46da9c16a923
themes/base/jquery.ui.progressbar.css 1c8305df46572ac77fff235ebb5b4306
themes/base/jquery.ui.resizable.css d36cc4aaab721213cb6199ee478ba814
themes/base/jquery.ui.selectable.css 322e6bf36321f152df15cb3b27b69cf2
themes/base/jquery.ui.slider.css 9f6ab2debff83808eb9c8ed1680bf6b8
themes/base/jquery.ui.spinner.css ee270ce5320167691429766643d70fd9
themes/base/jquery.ui.tabs.css 0c381a792b04e0228679100b9b0d97d6
themes/base/jquery.ui.theme.css 9c8aafee1198a70c021d8a04267f3c7c
themes/base/jquery.ui.tooltip.css f32c5bdd1477d5e7d7b3a28eb39762ff
themes/base/minified/images/animated-overlay.gif 7b1d52ef7bdaa05f5948dc3a1f75d1e3
themes/base/minified/images/ui-bg_flat_0_aaaaaa_40x100.png 4b5c132cf83d2d3e48e5a559739709ee
themes/base/minified/images/ui-bg_flat_75_ffffff_40x100.png 1bbf3e17a3d20b23c665b78994c93f0a
themes/base/minified/images/ui-bg_glass_55_fbf9ee_1x400.png 472c9a50dc526afcfeaf739ff67816cd
themes/base/minified/images/ui-bg_glass_65_ffffff_1x400.png 6fed187efb9afef2679ff9a46dd6bef7
themes/base/minified/images/ui-bg_glass_75_dadada_1x400.png 49c75ede4c8c0a97752cc2bd34c36e3f
themes/base/minified/images/ui-bg_glass_75_e6e6e6_1x400.png a4b7af89c12cc2b0c24a6b06c6ee4c46
themes/base/minified/images/ui-bg_glass_95_fef1ec_1x400.png 0fdd53e72a8bded481b24b70797b8373
themes/base/minified/images/ui-bg_highlight-soft_75_cccccc_1x100.png e2a1926fe6fcb1f025c300fb36b76a6f
themes/base/minified/images/ui-icons_222222_256x240.png cac9343b455ba1077614c1bb583cbe18
themes/base/minified/images/ui-icons_2e83ff_256x240.png 559b15504e19e757f34ac93c35893408
themes/base/minified/images/ui-icons_454545_256x240.png 619efe8651a5e9c49aed92368fa9dff3
themes/base/minified/images/ui-icons_888888_256x240.png 993a8340d509467ebd9dfcb49ed5daa0
themes/base/minified/images/ui-icons_cd0a0a_256x240.png e76f396c14ecad218b72a59abe670a9c
themes/base/minified/jquery-ui.min.css 9d95ec66df15e43bfecdd1d5864efb55
themes/base/minified/jquery.ui.accordion.min.css 6178d449173a0f16318f2d443fe5a0b1
themes/base/minified/jquery.ui.autocomplete.min.css 047e6bd7bc0b9b4abae627428565b85e
themes/base/minified/jquery.ui.button.min.css 398d66013cf5d4b64139a550f5c5e9c5
themes/base/minified/jquery.ui.core.min.css f0cc1c2b01986602a6cf48df944119d5
themes/base/minified/jquery.ui.datepicker.min.css 0867ad8f5f89d0687245164d288d55bf
themes/base/minified/jquery.ui.dialog.min.css 3aeb5b6775e6943273845a3583a5c1b1
themes/base/minified/jquery.ui.menu.min.css bfbe3bd1d0a8103eda170299986b7e91
themes/base/minified/jquery.ui.progressbar.min.css 22755a727c954051376171bedd8647df
themes/base/minified/jquery.ui.resizable.min.css a56efc4d7558b342eace7f1d8c7cf3cb
themes/base/minified/jquery.ui.selectable.min.css 88a814a714f7d311581d3d2531c86b77
themes/base/minified/jquery.ui.slider.min.css 4d4f15e54e1dfb6bbc7d426cd9b8a800
themes/base/minified/jquery.ui.spinner.min.css c76035eb61639db767cf68186be3ef77
themes/base/minified/jquery.ui.tabs.min.css 0accc59e9a5c604f42314d9a3b86ab96
themes/base/minified/jquery.ui.theme.min.css bb82ca2fb091232e80da9ea110ead128
themes/base/minified/jquery.ui.tooltip.min.css 45c0991b4fa0d6cc1878bc8888d55149
ui.accordion.jquery.json 1653dbe5add587efefd0a3ce22934123
ui.autocomplete.jquery.json b5c59b2f981689a9cf49968953493b7e
ui.button.jquery.json cc18c0ca588d21c43376248d16935aaf
ui.core.jquery.json a328dc695d93239ae48799501b6eec9b
ui.datepicker.jquery.json 491eb8e2e622ea4a2ad89fa953f388a9
ui.dialog.jquery.json c5a1e8bf6710e04794c70167366f52a6
ui.draggable.jquery.json e73fd7e3ce3c79585d0c72185f28f917
ui.droppable.jquery.json 8edd833d1e806213c2d394526ebd1375
ui.effect-blind.jquery.json 1eb235c043184f8c7283385e6192d628
ui.effect-bounce.jquery.json 10607d25ea1372b390d7a691b790bda4
ui.effect-clip.jquery.json 20c91ac63b888c566cdd8eb549d36e2a
ui.effect-drop.jquery.json 39621cca64df51a69da7208db38a7b81
ui.effect-explode.jquery.json 9bd57a0453276e076e28a8bf098cb8da
ui.effect-fade.jquery.json 61b2f08373536cf450e9bdad37e2642d
ui.effect-fold.jquery.json f82e0995a9f0c17473fc30c99cdec500
ui.effect-highlight.jquery.json d8b8de3949e649c8c71ff80e5f7638fe
ui.effect-pulsate.jquery.json 7438063e95e07f5c84f4926aabaed126
ui.effect-scale.jquery.json 12e8a2ecec24189ac1cbc7ad08edb8f9
ui.effect-shake.jquery.json 14df6355345e19f1ff83f7e3a5f8e577
ui.effect-slide.jquery.json 0654f00c18c8567970396a29f0e78d75
ui.effect-transfer.jquery.json 0ce7803c05f9b0ba2c0e556534c9c8ca
ui.effect.jquery.json d22d3d8c34df8b3402d1af1dd489358f
ui.menu.jquery.json 9015b4ef9ec6b51fe54857e7a82b2ae2
ui.mouse.jquery.json e2717f8c3ebb11255a1f9178b4689943
ui.position.jquery.json 659c040e45dbcaba166c64f5fb97a0fc
ui.progressbar.jquery.json ad91ba2e4fc4d0be67e309b90cfabe16
ui.resizable.jquery.json 3060ebb7b73eaffe26a890eb7513ebfd
ui.selectable.jquery.json 105f25469b30db6df284c3ae1fa2ac81
ui.slider.jquery.json 93c7047e401c44fad2982b40aee36239
ui.sortable.jquery.json ccf1df56232cf1a75cad27ef0ca6c4b4
ui.spinner.jquery.json f7173639f6ae4474709607d7bb1b106e
ui.tabs.jquery.json 49c67464eb5bd3398e89466591ce432b
ui.tooltip.jquery.json a2ddca9d7c3ac0e273bb0f02a72f452d
ui.widget.jquery.json a05552d47eb8d02044409fc16c32d320
ui/i18n/jquery-ui-i18n.js 7c027e664bcbda347e119363551cd78a
ui/i18n/jquery.ui.datepicker-af.js 3f6dc7167ebfdab2e4c06ca1f7ecbf55
ui/i18n/jquery.ui.datepicker-ar-DZ.js 56090ba6fc5c479ae4cda7c576836820
ui/i18n/jquery.ui.datepicker-ar.js 942eaa8e2a371c9858a75e3f58ac27c0
ui/i18n/jquery.ui.datepicker-az.js 1aea4f0f8c0e6a30328f192aff2a1c0f
ui/i18n/jquery.ui.datepicker-be.js 47798259b2d017908e77584020a46d8c
ui/i18n/jquery.ui.datepicker-bg.js 24c64c898073fdf4e176c7a57eed2108
ui/i18n/jquery.ui.datepicker-bs.js 02b7d071b1bd94ea1bf6b80434bf25bd
ui/i18n/jquery.ui.datepicker-ca.js 94457329c5dfa19ec34ca758ef9af96f
ui/i18n/jquery.ui.datepicker-cs.js 8b24b1eb5c72e00db3eaf1893712535d
ui/i18n/jquery.ui.datepicker-cy-GB.js fb40b70ba78ef9f4251a86355c5f65f7
ui/i18n/jquery.ui.datepicker-da.js 4ca3479af4b128605a4b1ba0a460864b
ui/i18n/jquery.ui.datepicker-de.js 9e724b55121fe31b366ec12cdf7dd04f
ui/i18n/jquery.ui.datepicker-el.js 9e5f4ce80bfa2188a0c39bc0ef3f67bc
ui/i18n/jquery.ui.datepicker-en-AU.js 4a38655904f6c55da227cea464b55a2b
ui/i18n/jquery.ui.datepicker-en-GB.js 24a226a281a11799c495abc21f696c23
ui/i18n/jquery.ui.datepicker-en-NZ.js af985e8d034123f14696aa116027760d
ui/i18n/jquery.ui.datepicker-eo.js 284e3234ab912cab90b7df798d2229d6
ui/i18n/jquery.ui.datepicker-es.js 063c7ec2c48ba362e5a5d80dbb3add9d
ui/i18n/jquery.ui.datepicker-et.js 7340534709a670fdc0e6c778f81e69d0
ui/i18n/jquery.ui.datepicker-eu.js 24751dd4dcabb58b82ee0817fea84fd3
ui/i18n/jquery.ui.datepicker-fa.js 5e6dab546d1aeaba3ea1b08cb8a78905
ui/i18n/jquery.ui.datepicker-fi.js a2316212db1e436c816e97bbaf408fa0
ui/i18n/jquery.ui.datepicker-fo.js afd8c9bc15c5885362c656e5d7c84890
ui/i18n/jquery.ui.datepicker-fr-CA.js caa86bb3277b220d6412236a27a3fc2d
ui/i18n/jquery.ui.datepicker-fr-CH.js 7e82004b7c69db43aa72d4cbb1713b6f
ui/i18n/jquery.ui.datepicker-fr.js 74ff0c1602ce756629885acbd13680b8
ui/i18n/jquery.ui.datepicker-gl.js 4675f94f450180e380a1601a6b371508
ui/i18n/jquery.ui.datepicker-he.js a791d8f0ac08b0d2876bcd21e029d39a
ui/i18n/jquery.ui.datepicker-hi.js 0a77f05797ea2541aefa54587f72d274
ui/i18n/jquery.ui.datepicker-hr.js 09c6d590bc6341774a7b6fe81949bcc1
ui/i18n/jquery.ui.datepicker-hu.js df7114c7beb23fac576e3ad4dd58f826
ui/i18n/jquery.ui.datepicker-hy.js bf602b00d963e8060915f407aa06117c
ui/i18n/jquery.ui.datepicker-id.js fb0ad98a3ad212b1986fcac5015b0435
ui/i18n/jquery.ui.datepicker-is.js 24fe69d4dfc3f807c0409c1825bf06c1
ui/i18n/jquery.ui.datepicker-it.js eee4c720914c640bcd852ba846549823
ui/i18n/jquery.ui.datepicker-ja.js 8f22d47039a716ab9dc558ef5cb606c5
ui/i18n/jquery.ui.datepicker-ka.js 59ced607df538bff4b30bde208558e78
ui/i18n/jquery.ui.datepicker-kk.js 17cd8c09bfc4fb4d1009d0d7251d45bc
ui/i18n/jquery.ui.datepicker-km.js 1adb43c3aaa02820ddc47bd6291c35d5
ui/i18n/jquery.ui.datepicker-ko.js c07463fd9be92f2a74a8693e11a3a6ed
ui/i18n/jquery.ui.datepicker-ky.js 78c4fbe7edc801a3d7ac96479f38c471
ui/i18n/jquery.ui.datepicker-lb.js 070ff75dff2e4c5ecaeefcf69362a824
ui/i18n/jquery.ui.datepicker-lt.js 241a4dbd5a6a6eebf0bff07d898d70ef
ui/i18n/jquery.ui.datepicker-lv.js c0f98f27e580e081955e201b181fb061
ui/i18n/jquery.ui.datepicker-mk.js a5f91ba250568d5923bc0d7cb5cbc949
ui/i18n/jquery.ui.datepicker-ml.js 183a9c567fe5c2d5024fb82cee66ff32
ui/i18n/jquery.ui.datepicker-ms.js 51efc50e21ae012a17f4f3cd0f2ac93d
ui/i18n/jquery.ui.datepicker-nb.js 1f791342b878511cb7f589cd27b04521
ui/i18n/jquery.ui.datepicker-nl-BE.js 1e12ff741b12964e1fd73673d4aaf44d
ui/i18n/jquery.ui.datepicker-nl.js e2e6f9efa2cd758e29c5a5d9463f3a4f
ui/i18n/jquery.ui.datepicker-nn.js 81637fae116f19daaaeec66ece964b4b
ui/i18n/jquery.ui.datepicker-no.js 2fa4f2894f94ecf35729c51e8183addf
ui/i18n/jquery.ui.datepicker-pl.js be287cf01c812b8df9e69fd84b075cef
ui/i18n/jquery.ui.datepicker-pt-BR.js dff6b4f131f72032ff0a69a555c7c6a4
ui/i18n/jquery.ui.datepicker-pt.js 195beac164d3b2cfc5b8d02e98353834
ui/i18n/jquery.ui.datepicker-rm.js 0601228208954434efea2ccf265f5b94
ui/i18n/jquery.ui.datepicker-ro.js 6ca441ce332624416e84ad38034e7795
ui/i18n/jquery.ui.datepicker-ru.js 0d41df9a86abe85b7b63028957c73106
ui/i18n/jquery.ui.datepicker-sk.js 87b2273b7f29f1e0d848891337beb5c4
ui/i18n/jquery.ui.datepicker-sl.js 486c5cd6945b02f0749df41fdc3f352e
ui/i18n/jquery.ui.datepicker-sq.js 77e864f9841f5152d5920daf9e3e2f32
ui/i18n/jquery.ui.datepicker-sr-SR.js 7bdeb5ef060f89148dcd12c60fba60c6
ui/i18n/jquery.ui.datepicker-sr.js a4e1213e926bba1252da0081dfeccec3
ui/i18n/jquery.ui.datepicker-sv.js 018ff01252e3a0ac5bb15dbe39b429cd
ui/i18n/jquery.ui.datepicker-ta.js 3bf8496fd386e4694af8823300a532e3
ui/i18n/jquery.ui.datepicker-th.js abd6001b82c780af58bfe9a64d0a0d9a
ui/i18n/jquery.ui.datepicker-tj.js bab95a8bfd829041af4be1c5a782c935
ui/i18n/jquery.ui.datepicker-tr.js adc631c4ab7b682d16a2153c27ce288c
ui/i18n/jquery.ui.datepicker-uk.js aae92337f25274e12be54766da9ad8d0
ui/i18n/jquery.ui.datepicker-vi.js eb28234f86f04fc28490f893fa4238fe
ui/i18n/jquery.ui.datepicker-zh-CN.js 26131e8a11678170b28c0a469d43aaee
ui/i18n/jquery.ui.datepicker-zh-HK.js d283c9c7266ad9f6fa2dbc46db3026f5
ui/i18n/jquery.ui.datepicker-zh-TW.js 443e52a6fae355caecbd60e63b5c72d3
ui/jquery-ui.js 3eee9a6b26f48cb2d730fb5fad7bf221
ui/jquery.ui.accordion.js c09467e2a4aef52081a63db7400f6ebe
ui/jquery.ui.autocomplete.js f4b0a7f9d451acee52fcfa858715424a
ui/jquery.ui.button.js dc058c7f2d76345aa21620db5217a50b
ui/jquery.ui.core.js a29d6d9466b021bd343ccca3e12cb79c
ui/jquery.ui.datepicker.js db82c886ef81cd75e9d663086f8e7c39
ui/jquery.ui.dialog.js f63d63b6dbb12f29cfb64935f54dceea
ui/jquery.ui.draggable.js a12eb3f69ed624e291a1449ffc76dbdf
ui/jquery.ui.droppable.js f6d9ccf8e949d7bd07113a6b4f7ab0d1
ui/jquery.ui.effect-blind.js 261b80fd422229847f1c6199b7474b40
ui/jquery.ui.effect-bounce.js 0fb974ba62c9090b6a4e181922d8d0a3
ui/jquery.ui.effect-clip.js 518c0b37430b0c31d1ce612f47bc7a38
ui/jquery.ui.effect-drop.js 283ff4034aafec0f7499b62100be1245
ui/jquery.ui.effect-explode.js fc49b90ea9549cf445e3a48292d48636
ui/jquery.ui.effect-fade.js 4b5a3698f59c363caf8b412f8e96ffe9
ui/jquery.ui.effect-fold.js c438f43346f724e056fbfd3f176604de
ui/jquery.ui.effect-highlight.js 94e1a411e2b95efbb4d3db4c67f88001
ui/jquery.ui.effect-pulsate.js 62dc900168c198dd71982bdbc51862cb
ui/jquery.ui.effect-scale.js 91e6c2071484246b0285aee95c84359a
ui/jquery.ui.effect-shake.js 9d058bdc6fe3d682b64047b453cc8277
ui/jquery.ui.effect-slide.js d01adb2cab86b89b2ba3e95887cf8d71
ui/jquery.ui.effect-transfer.js 1ae33b97ced71a500e5792821dddbe89
ui/jquery.ui.effect.js cc8c8aef82d6afb7ab28a006b631d111
ui/jquery.ui.menu.js 80677de9342c936284f52f88fd9b8bfa
ui/jquery.ui.mouse.js ec55a32cdfa93ff2b3d2da76255ee7f1
ui/jquery.ui.position.js 6d72c0833f46b6496fe49f02a3cf2c3e
ui/jquery.ui.progressbar.js 3c2fc85abf5692172584144e78f48252
ui/jquery.ui.resizable.js 6d83739ff4903d0252bab9741bda0ea4
ui/jquery.ui.selectable.js d7eda1187fbee25351be7788007b172d
ui/jquery.ui.slider.js 693c6a6856070dd23d1a2930c9c98d64
ui/jquery.ui.sortable.js caf69f5c0437e26919ea2b4dac859041
ui/jquery.ui.spinner.js 0a04f784e0344303fe3fa7ea5381eb59
ui/jquery.ui.tabs.js 9ac8652711c2f951a4d1686f3e668503
ui/jquery.ui.tooltip.js 8db4414b63a404a73ec19bd1ed8f0d37
ui/jquery.ui.widget.js 58a5c152a3f6d7951f26f54b1b18754e
ui/minified/i18n/jquery-ui-i18n.min.js 14f952d86a7f148bef7b8c4272b65ae8
ui/minified/i18n/jquery.ui.datepicker-af.min.js 148183066ea0e2c85b09de5a42b7d269
ui/minified/i18n/jquery.ui.datepicker-ar-DZ.min.js 5d4c9011af6bf255afb5baef0b25bd37
ui/minified/i18n/jquery.ui.datepicker-ar.min.js b5887b106752a0af56b7d4f9ec47618a
ui/minified/i18n/jquery.ui.datepicker-az.min.js a61fb539994004e6d9f4bde9fce46749
ui/minified/i18n/jquery.ui.datepicker-be.min.js df6be4e0fce885870e8fc7975141924f
ui/minified/i18n/jquery.ui.datepicker-bg.min.js cbb357573261ea3a0d73c2e9dae669de
ui/minified/i18n/jquery.ui.datepicker-bs.min.js 0ac10cda4f2863271b23a145d0f3da87
ui/minified/i18n/jquery.ui.datepicker-ca.min.js 4d926a595912dd9704d3e9570a3064d6
ui/minified/i18n/jquery.ui.datepicker-cs.min.js 30dc9ce1c83403546d8bd2c7b2bdeebc
ui/minified/i18n/jquery.ui.datepicker-cy-GB.min.js b52e88abf4da36e6ffb83fdd28b9ad42
ui/minified/i18n/jquery.ui.datepicker-da.min.js 19ef59f5e0673b4b9dd846147677335d
ui/minified/i18n/jquery.ui.datepicker-de.min.js a51def63fb0daf2099ad397201a0d7d7
ui/minified/i18n/jquery.ui.datepicker-el.min.js dc850ec8ab03d0a8f6e28d28d5d74b5e
ui/minified/i18n/jquery.ui.datepicker-en-AU.min.js 7304953f185fea621d806ad47da40306
ui/minified/i18n/jquery.ui.datepicker-en-GB.min.js 6a4ab0da8b1b697b06e6fcaa98b76180
ui/minified/i18n/jquery.ui.datepicker-en-NZ.min.js c96d9d2cae717b94d42b2d1b2d95626d
ui/minified/i18n/jquery.ui.datepicker-eo.min.js 8b3472fced5ef5dceb1c6f31bbff6098
ui/minified/i18n/jquery.ui.datepicker-es.min.js eaa20d4e50f6ac6f8e12c5f021d5c069
ui/minified/i18n/jquery.ui.datepicker-et.min.js 17137238138377c67e3ea9e178502111
ui/minified/i18n/jquery.ui.datepicker-eu.min.js 846da5fb3abb48b368c761f68417992a
ui/minified/i18n/jquery.ui.datepicker-fa.min.js c7324cdf96af36226dcc25e2e3e7ea92
ui/minified/i18n/jquery.ui.datepicker-fi.min.js 58324acdc84fab84a4318edd2385d6fb
ui/minified/i18n/jquery.ui.datepicker-fo.min.js 516f23f0886ee2206050279a3ae92a09
ui/minified/i18n/jquery.ui.datepicker-fr-CA.min.js 121e8f684013b3e997901c06fbf0d966
ui/minified/i18n/jquery.ui.datepicker-fr-CH.min.js 1a2e0d9116fe0fa7822e60e272cc5622
ui/minified/i18n/jquery.ui.datepicker-fr.min.js 8ac791ee44c4595b66cb5ef9aa0b4ee8
ui/minified/i18n/jquery.ui.datepicker-gl.min.js 5cbc0ea0e1f1a44e021f6397895d32a9
ui/minified/i18n/jquery.ui.datepicker-he.min.js 0baef5f069669b8daf29a39d59f1d94a
ui/minified/i18n/jquery.ui.datepicker-hi.min.js ea03342360891e7cb0a3602e5fda2ff6
ui/minified/i18n/jquery.ui.datepicker-hr.min.js 021473e4ea383455da07cc7051de1670
ui/minified/i18n/jquery.ui.datepicker-hu.min.js fa7636bb85f2ba6d54be89023451b1d7
ui/minified/i18n/jquery.ui.datepicker-hy.min.js 870b4d27d177395ebcc18da7e58fe068
ui/minified/i18n/jquery.ui.datepicker-id.min.js 2f0843cb8e18573694369fb40a3c039d
ui/minified/i18n/jquery.ui.datepicker-is.min.js 1caa7185672fd662061eeb0470b794c8
ui/minified/i18n/jquery.ui.datepicker-it.min.js af2f67b295e5284d185acbffbaac5b24
ui/minified/i18n/jquery.ui.datepicker-ja.min.js fbb413a52c8443fe210d2a8a5f8003ce
ui/minified/i18n/jquery.ui.datepicker-ka.min.js ca1ddaf6a87ba37f661ec08917ae3a78
ui/minified/i18n/jquery.ui.datepicker-kk.min.js e98234e8b8e4878850390546fe07c215
ui/minified/i18n/jquery.ui.datepicker-km.min.js 070666203dbc0101bec19795e436d5c2
ui/minified/i18n/jquery.ui.datepicker-ko.min.js f883b29966c645cb39ac62cc35f2e891
ui/minified/i18n/jquery.ui.datepicker-ky.min.js 7f8ca400dae73cbae750707627f9ddc7
ui/minified/i18n/jquery.ui.datepicker-lb.min.js d506f478ce3f4454b4ba15545214cc5f
ui/minified/i18n/jquery.ui.datepicker-lt.min.js a20d7148e77f2541f9fee239fcd1b09b
ui/minified/i18n/jquery.ui.datepicker-lv.min.js c83e999dfe09c79502db1e481b076487
ui/minified/i18n/jquery.ui.datepicker-mk.min.js 31e735fac86456785d29142cec0c2f54
ui/minified/i18n/jquery.ui.datepicker-ml.min.js e0448c1d5823a09684cd4b9f48ee5217
ui/minified/i18n/jquery.ui.datepicker-ms.min.js a772e9762d31940bfcef6a0da1b9d192
ui/minified/i18n/jquery.ui.datepicker-nb.min.js dc5a2127747ec1d24d6c1a09dd54779d
ui/minified/i18n/jquery.ui.datepicker-nl-BE.min.js 0412341132c71739f3ad15714f61640f
ui/minified/i18n/jquery.ui.datepicker-nl.min.js 26378c901b567103a1157571f553aa3a
ui/minified/i18n/jquery.ui.datepicker-nn.min.js b229f968db6e46054963b603a590c07b
ui/minified/i18n/jquery.ui.datepicker-no.min.js aad4d5802404e8b8e17dfacda39efa08
ui/minified/i18n/jquery.ui.datepicker-pl.min.js 3a17e02528f23319ef477ca6e95d3eca
ui/minified/i18n/jquery.ui.datepicker-pt-BR.min.js 2b98939615e56026b4a52741ddfa7337
ui/minified/i18n/jquery.ui.datepicker-pt.min.js d6e6210c74a1701b38d821effeba691a
ui/minified/i18n/jquery.ui.datepicker-rm.min.js 1631c702c4b19e79ba531edb8e4eeeb6
ui/minified/i18n/jquery.ui.datepicker-ro.min.js bec7b7f95d557edc38acc2c0d7a32d68
ui/minified/i18n/jquery.ui.datepicker-ru.min.js 17a237070213243b23de8e655c2a7c5c
ui/minified/i18n/jquery.ui.datepicker-sk.min.js ca08f2b75b3b63974ab92a163b9de730
ui/minified/i18n/jquery.ui.datepicker-sl.min.js af1c2f15cda8eeaf6cd2876e374a0256
ui/minified/i18n/jquery.ui.datepicker-sq.min.js dc5c9a3472b3fbb8a9e920bd36c120ab
ui/minified/i18n/jquery.ui.datepicker-sr-SR.min.js 9d8bc96977c703e21f708acbfed42cd6
ui/minified/i18n/jquery.ui.datepicker-sr.min.js da6676396e88a5e6eb9894be1e153816
ui/minified/i18n/jquery.ui.datepicker-sv.min.js 13d16d6bb0f0a9b2e87ead4a8a3d0438
ui/minified/i18n/jquery.ui.datepicker-ta.min.js c82f2737812a18ca5184cfd37d2b5cc2
ui/minified/i18n/jquery.ui.datepicker-th.min.js bf581646acce8ce8f59e429aa7960c8e
ui/minified/i18n/jquery.ui.datepicker-tj.min.js 67cdc55b176b101af03b0d8feeac6dae
ui/minified/i18n/jquery.ui.datepicker-tr.min.js dd0aded06f33b425681cb64c38b48c8c
ui/minified/i18n/jquery.ui.datepicker-uk.min.js 7d1e2bb70d141e3ab71a649f55a6f822
ui/minified/i18n/jquery.ui.datepicker-vi.min.js 5c209069c32a554decc682dbddfc559e
ui/minified/i18n/jquery.ui.datepicker-zh-CN.min.js feac040176ba0702fc88751ba77dcfd8
ui/minified/i18n/jquery.ui.datepicker-zh-HK.min.js 0be6dc96331f78835488a9eae271f6e8
ui/minified/i18n/jquery.ui.datepicker-zh-TW.min.js ff4acdb2d9fa877364e1c6ee51141ff6
ui/minified/jquery-ui.min.js 3e6acb1e6426ef90d2e786a006a4ea28
ui/minified/jquery.ui.accordion.min.js c26a80264fa7624dc4acf805e22bb3c8
ui/minified/jquery.ui.autocomplete.min.js 0ce8294df3f9c6eb59e9dec6c1be7e06
ui/minified/jquery.ui.button.min.js 0949a950cff216e6e86e9078f6824d43
ui/minified/jquery.ui.core.min.js b18a32c93318cb8a9d270c9b0f4b7119
ui/minified/jquery.ui.datepicker.min.js 736fb0e713579c82cad9a640d1286400
ui/minified/jquery.ui.dialog.min.js 1615e72183862faa389b147a219d899b
ui/minified/jquery.ui.draggable.min.js baed3a46d947fc64c149531c950d2205
ui/minified/jquery.ui.droppable.min.js 72ea59f6c925bfb8057717e0db2af4df
ui/minified/jquery.ui.effect-blind.min.js 133f1e09af92babfe2f105cf8114c8b1
ui/minified/jquery.ui.effect-bounce.min.js 47b270d1bc72491ba8eb8e114313106e
ui/minified/jquery.ui.effect-clip.min.js 4562734a28eb6a28ca9bbb1345845e30
ui/minified/jquery.ui.effect-drop.min.js c7c4506f9fc2d6e256fc9c87051f4d28
ui/minified/jquery.ui.effect-explode.min.js f42a1d02c952523ae63d8e9d0c038564
ui/minified/jquery.ui.effect-fade.min.js 0240f088f755570918d7392c22182b33
ui/minified/jquery.ui.effect-fold.min.js 9e1bd12d6951e7488fdf11b5303e3948
ui/minified/jquery.ui.effect-highlight.min.js d1bc2bd7f1effddc3ba64fbce12a04d0
ui/minified/jquery.ui.effect-pulsate.min.js 0b1cbfbba95623baf48f9d06e0c71d94
ui/minified/jquery.ui.effect-scale.min.js 12b62d4009b94c6b179370be4d47a0d8
ui/minified/jquery.ui.effect-shake.min.js 496f2d4a66c1cd045d283d2c652ab78c
ui/minified/jquery.ui.effect-slide.min.js 06d6273a42592149c92632bb5f4b41fa
ui/minified/jquery.ui.effect-transfer.min.js 762d93af20627a844c960bad9a5b10ec
ui/minified/jquery.ui.effect.min.js 71465c8c4c075daecba0ecc2cb82e798
ui/minified/jquery.ui.menu.min.js 662d7945bdef313e7f2c7e7e30333f4a
ui/minified/jquery.ui.mouse.min.js 67ed7fa52484a88f343490aadccb9780
ui/minified/jquery.ui.position.min.js e164077a4a51602212b99afa9a4733e0
ui/minified/jquery.ui.progressbar.min.js 8f621020d361b67053ae9c29c3d775d8
ui/minified/jquery.ui.resizable.min.js 425269cc09ad88648a7f49e51e43894c
ui/minified/jquery.ui.selectable.min.js 0932d591319bec5219abcd2fc164e7e2
ui/minified/jquery.ui.slider.min.js 30aaa48ce68e325cd49cb42696c44436
ui/minified/jquery.ui.sortable.min.js dfd98d9d8b000f4e9c49cbf5793d1a95
ui/minified/jquery.ui.spinner.min.js d7277d6a0c9f46024f072532cceb59ee
ui/minified/jquery.ui.tabs.min.js cf03157fa462226d102e097355bbf258
ui/minified/jquery.ui.tooltip.min.js 1e50cc11dc912265fdc593d13e0b358d
ui/minified/jquery.ui.widget.min.js f85896deacad3168a5dc492a22129495
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/MIT-LICENSE.txt000066400000000000000000000024701231750357400222710ustar00rootroot00000000000000Copyright 2013 jQuery Foundation and other contributors,
http://jqueryui.com/

This software consists of voluntary contributions made by many
individuals (AUTHORS.txt, http://jqueryui.com/about) For exact
contribution history, see the revision history and logs, available
at http://jquery-ui.googlecode.com/svn/

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/README.md000066400000000000000000000065751231750357400213100ustar00rootroot00000000000000[jQuery UI](http://jqueryui.com/) - Interactions and Widgets for the web
================================

jQuery UI provides interactions like Drag and Drop and widgets like Autocomplete, Tabs and Slider and makes these as easy to use as jQuery itself.

If you want to use jQuery UI, go to [jqueryui.com](http://jqueryui.com) to get started. Or visit the [Using jQuery UI Forum](http://forum.jquery.com/using-jquery-ui) for discussions and questions.

If you are interested in helping develop jQuery UI, you are in the right place.
To discuss development with team members and the community, visit the [Developing jQuery UI Forum](http://forum.jquery.com/developing-jquery-ui) or in #jquery on irc.freednode.net.


For contributors
---

If you want to help and provide a patch for a bugfix or new feature, please take
a few minutes and look at [our Getting Involved guide](http://wiki.jqueryui.com/w/page/35263114/Getting-Involved).
In particular check out the [Coding standards](http://wiki.jqueryui.com/w/page/12137737/Coding-standards)
and [Commit Message Style Guide](http://wiki.jqueryui.com/w/page/25941597/Commit-Message-Style-Guide).

In general, fork the project, create a branch for a specific change and send a
pull request for that branch. Don't mix unrelated changes. You can use the commit
message as the description for the pull request.


Running the Unit Tests
---

Run the unit tests with a local server that supports PHP. No database is required. Pre-configured php local servers are available for Windows and Mac. Here are some options:

- Windows: [WAMP download](http://www.wampserver.com/en/)
- Mac: [MAMP download](http://www.mamp.info/en/index.html)
- Linux: [Setting up LAMP](https://www.linux.com/learn/tutorials/288158-easy-lamp-server-installation)
- [Mongoose (most platforms)](http://code.google.com/p/mongoose/)


Building jQuery UI
---

jQuery UI uses the [grunt](http://github.com/cowboy/grunt) build system. Building jQuery UI requires node.js and a command line zip program.

Install grunt.

`npm install grunt -g`

Clone the jQuery UI git repo.

`git clone git://github.com/jquery/jquery-ui.git`

`cd jquery-ui`

Install node modules.

`npm install`

Run grunt.

`grunt build`

There are many other tasks that can be run through grunt. For a list of all tasks:

`grunt --help`


For committers
---

When looking at pull requests, first check for [proper commit messages](http://wiki.jqueryui.com/w/page/12137724/Bug-Fixing-Guide).

Do not merge pull requests directly through GitHub's interface.
Most pull requests are a single commit; cherry-picking will avoid creating a merge commit.
It's also common for contributors to make minor fixes in an additional one or two commits.
These should be squashed before landing in master.

**Make sure the author has a valid name and email address associated with the commit.**

Fetch the remote first:

    git fetch [their-fork.git] [their-branch]

Then cherry-pick the commit(s):

	git cherry-pick [sha-of-commit]

If you need to edit the commit message:

    git cherry-pick -e [sha-of-commit]

If you need to edit the changes:

	git cherry-pick -n [sha-of-commit]
	# make changes
	git commit --author="[author-name-and-email]"

If it should go to the stable brach, cherry-pick it to stable:

    git checkout 1-8-stable
    git cherry-pick -x [sha-of-commit-from-master]

*NOTE: Do not cherry-pick into 1-8-stable until you have pushed the commit from master upstream.*
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/000077500000000000000000000000001231750357400211235ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/000077500000000000000000000000001231750357400230645ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/collapsible.html000066400000000000000000000045031231750357400262450ustar00rootroot00000000000000


	
	jQuery UI Accordion - Collapse content
	
	
	
	
	
	
	



Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

By default, accordions always keep one section open. To allow for all sections to be be collapsible, set the collapsible option to true. Click on the currently open section to collapse its content pane.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/custom-icons.html000066400000000000000000000054061231750357400264020ustar00rootroot00000000000000 jQuery UI Accordion - Customize icons

Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Customize the header icons with the icons option, which accepts classes for the header's default and active (open) state. Use any class from the UI CSS framework, or create custom classes with background images.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/default.html000066400000000000000000000046511231750357400254040ustar00rootroot00000000000000 jQuery UI Accordion - Default functionality

Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Click headers to expand/collapse content that is broken into logical sections, much like tabs. Optionally, toggle sections open/closed on mouseover.

The underlying HTML markup is a series of headers (H3 tags) and content divs so the content is usable without JavaScript.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/fillspace.html000066400000000000000000000057111231750357400257200ustar00rootroot00000000000000 jQuery UI Accordion - Fill space

Resize the outer container:

Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Because the accordion is comprised of block-level elements, by default its width fills the available horizontal space. To fill the vertical space allocated by its container, set the heightStyle option to "fill", and the script will automatically set the dimensions of the accordion to the height of its parent container.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/hoverintent.html000066400000000000000000000077661231750357400263370ustar00rootroot00000000000000 jQuery UI Accordion - Open on hoverintent

Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Click headers to expand/collapse content that is broken into logical sections, much like tabs. Optionally, toggle sections open/closed on mouseover.

The underlying HTML markup is a series of headers (H3 tags) and content divs so the content is usable without JavaScript.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/index.html000066400000000000000000000010361231750357400250610ustar00rootroot00000000000000 jQuery UI Accordion Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/no-auto-height.html000066400000000000000000000033301231750357400266010ustar00rootroot00000000000000 jQuery UI Accordion - No auto height

Section 1

Mauris mauris ante, blandit et, ultrices a, susceros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item
  • List item
  • List item
  • List item
  • List item
  • List item
  • List item

Setting heightStyle: "content" allows the accordion panels to keep their native height.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/accordion/sortable.html000066400000000000000000000053551231750357400255750ustar00rootroot00000000000000 jQuery UI Accordion - Sortable

Section 1

Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.

Section 2

Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In suscipit faucibus urna.

Section 3

Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis. Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.

  • List item one
  • List item two
  • List item three

Section 4

Cras dictum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia mauris vel est.

Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.

Drag the header to re-order panels.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/addClass/000077500000000000000000000000001231750357400226415ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/addClass/default.html000066400000000000000000000026111231750357400251530ustar00rootroot00000000000000 jQuery UI Effects - addClass demo
Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede.
Run Effect

This demo adds a class which animates: text-indent, letter-spacing, width, height, padding, margin, and font-size.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/addClass/index.html000066400000000000000000000003171231750357400246370ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/animate/000077500000000000000000000000001231750357400225415ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/animate/default.html000066400000000000000000000027631231750357400250630ustar00rootroot00000000000000 jQuery UI Effects - Animate demo

Animate

Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede. Nulla lorem metus, adipiscing ut, luctus sed, hendrerit vitae, mi.

Toggle Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/animate/index.html000066400000000000000000000003171231750357400245370ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/000077500000000000000000000000001231750357400236245ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/categories.html000066400000000000000000000035021231750357400266370ustar00rootroot00000000000000 jQuery UI Autocomplete - Categories

A categorized search result. Try typing "a" or "n".

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/combobox.html000066400000000000000000000137271231750357400263340ustar00rootroot00000000000000 jQuery UI Autocomplete - Combobox

A custom widget built by composition of Autocomplete and Button. You can either type something into the field to get filtered suggestions based on your input, or use the button to get the full list of selections.

The input is read from an existing select-element for progressive enhancement, passed to Autocomplete with a customized source-option.

This is not a supported or even complete widget. Its purely for demoing what autocomplete can do with a bit of customization. For a detailed explanation of how the widget works, check out this Learning jQuery article.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/custom-data.html000066400000000000000000000045641231750357400267440ustar00rootroot00000000000000 jQuery UI Autocomplete - Custom data and display
Select a project (type "j" for a start):

You can use your own custom data formats and displays by simply overriding the default focus and select actions.

Try typing "j" to get a list of projects or just press the down arrow.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/default.html000066400000000000000000000026051231750357400261410ustar00rootroot00000000000000 jQuery UI Autocomplete - Default functionality

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are tags for programming languages, give "ja" (for Java or JavaScript) a try.

The datasource is a simple JavaScript array, provided to the widget using the source-option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/folding.html000066400000000000000000000034501231750357400261360ustar00rootroot00000000000000 jQuery UI Autocomplete - Accent folding

The autocomplete field uses a custom source option which will match results that have accented characters even when the text field doesn't contain accented characters. However if the you type in accented characters in the text field it is smart enough not to show results that aren't accented.

Try typing "Jo" to see "John" and "Jörn", then type "Jö" to see only "Jörn".

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/images/000077500000000000000000000000001231750357400250715ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/images/jquery_32x32.png000066400000000000000000000026111231750357400277570ustar00rootroot00000000000000PNG  IHDR DPLTE#   !"!@BB "ACA & ! @)D:O BA BB$2/^~(?$!@W8q *:">~:t;QB8L@,C%4H"+UsAD%0`4h1b?'Lf'66m =TC)Pl&43e:u,=!:v(C&Kf!?V@+;).\|*Qn('52E T'Mi)Ok"0"AXC0_.>(NjB3f 9t'Kf2D#1a!-6J&5"BZ:u(Oi;y*&1`3G4j-Zy2e'Mg-Xw*; -D+(Pk6l*Rn,Wv ,+Ur;R#E^>}$F_)PmC*Ro 8q-Yx1BA9s.@1b$1EC6lB*9'0`5j2d,Vt0`!-A"..]~#C\2c#1$@9r+Tq0C=z(8L1CC7p ?V3F->/A7o&51`#D\@-Xx'$7p@8o =S U*#07o/@0A/@"0  &ю IDATx^Sc,1MZ۶mٶm۶m۶mOtlͫEmջR\#eMB4tLN\|#nUo3zy6¥z)Q+!A΀ke @ma[=OɀVQqŘ;Iav\s89EH$%cNT&'#b&`<ܵ(F (L\XG?^tTMPv/9B'C4E?b $2!)C}C{ ˍ+tڡU `h: dLEy\WYtW2}gF?7kF/EϲX..YF֪4+BW&E \ƮWĆnn^[+=+w^]3tp"Eɴ@C8D}g%j&;~"@ue6Y#zWD-hi';AEJ$ X.3y^b.{+%Tݟ5eC’BQ7|+H97nL;w| 4Qo`mF=y2lJN!&/`|?y|F _˾Y7\4Br¿[?OLcEIENDB`ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/images/jqueryui_32x32.png000066400000000000000000000022511231750357400303150ustar00rootroot00000000000000PNG  IHDR pIDATxŖlSU+^n׾ڮP q!F 3B L4KĨ11*¢P8u0S1saAΑH ts^3;f''sι{tYt4&lʿRQ1dQ8MV+a;[v?nNUw?a`p09lx PG0uw5?\8$kQONiߊ@ Y ȴ*JVPO ekSDкo&,Ew3:h݀{Fؑݩ9U=39g&Do5v>~b_ov y ~t$ʍ~:jCB4`ؼbA({6P i1d_o%vޙmw0 @w{=7i^ϥ0DNөoobVG0F'BTK*0BӶY?˺lQ˛lgӋ"jx  GK]9m[22PӎHX4>i;w/[{ؕԛ5¨i_a|56qbyJҠHkFPkr w=-+ȴB}ry}ˆM#4NAzMRyiA()lSr<"rltSE5\mVi+/V9 ڨR&Qr>wE 5gkS s=99oOcSoYdmH郌%`M}Z@½IENDB`ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/images/sizzlejs_32x32.png000066400000000000000000000017471231750357400303260ustar00rootroot00000000000000PNG  IHDR IDATx}Leǿ6q f֌$Q/Ya򎂼EXȵfm&Q [^XsUX*4EP#DlwMm^gϞ9~_Jk| ye fr>(Z:oCnQ/*IOTTlATP{>Gw|P#ty~P(rEX/S%^vR,L *יV1<$^v{-7 np79vǺ:vYgl(6@S>EN1kIkM4jPd]vM[X#RNV NӖ=dyޞoz\1kDsT"S/ٽZ 2@2H 2,La &xn @ !h]jÁ²Dve0M['~!,FɉXY !(EIUVlXWF0 /fddQ1|/ &!,lح21d)׌ـ @( ^`4 OXLCA # FLp`(L (@^)B!(U;B@HgPQ $m~!,_ƠXBA&Ò|#JI2 fq ``g` #J  !j DWd@4 DA"OptG∭c!,Zɱ5-Aq 0  RhƘsh05MPKJl4 QP0 E6p A2Qg aksHF!,`֢X5WqG.ʐm0HJbXb ș`ZPMQ @XIIUɏXi$ jQuery UI Autocomplete Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/london.xml000066400000000000000000000045751231750357400256520ustar00rootroot00000000000000 6987 London 51.5084152563931 -0.125532746315002 2643743 GB United Kingdom P PPLC London 42.983389283 -81.233042387 6058560 CA Canada P PPL East London -33.0152850934643 27.9116249084473 1006984 ZA South Africa P PPL City 51.5133363996235 -0.0890064239501953 2643744 GB United Kingdom A ADM2 London 37.1289771 -84.0832646 4298960 US United States P PPL The Tower of London 51.5082349601834 -0.0763034820556641 6286786 GB United Kingdom S CSTL London Reefs 8.85 112.5333333 1879967 U RFSU Greater London 51.5 -0.1666667 2648110 GB United Kingdom A ADM2 London 46.1666667 6.0166667 2661811 CH Switzerland H STM London Borough of Islington 51.5333333 -0.1333333 3333156 GB United Kingdom A ADM2 ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/maxheight.html000066400000000000000000000032061231750357400264710ustar00rootroot00000000000000 jQuery UI Autocomplete - Scrollable results

When displaying a long list of options, you can simply set the max-height for the autocomplete menu to prevent the menu from growing too large. Try typing "a" or "s" above to get a long list of results that you can scroll through.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/multiple-remote.html000066400000000000000000000044241231750357400276420ustar00rootroot00000000000000 jQuery UI Autocomplete - Multiple, remote

Usage: Enter at least two characters to get bird name suggestions. Select a value to continue adding more names.

This is an example showing how to use the source-option along with some events to enable autocompleting multiple values into a single field.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/multiple.html000066400000000000000000000046521231750357400263540ustar00rootroot00000000000000 jQuery UI Autocomplete - Multiple values

Usage: Type something, eg. "j" to see suggestions for tagging with programming languages. Select a value, then continue typing to add more.

This is an example showing how to use the source-option along with some events to enable autocompleting multiple values into a single field.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/remote-jsonp.html000066400000000000000000000052641231750357400271430ustar00rootroot00000000000000 jQuery UI Autocomplete - Remote JSONP datasource
Powered by geonames.org
Result:

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are cities, displayed when at least two characters are entered into the field.

In this case, the datasource is the geonames.org webservice. While only the city name itself ends up in the input after selecting an element, more info is displayed in the suggestions to help find the right entry. That data is also available in callbacks, as illustrated by the Result area below the input.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/remote-with-cache.html000066400000000000000000000031551231750357400300230ustar00rootroot00000000000000 jQuery UI Autocomplete - Remote with caching

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are bird names, displayed when at least two characters are entered into the field.

Similar to the remote datasource demo, though this adds some local caching to improve performance. The cache here saves just one query, and could be extended to cache multiple values, one for each term.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/remote.html000066400000000000000000000035551231750357400260150ustar00rootroot00000000000000 jQuery UI Autocomplete - Remote datasource
Result:

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are bird names, displayed when at least two characters are entered into the field.

The datasource is a server-side script which returns JSON data, specified via a simple URL for the source-option. In addition, the minLength-option is set to 2 to avoid queries that would return too many results and the select-event is used to display some feedback.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/search.php000066400000000000000000000561031231750357400256070ustar00rootroot00000000000000"Botaurus stellaris", "Little Grebe"=>"Tachybaptus ruficollis", "Black-necked Grebe"=>"Podiceps nigricollis", "Little Bittern"=>"Ixobrychus minutus", "Black-crowned Night Heron"=>"Nycticorax nycticorax", "Purple Heron"=>"Ardea purpurea", "White Stork"=>"Ciconia ciconia", "Spoonbill"=>"Platalea leucorodia", "Red-crested Pochard"=>"Netta rufina", "Common Eider"=>"Somateria mollissima", "Red Kite"=>"Milvus milvus", "Hen Harrier"=>"Circus cyaneus", "Montagu`s Harrier"=>"Circus pygargus", "Black Grouse"=>"Tetrao tetrix", "Grey Partridge"=>"Perdix perdix", "Spotted Crake"=>"Porzana porzana", "Corncrake"=>"Crex crex", "Common Crane"=>"Grus grus", "Avocet"=>"Recurvirostra avosetta", "Stone Curlew"=>"Burhinus oedicnemus", "Common Ringed Plover"=>"Charadrius hiaticula", "Kentish Plover"=>"Charadrius alexandrinus", "Ruff"=>"Philomachus pugnax", "Common Snipe"=>"Gallinago gallinago", "Black-tailed Godwit"=>"Limosa limosa", "Common Redshank"=>"Tringa totanus", "Sandwich Tern"=>"Sterna sandvicensis", "Common Tern"=>"Sterna hirundo", "Arctic Tern"=>"Sterna paradisaea", "Little Tern"=>"Sternula albifrons", "Black Tern"=>"Chlidonias niger", "Barn Owl"=>"Tyto alba", "Little Owl"=>"Athene noctua", "Short-eared Owl"=>"Asio flammeus", "European Nightjar"=>"Caprimulgus europaeus", "Common Kingfisher"=>"Alcedo atthis", "Eurasian Hoopoe"=>"Upupa epops", "Eurasian Wryneck"=>"Jynx torquilla", "European Green Woodpecker"=>"Picus viridis", "Crested Lark"=>"Galerida cristata", "White-headed Duck"=>"Oxyura leucocephala", "Pale-bellied Brent Goose"=>"Branta hrota", "Tawny Pipit"=>"Anthus campestris", "Whinchat"=>"Saxicola rubetra", "European Stonechat"=>"Saxicola rubicola", "Northern Wheatear"=>"Oenanthe oenanthe", "Savi`s Warbler"=>"Locustella luscinioides", "Sedge Warbler"=>"Acrocephalus schoenobaenus", "Great Reed Warbler"=>"Acrocephalus arundinaceus", "Bearded Reedling"=>"Panurus biarmicus", "Red-backed Shrike"=>"Lanius collurio", "Great Grey Shrike"=>"Lanius excubitor", "Woodchat Shrike"=>"Lanius senator", "Common Raven"=>"Corvus corax", "Yellowhammer"=>"Emberiza citrinella", "Ortolan Bunting"=>"Emberiza hortulana", "Corn Bunting"=>"Emberiza calandra", "Great Cormorant"=>"Phalacrocorax carbo", "Hawfinch"=>"Coccothraustes coccothraustes", "Common Shelduck"=>"Tadorna tadorna", "Bluethroat"=>"Luscinia svecica", "Grey Heron"=>"Ardea cinerea", "Barn Swallow"=>"Hirundo rustica", "Hooded Crow"=>"Corvus cornix", "Dunlin"=>"Calidris alpina", "Eurasian Pied Flycatcher"=>"Ficedula hypoleuca", "Eurasian Nuthatch"=>"Sitta europaea", "Short-toed Tree Creeper"=>"Certhia brachydactyla", "Wood Lark"=>"Lullula arborea", "Tree Pipit"=>"Anthus trivialis", "Eurasian Hobby"=>"Falco subbuteo", "Marsh Warbler"=>"Acrocephalus palustris", "Wood Sandpiper"=>"Tringa glareola", "Tawny Owl"=>"Strix aluco", "Lesser Whitethroat"=>"Sylvia curruca", "Barnacle Goose"=>"Branta leucopsis", "Common Goldeneye"=>"Bucephala clangula", "Western Marsh Harrier"=>"Circus aeruginosus", "Common Buzzard"=>"Buteo buteo", "Sanderling"=>"Calidris alba", "Little Gull"=>"Larus minutus", "Eurasian Magpie"=>"Pica pica", "Willow Warbler"=>"Phylloscopus trochilus", "Wood Warbler"=>"Phylloscopus sibilatrix", "Great Crested Grebe"=>"Podiceps cristatus", "Eurasian Jay"=>"Garrulus glandarius", "Common Redstart"=>"Phoenicurus phoenicurus", "Blue-headed Wagtail"=>"Motacilla flava", "Common Swift"=>"Apus apus", "Marsh Tit"=>"Poecile palustris", "Goldcrest"=>"Regulus regulus", "European Golden Plover"=>"Pluvialis apricaria", "Eurasian Bullfinch"=>"Pyrrhula pyrrhula", "Common Whitethroat"=>"Sylvia communis", "Meadow Pipit"=>"Anthus pratensis", "Greylag Goose"=>"Anser anser", "Spotted Flycatcher"=>"Muscicapa striata", "European Greenfinch"=>"Carduelis chloris", "Common Greenshank"=>"Tringa nebularia", "Great Spotted Woodpecker"=>"Dendrocopos major", "Greater Canada Goose"=>"Branta canadensis", "Mistle Thrush"=>"Turdus viscivorus", "Great Black-backed Gull"=>"Larus marinus", "Goosander"=>"Mergus merganser", "Great Egret"=>"Casmerodius albus", "Northern Goshawk"=>"Accipiter gentilis", "Dunnock"=>"Prunella modularis", "Stock Dove"=>"Columba oenas", "Common Wood Pigeon"=>"Columba palumbus", "Eurasian Woodcock"=>"Scolopax rusticola", "House Sparrow"=>"Passer domesticus", "Common House Martin"=>"Delichon urbicum", "Red Knot"=>"Calidris canutus", "Western Jackdaw"=>"Corvus monedula", "Brambling"=>"Fringilla montifringilla", "Northern Lapwing"=>"Vanellus vanellus", "European Reed Warbler"=>"Acrocephalus scirpaceus", "Lesser Black-backed Gull"=>"Larus fuscus", "Little Egret"=>"Egretta garzetta", "Little Stint"=>"Calidris minuta", "Common Linnet"=>"Carduelis cannabina", "Mute Swan"=>"Cygnus olor", "Common Cuckoo"=>"Cuculus canorus", "Black-headed Gull"=>"Larus ridibundus", "Greater White-fronted Goose"=>"Anser albifrons", "Great Tit"=>"Parus major", "Redwing"=>"Turdus iliacus", "Gadwall"=>"Anas strepera", "Fieldfare"=>"Turdus pilaris", "Tufted Duck"=>"Aythya fuligula", "Crested Tit"=>"Lophophanes cristatus", "Willow Tit"=>"Poecile montanus", "Eurasian Coot"=>"Fulica atra", "Common Blackbird"=>"Turdus merula", "Smew"=>"Mergus albellus", "Common Sandpiper"=>"Actitis hypoleucos", "Sand Martin"=>"Riparia riparia", "Purple Sandpiper"=>"Calidris maritima", "Northern Pintail"=>"Anas acuta", "Blue Tit"=>"Cyanistes caeruleus", "European Goldfinch"=>"Carduelis carduelis", "Eurasian Whimbrel"=>"Numenius phaeopus", "Common Reed Bunting"=>"Emberiza schoeniclus", "Eurasian Tree Sparrow"=>"Passer montanus", "Rook"=>"Corvus frugilegus", "European Robin"=>"Erithacus rubecula", "Bar-tailed Godwit"=>"Limosa lapponica", "Dark-bellied Brent Goose"=>"Branta bernicla", "Eurasian Oystercatcher"=>"Haematopus ostralegus", "Eurasian Siskin"=>"Carduelis spinus", "Northern Shoveler"=>"Anas clypeata", "Eurasian Wigeon"=>"Anas penelope", "Eurasian Sparrow Hawk"=>"Accipiter nisus", "Icterine Warbler"=>"Hippolais icterina", "Common Starling"=>"Sturnus vulgaris", "Long-tailed Tit"=>"Aegithalos caudatus", "Ruddy Turnstone"=>"Arenaria interpres", "Mew Gull"=>"Larus canus", "Common Pochard"=>"Aythya ferina", "Common Chiffchaff"=>"Phylloscopus collybita", "Greater Scaup"=>"Aythya marila", "Common Kestrel"=>"Falco tinnunculus", "Garden Warbler"=>"Sylvia borin", "Eurasian Collared Dove"=>"Streptopelia decaocto", "Eurasian Skylark"=>"Alauda arvensis", "Common Chaffinch"=>"Fringilla coelebs", "Common Moorhen"=>"Gallinula chloropus", "Water Pipit"=>"Anthus spinoletta", "Mallard"=>"Anas platyrhynchos", "Winter Wren"=>"Troglodytes troglodytes", "Common Teal"=>"Anas crecca", "Green Sandpiper"=>"Tringa ochropus", "White Wagtail"=>"Motacilla alba", "Eurasian Curlew"=>"Numenius arquata", "Song Thrush"=>"Turdus philomelos", "European Herring Gull"=>"Larus argentatus", "Grey Plover"=>"Pluvialis squatarola", "Carrion Crow"=>"Corvus corone", "Coal Tit"=>"Periparus ater", "Spotted Redshank"=>"Tringa erythropus", "Blackcap"=>"Sylvia atricapilla", "Egyptian Vulture"=>"Neophron percnopterus", "Razorbill"=>"Alca torda", "Alpine Swift"=>"Apus melba", "Long-legged Buzzard"=>"Buteo rufinus", "Audouin`s Gull"=>"Larus audouinii", "Balearic Shearwater"=>"Puffinus mauretanicus", "Upland Sandpiper"=>"Bartramia longicauda", "Greater Spotted Eagle"=>"Aquila clanga", "Ring Ouzel"=>"Turdus torquatus", "Yellow-browed Warbler"=>"Phylloscopus inornatus", "Blue Rock Thrush"=>"Monticola solitarius", "Buff-breasted Sandpiper"=>"Tryngites subruficollis", "Jack Snipe"=>"Lymnocryptes minimus", "White-rumped Sandpiper"=>"Calidris fuscicollis", "Ruddy Shelduck"=>"Tadorna ferruginea", "Cetti's Warbler"=>"Cettia cetti", "Citrine Wagtail"=>"Motacilla citreola", "Roseate Tern"=>"Sterna dougallii", "Black-legged Kittiwake"=>"Rissa tridactyla", "Pygmy Cormorant"=>"Phalacrocorax pygmeus", "Booted Eagle"=>"Aquila pennata", "Lesser White-fronted Goose"=>"Anser erythropus", "Little Bunting"=>"Emberiza pusilla", "Eleonora's Falcon"=>"Falco eleonorae", "European Serin"=>"Serinus serinus", "Twite"=>"Carduelis flavirostris", "Yellow-legged Gull"=>"Larus michahellis", "Gyr Falcon"=>"Falco rusticolus", "Greenish Warbler"=>"Phylloscopus trochiloides", "Red-necked Phalarope"=>"Phalaropus lobatus", "Mealy Redpoll"=>"Carduelis flammea", "Glaucous Gull"=>"Larus hyperboreus", "Great Skua"=>"Stercorarius skua", "Great Bustard"=>"Otis tarda", "Velvet Scoter"=>"Melanitta fusca", "Pine Grosbeak"=>"Pinicola enucleator", "House Crow"=>"Corvus splendens", "Hume`s Leaf Warbler"=>"Phylloscopus humei", "Great Northern Loon"=>"Gavia immer", "Long-tailed Duck"=>"Clangula hyemalis", "Lapland Longspur"=>"Calcarius lapponicus", "Northern Gannet"=>"Morus bassanus", "Eastern Imperial Eagle"=>"Aquila heliaca", "Little Auk"=>"Alle alle", "Lesser Spotted Woodpecker"=>"Dendrocopos minor", "Iceland Gull"=>"Larus glaucoides", "Parasitic Jaeger"=>"Stercorarius parasiticus", "Bewick`s Swan"=>"Cygnus bewickii", "Little Bustard"=>"Tetrax tetrax", "Little Crake"=>"Porzana parva", "Baillon`s Crake"=>"Porzana pusilla", "Long-tailed Jaeger"=>"Stercorarius longicaudus", "King Eider"=>"Somateria spectabilis", "Greater Short-toed Lark"=>"Calandrella brachydactyla", "Houbara Bustard"=>"Chlamydotis undulata", "Curlew Sandpiper"=>"Calidris ferruginea", "Common Crossbill"=>"Loxia curvirostra", "European Shag"=>"Phalacrocorax aristotelis", "Horned Grebe"=>"Podiceps auritus", "Common Quail"=>"Coturnix coturnix", "Bearded Vulture"=>"Gypaetus barbatus", "Lanner Falcon"=>"Falco biarmicus", "Middle Spotted Woodpecker"=>"Dendrocopos medius", "Pomarine Jaeger"=>"Stercorarius pomarinus", "Red-breasted Merganser"=>"Mergus serrator", "Eurasian Black Vulture"=>"Aegypius monachus", "Eurasian Dotterel"=>"Charadrius morinellus", "Common Nightingale"=>"Luscinia megarhynchos", "Northern willow warbler"=>"Phylloscopus trochilus acredula", "Manx Shearwater"=>"Puffinus puffinus", "Northern Fulmar"=>"Fulmarus glacialis", "Eurasian Eagle Owl"=>"Bubo bubo", "Orphean Warbler"=>"Sylvia hortensis", "Melodious Warbler"=>"Hippolais polyglotta", "Pallas's Leaf Warbler"=>"Phylloscopus proregulus", "Atlantic Puffin"=>"Fratercula arctica", "Black-throated Loon"=>"Gavia arctica", "Bohemian Waxwing"=>"Bombycilla garrulus", "Marsh Sandpiper"=>"Tringa stagnatilis", "Great Snipe"=>"Gallinago media", "Squacco Heron"=>"Ardeola ralloides", "Long-eared Owl"=>"Asio otus", "Caspian Tern"=>"Hydroprogne caspia", "Red-breasted Goose"=>"Branta ruficollis", "Red-throated Loon"=>"Gavia stellata", "Common Rosefinch"=>"Carpodacus erythrinus", "Red-footed Falcon"=>"Falco vespertinus", "Ross's Goose"=>"Anser rossii", "Red Phalarope"=>"Phalaropus fulicarius", "Pied Wagtail"=>"Motacilla yarrellii", "Rose-coloured Starling"=>"Sturnus roseus", "Rough-legged Buzzard"=>"Buteo lagopus", "Saker Falcon"=>"Falco cherrug", "European Roller"=>"Coracias garrulus", "Short-toed Eagle"=>"Circaetus gallicus", "Peregrine Falcon"=>"Falco peregrinus", "Merlin"=>"Falco columbarius", "Snow Goose"=>"Anser caerulescens", "Snowy Owl"=>"Bubo scandiacus", "Snow Bunting"=>"Plectrophenax nivalis", "Common Grasshopper Warbler"=>"Locustella naevia", "Golden Eagle"=>"Aquila chrysaetos", "Black-winged Stilt"=>"Himantopus himantopus", "Steppe Eagle"=>"Aquila nipalensis", "Pallid Harrier"=>"Circus macrourus", "European Storm-petrel"=>"Hydrobates pelagicus", "Horned Lark"=>"Eremophila alpestris", "Eurasian Treecreeper"=>"Certhia familiaris", "Taiga Bean Goose"=>"Anser fabalis", "Temminck`s Stint"=>"Calidris temminckii", "Terek Sandpiper"=>"Xenus cinereus", "Tundra Bean Goose"=>"Anser serrirostris", "European Turtle Dove"=>"Streptopelia turtur", "Leach`s Storm-petrel"=>"Oceanodroma leucorhoa", "Eurasian Griffon Vulture"=>"Gyps fulvus", "Paddyfield Warbler"=>"Acrocephalus agricola", "Osprey"=>"Pandion haliaetus", "Firecrest"=>"Regulus ignicapilla", "Water Rail"=>"Rallus aquaticus", "European Honey Buzzard"=>"Pernis apivorus", "Eurasian Golden Oriole"=>"Oriolus oriolus", "Whooper Swan"=>"Cygnus cygnus", "Two-barred Crossbill"=>"Loxia leucoptera", "White-tailed Eagle"=>"Haliaeetus albicilla", "Atlantic Murre"=>"Uria aalge", "Garganey"=>"Anas querquedula", "Black Redstart"=>"Phoenicurus ochruros", "Common Scoter"=>"Melanitta nigra", "Rock Pipit"=>"Anthus petrosus", "Lesser Spotted Eagle"=>"Aquila pomarina", "Cattle Egret"=>"Bubulcus ibis", "White-winged Black Tern"=>"Chlidonias leucopterus", "Black Stork"=>"Ciconia nigra", "Mediterranean Gull"=>"Larus melanocephalus", "Black Kite"=>"Milvus migrans", "Yellow Wagtail"=>"Motacilla flavissima", "Red-necked Grebe"=>"Podiceps grisegena", "Gull-billed Tern"=>"Gelochelidon nilotica", "Pectoral Sandpiper"=>"Calidris melanotos", "Barred Warbler"=>"Sylvia nisoria", "Red-throated Pipit"=>"Anthus cervinus", "Grey Wagtail"=>"Motacilla cinerea", "Richard`s Pipit"=>"Anthus richardi", "Black Woodpecker"=>"Dryocopus martius", "Little Ringed Plover"=>"Charadrius dubius", "Whiskered Tern"=>"Chlidonias hybrida", "Lesser Redpoll"=>"Carduelis cabaret", "Pallas' Bunting"=>"Emberiza pallasi", "Ferruginous Duck"=>"Aythya nyroca", "Whistling Swan"=>"Cygnus columbianus", "Black Brant"=>"Branta nigricans", "Marbled Teal"=>"Marmaronetta angustirostris", "Canvasback"=>"Aythya valisineria", "Redhead"=>"Aythya americana", "Lesser Scaup"=>"Aythya affinis", "Steller`s Eider"=>"Polysticta stelleri", "Spectacled Eider"=>"Somateria fischeri", "Harlequin Duck"=>"Histronicus histrionicus", "Black Scoter"=>"Melanitta americana", "Surf Scoter"=>"Melanitta perspicillata", "Barrow`s Goldeneye"=>"Bucephala islandica", "Falcated Duck"=>"Anas falcata", "American Wigeon"=>"Anas americana", "Blue-winged Teal"=>"Anas discors", "American Black Duck"=>"Anas rubripes", "Baikal Teal"=>"Anas formosa", "Green-Winged Teal"=>"Anas carolinensis", "Hazel Grouse"=>"Bonasa bonasia", "Rock Partridge"=>"Alectoris graeca", "Red-legged Partridge"=>"Alectoris rufa", "Yellow-billed Loon"=>"Gavia adamsii", "Cory`s Shearwater"=>"Calonectris borealis", "Madeiran Storm-Petrel"=>"Oceanodroma castro", "Great White Pelican"=>"Pelecanus onocrotalus", "Dalmatian Pelican"=>"Pelecanus crispus", "American Bittern"=>"Botaurus lentiginosus", "Glossy Ibis"=>"Plegadis falcinellus", "Spanish Imperial Eagle"=>"Aquila adalberti", "Lesser Kestrel"=>"Falco naumanni", "Houbara Bustard"=>"Chlamydotis undulata", "Crab-Plover"=>"Dromas ardeola", "Cream-coloured Courser"=>"Cursorius cursor", "Collared Pratincole"=>"Glareola pratincola", "Black-winged Pratincole"=>"Glareola nordmanni", "Killdeer"=>"Charadrius vociferus", "Lesser Sand Plover"=>"Charadrius mongolus", "Greater Sand Plover"=>"Charadrius leschenaultii", "Caspian Plover"=>"Charadrius asiaticus", "American Golden Plover"=>"Pluvialis dominica", "Pacific Golden Plover"=>"Pluvialis fulva", "Sharp-tailed Sandpiper"=>"Calidris acuminata", "Broad-billed Sandpiper"=>"Limicola falcinellus", "Spoon-Billed Sandpiper"=>"Eurynorhynchus pygmaeus", "Short-Billed Dowitcher"=>"Limnodromus griseus", "Long-billed Dowitcher"=>"Limnodromus scolopaceus", "Hudsonian Godwit"=>"Limosa haemastica", "Little Curlew"=>"Numenius minutus", "Lesser Yellowlegs"=>"Tringa flavipes", "Wilson`s Phalarope"=>"Phalaropus tricolor", "Pallas`s Gull"=>"Larus ichthyaetus", "Laughing Gull"=>"Larus atricilla", "Franklin`s Gull"=>"Larus pipixcan", "Bonaparte`s Gull"=>"Larus philadelphia", "Ring-billed Gull"=>"Larus delawarensis", "American Herring Gull"=>"Larus smithsonianus", "Caspian Gull"=>"Larus cachinnans", "Ivory Gull"=>"Pagophila eburnea", "Royal Tern"=>"Sterna maxima", "Brünnich`s Murre"=>"Uria lomvia", "Crested Auklet"=>"Aethia cristatella", "Parakeet Auklet"=>"Cyclorrhynchus psittacula", "Tufted Puffin"=>"Lunda cirrhata", "Laughing Dove"=>"Streptopelia senegalensis", "Great Spotted Cuckoo"=>"Clamator glandarius", "Great Grey Owl"=>"Strix nebulosa", "Tengmalm`s Owl"=>"Aegolius funereus", "Red-Necked Nightjar"=>"Caprimulgus ruficollis", "Chimney Swift"=>"Chaetura pelagica", "Green Bea-Eater"=>"Merops orientalis", "Grey-headed Woodpecker"=>"Picus canus", "Lesser Short-Toed Lark"=>"Calandrella rufescens", "Eurasian Crag Martin"=>"Hirundo rupestris", "Red-rumped Swallow"=>"Cecropis daurica", "Blyth`s Pipit"=>"Anthus godlewskii", "Pechora Pipit"=>"Anthus gustavi", "Grey-headed Wagtail"=>"Motacilla thunbergi", "Yellow-Headed Wagtail"=>"Motacilla lutea", "White-throated Dipper"=>"Cinclus cinclus", "Rufous-Tailed Scrub Robin"=>"Cercotrichas galactotes", "Thrush Nightingale"=>"Luscinia luscinia", "White-throated Robin"=>"Irania gutturalis", "Caspian Stonechat"=>"Saxicola maura variegata", "Western Black-eared Wheatear"=>"Oenanthe hispanica", "Rufous-tailed Rock Thrush"=>"Monticola saxatilis", "Red-throated Thrush/Black-throated"=>"Turdus ruficollis", "American Robin"=>"Turdus migratorius", "Zitting Cisticola"=>"Cisticola juncidis", "Lanceolated Warbler"=>"Locustella lanceolata", "River Warbler"=>"Locustella fluviatilis", "Blyth`s Reed Warbler"=>"Acrocephalus dumetorum", "Caspian Reed Warbler"=>"Acrocephalus fuscus", "Aquatic Warbler"=>"Acrocephalus paludicola", "Booted Warbler"=>"Acrocephalus caligatus", "Marmora's Warbler"=>"Sylvia sarda", "Dartford Warbler"=>"Sylvia undata", "Subalpine Warbler"=>"Sylvia cantillans", "Ménétries's Warbler"=>"Sylvia mystacea", "Rüppel's Warbler"=>"Sylvia rueppelli", "Asian Desert Warbler"=>"Sylvia nana", "Western Orphean Warbler"=>"Sylvia hortensis hortensis", "Arctic Warbler"=>"Phylloscopus borealis", "Radde`s Warbler"=>"Phylloscopus schwarzi", "Western Bonelli`s Warbler"=>"Phylloscopus bonelli", "Red-breasted Flycatcher"=>"Ficedula parva", "Eurasian Penduline Tit"=>"Remiz pendulinus", "Daurian Shrike"=>"Lanius isabellinus", "Long-Tailed Shrike"=>"Lanius schach", "Lesser Grey Shrike"=>"Lanius minor", "Southern Grey Shrike"=>"Lanius meridionalis", "Masked Shrike"=>"Lanius nubicus", "Spotted Nutcracker"=>"Nucifraga caryocatactes", "Daurian Jackdaw"=>"Corvus dauuricus", "Purple-Backed Starling"=>"Sturnus sturninus", "Red-Fronted Serin"=>"Serinus pusillus", "Arctic Redpoll"=>"Carduelis hornemanni", "Scottish Crossbill"=>"Loxia scotica", "Parrot Crossbill"=>"Loxia pytyopsittacus", "Black-faced Bunting"=>"Emberiza spodocephala", "Pink-footed Goose"=>"Anser brachyrhynchus", "Black-winged Kite"=>"Elanus caeruleus", "European Bee-eater"=>"Merops apiaster", "Sabine`s Gull"=>"Larus sabini", "Sooty Shearwater"=>"Puffinus griseus", "Lesser Canada Goose"=>"Branta hutchinsii", "Ring-necked Duck"=>"Aythya collaris", "Greater Flamingo"=>"Phoenicopterus roseus", "Iberian Chiffchaff"=>"Phylloscopus ibericus", "Ashy-headed Wagtail"=>"Motacilla cinereocapilla", "Stilt Sandpiper"=>"Calidris himantopus", "Siberian Stonechat"=>"Saxicola maurus", "Greater Yellowlegs"=>"Tringa melanoleuca", "Forster`s Tern"=>"Sterna forsteri", "Dusky Warbler"=>"Phylloscopus fuscatus", "Cirl Bunting"=>"Emberiza cirlus", "Olive-backed Pipit"=>"Anthus hodgsoni", "Sociable Lapwing"=>"Vanellus gregarius", "Spotted Sandpiper"=>"Actitis macularius", "Baird`s Sandpiper"=>"Calidris bairdii", "Rustic Bunting"=>"Emberiza rustica", "Yellow-browed Bunting"=>"Emberiza chrysophrys", "Great Shearwater"=>"Puffinus gravis", "Bonelli`s Eagle"=>"Aquila fasciata", "Calandra Lark"=>"Melanocorypha calandra", "Sardinian Warbler"=>"Sylvia melanocephala", "Ross's Gull"=>"Larus roseus", "Yellow-Breasted Bunting"=>"Emberiza aureola", "Pine Bunting"=>"Emberiza leucocephalos", "Black Guillemot"=>"Cepphus grylle", "Pied-billed Grebe"=>"Podilymbus podiceps", "Soft-plumaged Petrel"=>"Pterodroma mollis", "Bulwer's Petrel"=>"Bulweria bulwerii", "White-Faced Storm-Petrel"=>"Pelagodroma marina", "Pallas’s Fish Eagle"=>"Haliaeetus leucoryphus", "Sandhill Crane"=>"Grus canadensis", "Macqueen’s Bustard"=>"Chlamydotis macqueenii", "White-tailed Lapwing"=>"Vanellus leucurus", "Great Knot"=>"Calidris tenuirostris", "Semipalmated Sandpiper"=>"Calidris pusilla", "Red-necked Stint"=>"Calidris ruficollis", "Slender-billed Curlew"=>"Numenius tenuirostris", "Bridled Tern"=>"Onychoprion anaethetus", "Pallas’s Sandgrouse"=>"Syrrhaptes paradoxus", "European Scops Owl"=>"Otus scops", "Northern Hawk Owl"=>"Surnia ulula", "White-Throated Needletail"=>"Hirundapus caudacutus", "Belted Kingfisher"=>"Ceryle alcyon", "Blue-cheeked Bee-eater"=>"Merops persicus", "Black-headed Wagtail"=>"Motacilla feldegg", "Northern Mockingbird"=>"Mimus polyglottos", "Alpine Accentor"=>"Prunella collaris", "Red-flanked Bluetail"=>"Tarsiger cyanurus", "Isabelline Wheatear"=>"Oenanthe isabellina", "Pied Wheatear"=>"Oenanthe pleschanka", "Eastern Black-eared Wheatear"=>"Oenanthe melanoleuca", "Desert Wheatear"=>"Oenanthe deserti", "White`s Thrush"=>"Zoothera aurea", "Siberian Thrush"=>"Zoothera sibirica", "Eyebrowed Thrush"=>"Turdus obscurus", "Dusky Thrush"=>"Turdus eunomus", "Black-throated Thrush"=>"Turdus atrogularis", "Pallas`s Grasshopper Warbler"=>"Locustella certhiola", "Spectacled Warbler"=>"Sylvia conspicillata", "Two-barred Warbler"=>"Phylloscopus plumbeitarsus", "Eastern Bonelli’s Warbler"=>"Phylloscopus orientalis", "Collared Flycatcher"=>"Ficedula albicollis", "Wallcreeper"=>"Tichodroma muraria", "Turkestan Shrike"=>"Lanius phoenicuroides", "Steppe Grey Shrike"=>"Lanius pallidirostris", "Spanish Sparrow"=>"Passer hispaniolensis", "Red-eyed Vireo"=>"Vireo olivaceus", "Myrtle Warbler"=>"Dendroica coronata", "White-crowned Sparrow"=>"Zonotrichia leucophrys", "White-throated Sparrow"=>"Zonotrichia albicollis", "Cretzschmar`s Bunting"=>"Emberiza caesia", "Chestnut Bunting"=>"Emberiza rutila", "Red-headed Bunting"=>"Emberiza bruniceps", "Black-headed Bunting"=>"Emberiza melanocephala", "Indigo Bunting"=>"Passerina cyanea", "Balearic Woodchat Shrike"=>"Lanius senator badius", "Demoiselle Crane"=>"Grus virgo", "Chough"=>"Pyrrhocorax pyrrhocorax", "Red-Billed Chough"=>"Pyrrhocorax graculus", "Elegant Tern"=>"Sterna elegans", "Chukar"=>"Alectoris chukar", "Yellow-Billed Cuckoo"=>"Coccyzus americanus", "American Sandwich Tern"=>"Sterna sandvicensis acuflavida", "Olive-Tree Warbler"=>"Hippolais olivetorum", "Eastern Olivaceous Warbler"=>"Acrocephalus pallidus", "Indian Cormorant"=>"Phalacrocorax fuscicollis", "Spur-Winged Lapwing"=>"Vanellus spinosus", "Yelkouan Shearwater"=>"Puffinus yelkouan", "Trumpeter Finch"=>"Bucanetes githagineus", "Red Grouse"=>"Lagopus scoticus", "Rock Ptarmigan"=>"Lagopus mutus", "Long-Tailed Cormorant"=>"Phalacrocorax africanus", "Double-crested Cormorant"=>"Phalacrocorax auritus", "Magnificent Frigatebird"=>"Fregata magnificens", "Naumann's Thrush"=>"Turdus naumanni", "Oriental Pratincole"=>"Glareola maldivarum", "Bufflehead"=>"Bucephala albeola", "Snowfinch"=>"Montifrigilla nivalis", "Ural owl"=>"Strix uralensis", "Spanish Wagtail"=>"Motacilla iberiae", "Song Sparrow"=>"Melospiza melodia", "Rock Bunting"=>"Emberiza cia", "Siberian Rubythroat"=>"Luscinia calliope", "Pallid Swift"=>"Apus pallidus", "Eurasian Pygmy Owl"=>"Glaucidium passerinum", "Madeira Little Shearwater"=>"Puffinus baroli", "House Finch"=>"Carpodacus mexicanus", "Green Heron"=>"Butorides virescens", "Solitary Sandpiper"=>"Tringa solitaria", "Heuglin's Gull"=>"Larus heuglini" ); $result = array(); foreach ($items as $key=>$value) { if (strpos(strtolower($key), $q) !== false) { array_push($result, array("id"=>$value, "label"=>$key, "value" => strip_tags($key))); } if (count($result) > 11) break; } // json_encode is available in PHP 5.2 and above, or you can install a PECL module in earlier versions echo json_encode($result); ?>ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/autocomplete/xml.html000066400000000000000000000041371231750357400253170ustar00rootroot00000000000000 jQuery UI Autocomplete - XML data parsed once
Result:

This demo shows how to retrieve some XML data, parse it using jQuery's methods, then provide it to the autocomplete as the datasource.

This should also serve as a reference on how to parse a remote XML datasource - the parsing would just happen for each request within the source-callback.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/000077500000000000000000000000001231750357400224365ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/checkbox.html000066400000000000000000000023161231750357400251140ustar00rootroot00000000000000 jQuery UI Button - Checkboxes

A checkbox is styled as a toggle button with the button widget. The label element associated with the checkbox is used for the button text.

This demo also demonstrates three checkboxes styled as a button set by calling .buttonset() on a common container.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/default.html000066400000000000000000000016001231750357400247450ustar00rootroot00000000000000 jQuery UI Button - Default functionality An anchor

Examples of the markup that can be used for buttons: A button element, an input of type submit and an anchor.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/icons.html000066400000000000000000000022241231750357400244370ustar00rootroot00000000000000 jQuery UI Button - Icons

Some buttons with various combinations of text and icons.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/index.html000066400000000000000000000006651231750357400244420ustar00rootroot00000000000000 jQuery UI Button Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/radio.html000066400000000000000000000016701231750357400244260ustar00rootroot00000000000000 jQuery UI Button - Radios

A set of three radio buttons transformed into a button set.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/splitbutton.html000066400000000000000000000031761231750357400257220ustar00rootroot00000000000000 jQuery UI Button - Split button

An example of a split button built with two buttons: A plain button with just text, one with only a primary icon and no text. Both are grouped together in a set.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/button/toolbar.html000066400000000000000000000051501231750357400247670ustar00rootroot00000000000000 jQuery UI Button - Toolbar

A mediaplayer toolbar. Take a look at the underlying markup: A few button elements, an input of type checkbox for the Shuffle button, and three inputs of type radio for the Repeat options.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/000077500000000000000000000000001231750357400232365ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/alt-field.html000066400000000000000000000020621231750357400257650ustar00rootroot00000000000000 jQuery UI Datepicker - Populate alternate field

Date:  

Populate an alternate field with its own date format whenever a date is selected using the altField and altFormat options. This feature could be used to present a human-friendly date for user selection, while passing a more computer-friendly date through for further processing.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/animation.html000066400000000000000000000036171231750357400261120ustar00rootroot00000000000000 jQuery UI Datepicker - Animations

Date:

Animations:

Use different animations when opening or closing the datepicker. Choose an animation from the dropdown, then click on the input to see its effect. You can use one of the three standard animations or any of the UI Effects.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/buttonbar.html000066400000000000000000000017001231750357400261220ustar00rootroot00000000000000 jQuery UI Datepicker - Display button bar

Date:

Display a button for selecting Today's date and a Done button for closing the calendar with the boolean showButtonPanel option. Each button is enabled by default when the bar is displayed, but can be turned off with additional options. Button text is customizable.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/date-formats.html000066400000000000000000000025301231750357400265120ustar00rootroot00000000000000 jQuery UI Datepicker - Format date

Date:

Format options:

Display date feedback in a variety of ways. Choose a date format from the dropdown, then click on the input and select a date to see it in that format.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/date-range.html000066400000000000000000000022121231750357400261300ustar00rootroot00000000000000 jQuery UI Datepicker - Select a Date Range

Select the date range to search for.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/default.html000066400000000000000000000017001231750357400255460ustar00rootroot00000000000000 jQuery UI Datepicker - Default functionality

Date:

The datepicker is tied to a standard form input field. Focus on the input (click, or use the tab key) to open an interactive calendar in a small overlay. Choose a date, click elsewhere on the page (blur the input), or hit the Esc key to close. If a date is chosen, feedback is shown as the input's value.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/dropdown-month-year.html000066400000000000000000000016161231750357400300450ustar00rootroot00000000000000 jQuery UI Datepicker - Display month & year menus

Date:

Show month and year dropdowns in place of the static month/year header to facilitate navigation through large timeframes. Add the boolean changeMonth and changeYear options.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/icon-trigger.html000066400000000000000000000015561231750357400265240ustar00rootroot00000000000000 jQuery UI Datepicker - Icon trigger

Date:

Click the icon next to the input field to show the datepicker. Set the datepicker to open on focus (default behavior), on icon click, or both.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/images/000077500000000000000000000000001231750357400245035ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/images/calendar.gif000066400000000000000000000004151231750357400267430ustar00rootroot00000000000000GIF89a9qZ1ise^c\mesl^Yzs[W}wSQ!,e5<Ҿn1q]UEQR=d[nI)*u/8p8kH ]H @w@b~Chff]nprxX^`Gi`G !;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/index.html000066400000000000000000000017431231750357400252400ustar00rootroot00000000000000 jQuery UI Datepicker Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/inline.html000066400000000000000000000013661231750357400254100ustar00rootroot00000000000000 jQuery UI Datepicker - Display inline Date:

Display the datepicker embedded in the page instead of in an overlay. Simply call .datepicker() on a div instead of an input.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/jquery.ui.datepicker-ar.js000066400000000000000000000024211231750357400302400ustar00rootroot00000000000000/* Arabic Translation for jQuery UI date picker plugin. */ /* Khaled Alhourani -- me@khaledalhourani.com */ /* NOTE: monthNames are the original months names and they are the Arabic names, not the new months name فبراير - يناير and there isn't any Arabic roots for these months */ jQuery(function($){ $.datepicker.regional['ar'] = { closeText: 'إغلاق', prevText: '<السابق', nextText: 'التالي>', currentText: 'اليوم', monthNames: ['كانون الثاني', 'شباط', 'آذار', 'نيسان', 'مايو', 'حزيران', 'تموز', 'آب', 'أيلول', 'تشرين الأول', 'تشرين الثاني', 'كانون الأول'], monthNamesShort: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], dayNames: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], dayNamesShort: ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'], dayNamesMin: ['ح', 'ن', 'ث', 'ر', 'خ', 'ج', 'س'], weekHeader: 'أسبوع', dateFormat: 'dd/mm/yy', firstDay: 6, isRTL: true, showMonthAfterYear: false, yearSuffix: ''}; $.datepicker.setDefaults($.datepicker.regional['ar']); }); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/jquery.ui.datepicker-fr.js000066400000000000000000000017601231750357400302520ustar00rootroot00000000000000/* French initialisation for the jQuery UI date picker plugin. */ /* Written by Keith Wood (kbwood{at}iinet.com.au), Stéphane Nahmani (sholby@sholby.net), Stéphane Raimbault */ jQuery(function($){ $.datepicker.regional['fr'] = { closeText: 'Fermer', prevText: 'Précédent', nextText: 'Suivant', currentText: 'Aujourd\'hui', monthNames: ['Janvier','Février','Mars','Avril','Mai','Juin', 'Juillet','Août','Septembre','Octobre','Novembre','Décembre'], monthNamesShort: ['Janv.','Févr.','Mars','Avril','Mai','Juin', 'Juil.','Août','Sept.','Oct.','Nov.','Déc.'], dayNames: ['Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi'], dayNamesShort: ['Dim.','Lun.','Mar.','Mer.','Jeu.','Ven.','Sam.'], dayNamesMin: ['D','L','M','M','J','V','S'], weekHeader: 'Sem.', dateFormat: 'dd/mm/yy', firstDay: 1, isRTL: false, showMonthAfterYear: false, yearSuffix: ''}; $.datepicker.setDefaults($.datepicker.regional['fr']); }); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/jquery.ui.datepicker-he.js000066400000000000000000000017701231750357400302400ustar00rootroot00000000000000/* Hebrew initialisation for the UI Datepicker extension. */ /* Written by Amir Hardon (ahardon at gmail dot com). */ jQuery(function($){ $.datepicker.regional['he'] = { closeText: 'סגור', prevText: '<הקודם', nextText: 'הבא>', currentText: 'היום', monthNames: ['ינואר','פברואר','מרץ','אפריל','מאי','יוני', 'יולי','אוגוסט','ספטמבר','אוקטובר','נובמבר','דצמבר'], monthNamesShort: ['ינו','פבר','מרץ','אפר','מאי','יוני', 'יולי','אוג','ספט','אוק','נוב','דצמ'], dayNames: ['ראשון','שני','שלישי','רביעי','חמישי','שישי','שבת'], dayNamesShort: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'], dayNamesMin: ['א\'','ב\'','ג\'','ד\'','ה\'','ו\'','שבת'], weekHeader: 'Wk', dateFormat: 'dd/mm/yy', firstDay: 0, isRTL: true, showMonthAfterYear: false, yearSuffix: ''}; $.datepicker.setDefaults($.datepicker.regional['he']); }); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/jquery.ui.datepicker-zh-TW.js000066400000000000000000000017211231750357400306110ustar00rootroot00000000000000/* Chinese initialisation for the jQuery UI date picker plugin. */ /* Written by Ressol (ressol@gmail.com). */ jQuery(function($){ $.datepicker.regional['zh-TW'] = { closeText: '關閉', prevText: '<上月', nextText: '下月>', currentText: '今天', monthNames: ['一月','二月','三月','四月','五月','六月', '七月','八月','九月','十月','十一月','十二月'], monthNamesShort: ['一月','二月','三月','四月','五月','六月', '七月','八月','九月','十月','十一月','十二月'], dayNames: ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'], dayNamesShort: ['周日','周一','周二','周三','周四','周五','周六'], dayNamesMin: ['日','一','二','三','四','五','六'], weekHeader: '周', dateFormat: 'yy/mm/dd', firstDay: 1, isRTL: false, showMonthAfterYear: true, yearSuffix: '年'}; $.datepicker.setDefaults($.datepicker.regional['zh-TW']); }); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/localization.html000066400000000000000000000031741231750357400266210ustar00rootroot00000000000000 jQuery UI Datepicker - Localize calendar

Date:  

Localize the datepicker calendar language and format (English / Western formatting is the default). The datepicker includes built-in support for languages that read right-to-left, such as Arabic and Hebrew.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/min-max.html000066400000000000000000000020171231750357400254720ustar00rootroot00000000000000 jQuery UI Datepicker - Restrict date range

Date:

Restrict the range of selectable dates with the minDate and maxDate options. Set the beginning and end dates as actual dates (new Date(2009, 1 - 1, 26)), as a numeric offset from today (-20), or as a string of periods and units ('+1M +10D'). For the last, use 'D' for days, 'W' for weeks, 'M' for months, or 'Y' for years.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/multiple-calendars.html000066400000000000000000000014711231750357400277140ustar00rootroot00000000000000 jQuery UI Datepicker - Display multiple months

Date:

Set the numberOfMonths option to an integer of 2 or more to show multiple months in a single datepicker.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/other-months.html000066400000000000000000000015171231750357400265570ustar00rootroot00000000000000 jQuery UI Datepicker - Dates in other months

Date:

The datepicker can show dates that come from other than the main month being displayed. These other dates can also be made selectable.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/datepicker/show-week.html000066400000000000000000000017331231750357400260410ustar00rootroot00000000000000 jQuery UI Datepicker - Show week of the year

Date:

The datepicker can show the week of the year. The default calculation follows the ISO 8601 definition: the week starts on Monday, the first week of the year contains the first Thursday of the year. This means that some days from one year may be placed into weeks 'belonging' to another year.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/demos.css000066400000000000000000000004371231750357400227500ustar00rootroot00000000000000body { font-size: 62.5%; font-family: "Trebuchet MS", "Arial", "Helvetica", "Verdana", "sans-serif"; } table { font-size: 1em; } .demo-description { clear: both; padding: 12px; font-size: 1.3em; line-height: 1.4em; } .ui-draggable, .ui-droppable { background-position: top; } ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/000077500000000000000000000000001231750357400223625ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/animated.html000066400000000000000000000031671231750357400250410ustar00rootroot00000000000000 jQuery UI Dialog - Animation

This is an animated dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.

Dialogs may be animated by specifying an effect for the show and/or hide properties. You must include the individual effects file for any effects you would like to use.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/default.html000066400000000000000000000024561231750357400247030ustar00rootroot00000000000000 jQuery UI Dialog - Default functionality

This is the default dialog which is useful for displaying information. The dialog window can be moved, resized and closed with the 'x' icon.

The basic dialog window is an overlay positioned within the viewport and is protected from page content (like select elements) shining through with an iframe. It has a title bar and a content area, and can be moved, resized and closed with the 'x' icon by default.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/index.html000066400000000000000000000007431231750357400243630ustar00rootroot00000000000000 jQuery UI Dialog Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/modal-confirmation.html000066400000000000000000000033411231750357400270330ustar00rootroot00000000000000 jQuery UI Dialog - Modal confirmation

These items will be permanently deleted and cannot be recovered. Are you sure?

Sed vel diam id libero rutrum convallis. Donec aliquet leo vel magna. Phasellus rhoncus faucibus ante. Etiam bibendum, enim faucibus aliquet rhoncus, arcu felis ultricies neque, sit amet auctor elit eros a lectus.

Confirm an action that may be destructive or important. Set the modal option to true, and specify primary and secondary user actions with the buttons option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/modal-form.html000066400000000000000000000135121231750357400253070ustar00rootroot00000000000000 jQuery UI Dialog - Modal form

All form fields are required.

Existing Users:

Name Email Password
John Doe john.doe@example.com johndoe1

Use a modal dialog to require that the user enter data during a multi-step process. Embed form markup in the content area, set the modal option to true, and specify primary and secondary user actions with the buttons option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/modal-message.html000066400000000000000000000033771231750357400260000ustar00rootroot00000000000000 jQuery UI Dialog - Modal message

Your files have downloaded successfully into the My Downloads folder.

Currently using 36% of your storage space.

Sed vel diam id libero rutrum convallis. Donec aliquet leo vel magna. Phasellus rhoncus faucibus ante. Etiam bibendum, enim faucibus aliquet rhoncus, arcu felis ultricies neque, sit amet auctor elit eros a lectus.

Use a modal dialog to explicitly acknowledge information or an action before continuing their work. Set the modal option to true, and specify a primary action (Ok) with the buttons option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/dialog/modal.html000066400000000000000000000026151231750357400243500ustar00rootroot00000000000000 jQuery UI Dialog - Basic modal

Adding the modal overlay screen makes the dialog look more prominent because it dims out the page content.

Sed vel diam id libero rutrum convallis. Donec aliquet leo vel magna. Phasellus rhoncus faucibus ante. Etiam bibendum, enim faucibus aliquet rhoncus, arcu felis ultricies neque, sit amet auctor elit eros a lectus.

A modal dialog prevents the user from interacting with the rest of the page until it is closed.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/000077500000000000000000000000001231750357400230335ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/constrain-movement.html000066400000000000000000000040241231750357400275510ustar00rootroot00000000000000 jQuery UI Draggable - Constrain movement

Constrain movement along an axis:

I can be dragged only vertically

I can be dragged only horizontally

Or to within another DOM element:

I'm contained within the box

I'm contained within my parent

Constrain the movement of each draggable by defining the boundaries of the draggable area. Set the axis option to limit the draggable's path to the x- or y-axis, or use the containment option to specify a parent DOM element or a jQuery selector, like 'document.'

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/cursor-style.html000066400000000000000000000033311231750357400263740ustar00rootroot00000000000000 jQuery UI Draggable - Cursor style

I will always stick to the center (relative to the mouse)

My cursor is at left -5 and top -5

My cursor position is only controlled for the 'bottom' value

Position the cursor while dragging the object. By default the cursor appears in the center of the dragged object; use the cursorAt option to specify another location relative to the draggable (specify a pixel value from the top, right, bottom, and/or left). Customize the cursor's appearance by supplying the cursor option with a valid CSS cursor value: default, move, pointer, crosshair, etc.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/default.html000066400000000000000000000017061231750357400253510ustar00rootroot00000000000000 jQuery UI Draggable - Default functionality

Drag me around

Enable draggable functionality on any DOM element. Move the draggable object by clicking on it with the mouse and dragging it anywhere within the viewport.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/delay-start.html000066400000000000000000000025341231750357400261560ustar00rootroot00000000000000 jQuery UI Draggable - Delay start

Only if you drag me by 20 pixels, the dragging will start

Regardless of the distance, you have to drag and wait for 1000ms before dragging starts

Delay the start of dragging for a number of milliseconds with the delay option; prevent dragging until the cursor is held down and dragged a specifed number of pixels with the distance option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/events.html000066400000000000000000000047561231750357400252410ustar00rootroot00000000000000 jQuery UI Draggable - Events

Drag me to trigger the chain of events.

  • "start" invoked 0x
  • "drag" invoked 0x
  • "stop" invoked 0x

Layer functionality onto the draggable using the start, drag, and stop events. Start is fired at the start of the drag; drag during the drag; and stop when dragging stops.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/handle.html000066400000000000000000000031621231750357400251560ustar00rootroot00000000000000 jQuery UI Draggable - Handles

I can be dragged only by this handle

You can drag me around…

…but you can't drag me by this handle.

Allow dragging only when the cursor is over a specific part of the draggable. Use the handle option to specify the jQuery selector of an element (or group of elements) used to drag the object.

Or prevent dragging when the cursor is over a specific element (or group of elements) within the draggable. Use the cancel option to specify a jQuery selector over which to "cancel" draggable functionality.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/index.html000066400000000000000000000013641231750357400250340ustar00rootroot00000000000000 jQuery UI Draggable Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/revert.html000066400000000000000000000021771231750357400252370ustar00rootroot00000000000000 jQuery UI Draggable - Revert position

Revert the original

Revert the helper

Return the draggable (or it's helper) to its original location when dragging stops with the boolean revert option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/scroll.html000066400000000000000000000030251231750357400252170ustar00rootroot00000000000000 jQuery UI Draggable - Auto-scroll

Scroll set to true, default settings

scrollSensitivity set to 100

scrollSpeed set to 100

Automatically scroll the document when the draggable is moved beyond the viewport. Set the scroll option to true to enable auto-scrolling, and fine-tune when scrolling is triggered and its speed with the scrollSensitivity and scrollSpeed options.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/snap-to.html000066400000000000000000000042351231750357400253060ustar00rootroot00000000000000 jQuery UI Draggable - Snap to element or grid

I'm a snap target


Default (snap: true), snaps to all other draggable elements

I only snap to the big box

I only snap to the outer edges of the big box

I snap to a 20 x 20 grid

I snap to a 80 x 80 grid

Snap the draggable to the inner or outer boundaries of a DOM element. Use the snap, snapMode (inner, outer, both), and snapTolerance (distance in pixels the draggable must be from the element when snapping is invoked) options.

Or snap the draggable to a grid. Set the dimensions of grid cells (height and width in pixels) with the grid option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/sortable.html000066400000000000000000000026171231750357400255420ustar00rootroot00000000000000 jQuery UI Draggable + Sortable
  • Drag me down
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5

Draggables are built to interact seamlessly with sortables.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/draggable/visual-feedback.html000066400000000000000000000051131231750357400267460ustar00rootroot00000000000000 jQuery UI Draggable - Visual feedback

With helpers:

Original

Semi-transparent clone

Custom helper (in combination with cursorAt)

Stacked:

We are draggables..

..whose z-indexes are controlled automatically..

..with the stack option.

Provide feedback to users as they drag an object in the form of a helper. The helper option accepts the values 'original' (the draggable object moves with the cursor), 'clone' (a duplicate of the draggable moves with the cursor), or a function that returns a DOM element (that element is shown near the cursor during drag). Control the helper's transparency with the opacity option.

To clarify which draggable is in play, bring the draggable in motion to front. Use the zIndex option to set a higher z-index for the helper, if in play, or use the stack option to ensure that the last item dragged will appear on top of others in the same group on drag stop.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/000077500000000000000000000000001231750357400230735ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/accepted-elements.html000066400000000000000000000031031231750357400273400ustar00rootroot00000000000000 jQuery UI Droppable - Accept

I'm draggable but can't be dropped

Drag me to my target

accept: '#draggable'

Specify using the accept option which element (or group of elements) is accepted by the target droppable.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/default.html000066400000000000000000000024471231750357400254140ustar00rootroot00000000000000 jQuery UI Droppable - Default functionality

Drag me to my target

Drop here

Enable any DOM element to be droppable, a target for draggable elements.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/000077500000000000000000000000001231750357400243405ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras.jpg000066400000000000000000000547221231750357400273510ustar00rootroot00000000000000JFIFHHC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222 ! -pmz1ju³ܵbdm|j"$͖nuf=gO\+hrSR+Tcu})/OO%S8@ _kfJ+/.vܺ^ՕidDVTbvLS^4"ص*m,?6g9m)e*DA N͘sGI)1O9WYEaѕ穡J!EU6pO,0w L@fu7ΙYM9mkjYZ\fkQTyW'(Im@%IH1Xޣ 8|'?Mm秏6o28q5XY ݧæ /'_6nʴ.UkL.Z~Đv'urwo"ܥh]vw鲹} 4KjF}35u)!vwtss[X]?GJ(@y<-y}8t24gMN =@@Kl֌ֹyfթF{GK?;^2ο&H_-Ϯ=[Jk2ċ4Vl=fʚfVm@Hǎ7rf/2Mn L]-GP9dS]hlz]3y 7\~9.c; B+_ /}^X*˛V@fҺNsӋn6jm|(1`'`V4fΊ73wLlY}5cO%rV1dXNMQQ!EaeU6bH1!(:&|RVu{g$ 9rǢ /|ͫ7-L9eU]1M֤5Y=`Qa]syyjCc573RpY\xi/ \5,1neNr^~]zd_i^gu.  S>ydT߫ÏъFuY8{aʹEEmY $6uJ~X/L鞚9gN3ܥe.UeF:bmFIs7˓YǮ#LK! YMk,QnBT{R=|sbcs2Kos:l^O3oB5 =f h}b̎!|_laHfR+.e!%tv֪˙W+y8鋳WFrxפ $/5cϤSleYؼڜDϕzHӕ/n:\2 ;-rRN:DWk8wLRK5ǐxܚP/}vhxיrı,BOlĬPlREa{˛@a S4{\bm.\\Ey tgĬ69 G=axrMsED2ct @1u4c|\8Akb =I]jIO_3m'C|Sհ3ؙqO63Dldr|~LMe d\CFgs)\I1A (P_W@&Su DœLL1#8Ԯx RAVUn$e_ǧ>,ʼnqJџ6K_ vj&+p#yƌ%YiY5}u4uѪ O(ɏ.Չ2VumP}ԏw'Z12[Y h SSu':*F~u!%cg:H8ړ(r,5:sHeXP~Ϗ_}pLk-sn1VY)?*^"\LV4W "!r a]Ul겜@_K| )z=ƒonX2/ q Ø ߸ui"@2Ycfm\װpNO 2/U]p&eH2)ͦuN^*3EMr?]Zke}^l6%D%6moAMM:qM+^Ly|d/mbHz'H(ߎy!V5/f|l"e ~'{39=!참֯V;鐀qf$>dRz4&{#?lwa'jy(G~&oOfwz@d)=AŁ:gGZS8ļĮd YQ.Wli2 E)P9wx OĪZU$?S'+~݄`bAVC&>4ɛrk e[X=s[η>\c[N a,myfpۣj*;}Ik3%å GaLɅ /5\,A˱\!'˦?͐ʼWMGUrw['.|JbȜŏ>ύr-dC`?}Q0UOI`ϙSUz^{l]Pkzߨj/XJcӇK 5RACSfoҘcP$l`7#<8?`(}e5Y^@Ú:YXt@UȀB.5*]P j,A^߃`xD\.`CTn C35S}HuN7p}hYLS4ɾ#9Ō$ By#'Xxc QLG>-eE68_\JkթL0{7cDn'rh *|{Ks6\؅\N(Ȼvjǐ?o$"T^0;'35EL'z"705?$0 !1@2APa?].ɰ=*a@4 "o'Oc+r 2)ѩ:tGJtkOQOp8 IUQ z8hiXcd(cr aP=1s=]BW򆞐SBV,ɓ&LLptrt.< .<0 [Vl#l(FaCOnaiN _' 01!A"@Q2aq?.bmc&.RȆ5eѧ-\ofF9Vʣ%?ؐ+迆5 <7qq9om&$͍\MMc4!1A"Qa 2q@0B#PCRb?UrGпr>-W)\u6ۜ,S>~NĿ$Ԫ=旿_}Ыm STO½Jئ-[V>7+J]uX>[QV%]#o_0S{: UG*ϡTox񸺌S@Si/!S*JjXIʣ:~l|u?7o>YkPtE<; ?1/iݗاR˿dQJ,;iOO'qƥa! [ú~g-dss!h8i?;dK?rw8kV|>U\NP>tzq@ѡ",$;rL>_UQO}ʨqݜ#W lz^#gnv,b k p*VVe1)*!dq]gZ*E$rxIx^Ï-k>U~OmUg`$}F+[Grt"N9f׃C%F=8nŏcԙ>mH8׊GN13pTBP_*8Zd]!ʩS1E*+b$߶kMԡS}4uHD L.|J:. 1±:G<>Wᶶ{SeU SNkѓ= R6psĤ|1?սh奲eZfFprIIYʪ_od])['s]YpB׋1+^VT}QNj[ȫ pFPndf#_ ⾐p<1j,O*Tմ?+h*OqSH۷s6V*pAɍ \yt(a %i~KE inPRGaz"s9q`0q%Xd3ó/ЩO-X͆1rO ~4kM=4+ث=yBçuS{ 0:cx0J8Cgss|DNfP=4C*Ia.URsTw$vL(pg"<v8;lpw",NfFn圥W{gX?޼xzYgzKЗjK)6E()_IJs#kuHzp;G~Wg(i.s(ӌoGuib?[Vq1q{]27Mdu;"+7wctG+9_)Fpw1}-b*3r䦟]8J7bDO\iM=2K>4J-]-d4>UgJ^j놷d Z} ~5RDZ㰸mZj9}{ʺ 6%EڃSC{зOzu8֨W"wd}qD.¥5]~Q*wWĥ-6ƔI\->BU+#{'Z% XӉk)}99jbvR e-$ЇLPڬi%/H!lB 0> Mv0IbH;iaVE{z|C->A4ijPSZ)DE*S]}t#9+m+[xV&ez_KఋSY 2YPb6Q*(z?CB wYfwӶkb^ I%(6-H~;t%s[ lG@P0j> 1{;u##8.i&;>UUДSX\Q-QeQ.2,eBh v0;N=[%+iO2 @'# 2+ҥJӌ:KD`1;D[bi_ Rb 9TrQnl*iB2rJ*TRJ*TRN(K^Yp%M^3ȸK`\bʎJ*TRJ*TRJ h,C!ЕTB,ԉ| kl/\DNDRJ*TRJ*TRJ^֥JۨR7^&RNpcm>\Ku Q5Jow4GMU ľMG`1`ʕ*TRJ*TRJ*TRJ*!+,8TڨIW^K- BJ"߬_昔F1eJ@h)xcL!KY2J*TRJ*TRJ*TR&fB; 3ȝseDh&{;fhgpw0sq.#%`Ƅk05+6N6]sATV^(_f 3w Pʕ*TRJ*TRJ*T|L;9QU"+!j 5&0똎0ߖjUe 1㛅]o.3N‚?37͊5r`e*TRJ*TRJ*T[+ʉݼ1/n1Q,Z>+cW]P23yPZ G\[eQnXk 6q;!>/lO1@K%J*TRJ*TRJJ%4SUp5ORzgL4t =)V=0rs./[f6̍I@*{?S ljQNkHΕM8tnw&J*TRJTRDL 5^^n8K/[Bu2;[jзʪ3,Qq/-/$1Ub_6비O ձ{M9%x._̲\rg؋ee>sd!:cRDcy"CzY^f@ *֍̸meu oU0de$aLV\-GĢ&3, R^ Z%0n`ΕYZa4W]as-0xnj3b4fEAuVWp!A/YeʠK9H~Ti0ٍ܈bJNf{6TŒ'14ϴZPb%i /UYMbh?e20Dt^' c\f:;eF^ טW'7Txošc5n,E7^ܫJY*4Xw]2tQ<8Ez au[ (wl-!̪g4yqkR =̜*\Bh¯G`!%&yBpD +-̩(1xa4BV@`wǙn`2&+;l2ϻ^ifvݧW+70qB솦)({%zȼ/`^c~aȲ핑>SށsDi$ه:] yF7s/1u(L!xZ,5Թw3ܱY"Ļt-3TH{=aNŠ˩\w-SPbL$|5W4?x&Zysٱ2Ko2a.3e]pbܫXNVc9FmcsWFؘNSAScV8Y%k8GMZ'ij%s:-D%h>ˮ'Sikq,f# -%2$B(R"A'3w!Hf>w/ad6GS()vN2*[k刈,2wh(F[fꡑԺ[bmDmwMyKT,ȣo e.@4K̡us3r>nYn!@=ڋ.#|'ЙA#^zDD r hP=%wUxĩ\]};aV#3)kL8/MEO s`Aзfu>+>j K;W Z5A˼12&U-.xŤ_ #C}KidM m3;@ˇewĤy0`B/@@myF *t4ޙ[(<|ccMF+q2VC0UaVQ >[菕LB_/jr2͏/ >bܬ.1.LJIHtW+usSi^0(b9c|3bw M^,n|LN̶֥[( ˑ-) 9M mN.;4\@n$(*#ӟc2L8]ƛhØ|J?idRVjxeg;gQue0Ju4R,kKNd퉓t ;,kwyX~onD;h@o4q,w)w >Bs_ghcwrMse 1Pj*WkXu%AX6TU+rDQ}%z;5xQԡv/ v2%!ġ9cEFW˹5 7ix:E2WGob\Bqg,(n%ک6 #ε dd >J#Jf gNc.əԭ3P;se^}+2fW[b~bհ;>!,'DerA&;!5 Y%-cS5 B#]ͥ(Ơ )>u,5uMQ̔hHQV䙔[^MrШ劷@]2 n4 n|b%Њ}[w; #+ibKb9Wr9;@Г빞ۧ1ͫ WK[*(dXw-Ubg`o=iȬ1X3f79@ 2X.3u-/DX MQLJp%\e:,xB^J9)~^!ܤU%UVFrxaDdjhfew6[PT& C` k ܂18\Coc\Dtm4ƿAix8nC pG֓><’ eP40-Z. mX2#^5,Yө|l ~#ITKݞ.%-֚w^#Jm; #2:7U3* ?OxX[䣹Z`{qmJٽp%$n&W'I:>YXpYQ 6i Hj;F-YG?R#,ϵK|/ \z'xd: DV\̆: ]ܮCZ\~{5MA#ymA= ބ<' MY S\ɝ5 I5hH>T'$5+}ΑŠ=cJ i( -ugc=^)yTQ' Ǫ@"T>5â2j=EIM1rхFgˑ Ot3eRu7] 3BZ-~؅$wQMwG|խvHv.hԧ 1y#uz^]!1 A0aQ?,pcyx NSHD[b=W .F2!A\hW>A 0م˔!T",SCM "7x#?\U/o*"aT:$vޢJEQ.>ϐ(|1? B1&#*W axRfzG|6*TRPqwܩRJ*T`a+]LB (`Lό*R.L'dФpɌ"Wis|:CGCS!3H,uχ'?YSfAoxؿtC#byLqGc^/rS .<ܙ}jY+;)+W*[¡ƍJHELfC$0~a= ?#}E*q9"+:i05 pfSN= DEŶytpɸ%zw0NL X?灴тHGgDM!ZՍza!?;ϋ7rU xnї-S mmCY"k+lI6mmKܡڿmm˄S'qSzmm'c{O6C#ܴ-ml]f^,ݗ g>GݶmmZRqmmܺ#oS/'ݙg -xI:s Hr޸/gu`R m .uzx^[$({Yt'#oa98k0^8M {9Ifnc쳹M>O̐wI.o%[)tާӨXGK\at_7_ݤ#sIz6 {kvåjw=G2|tvi&έDuX4`X ܛb>Cܚ _w%Y%2=Gewm@[]]YgLnmWgΦdf8y!eoI j3mчf]Yt2y\^a-{nzZvpO*/҇_.Yn9p1m?ztw\o#Ef9dM,&{̍|-8x'# lw=2A8^Cޯd}/: f}o GlBpp&M<;{o:8)!1AQaq@0 ?FuWL8O2P*82a W)7%*ntb^)N&:8.%SQhj(G yfS{ŰӸA "M\+.0|T" O5dip4R}-b;rJ @oYS}ORcP`Xcb EhɈ8Eu)qlV1|EgoO^bYן 1&5e FmB~ 0PqnF/gO:fRJ*T@Īav(hJ"ݮX͌PhW $g`/ BUh0Q+CzXawCX*X6cFc82xaS/j$% &R_9]Tc?% k+ w䦥P3`L`-4+2.Mw ĩRJ*TRAjl$R3Z͇QyW`*5wyֽ@TLp!T7m :֥+~#-K]Ru ib"݆>xsn,Yr_`&ۭh≧?=8s[^r|X&݄G:Mqc๵ܼF;x:q@f ZZ--28ߚ龸Ĺ`fd/@E]A W؏>yH iL uhc [}bx=l-NZ1Rܺ] S2cAP:ZWu5!]0kf<4֜I7W 5eRcva@y1CO>w`U ^G ښWqeSYӓ ZZs]Qc+o7Xa[/XBf si5r)))*(>4liۿV6<P .WT׊%ik|۸X]nת%Zz")2eߛED4ψ gXxJ{أUy8/.Ɛ8"+:%ı.SsKRZrRU f@uIJ_/?9 ne r[MLIsge^_p RRֹC[bO*Y_0!R(r.'g*6sQ12!{zJOqpr>K`U3OؘbٚՊ-='Q.s-rÔt>"0'kyN ]V>]Woᶇ%oű|:R!jzf AK%u*36Yn;Fn=aYH GSq.3[4+?uGgE5ij*iG *ٛ _e c_qi x҈ @lQ|tAEMklY@4Y%xZ/;؃]}KV]\z"ުbo#U }Ds]PW"s9q:N, @X[pKz1[ FYt,kJ;d2EiA g9OAbWK0>ahdngdijK5-| /9z9".>F x4u9AP:ib b|# xYU# Aiϣ̭i<|S46k&鉪67^8>W K:"* z"TUĄa"`+JS̻IK;ro 9iyy`9VT2ί46'rdrL^;D#R`Np.hiDͬh|%(й#f6!H+v&u+~q"߮㴴Z*EBmR(ZS[4YTZ#Se>Eczz(NmTSDl`` 2`qU|Kے_'1Ӆqc%j YrwԹќ5y}L%WgĤ3 aC"iKy' 7Et`$ Ԗ) OR.bfj,x6C%忢(Vo._068}wuF1dq18G؄@0bcE8'.[E^ڥǩ`UN棍ohڿRUyj=-usp4%+i2-gnyxn{CApd([%^, +kO{@vf;QQr~ Fi^AKBIroIxm4h!\#eS^{_VU6Ք/_ijNž8WǩtJo_5 `:0 V:cW5ۈ `06/ٳ]F9pIGz]xlhA_fXበ bTUZ ~*3u#tUAWAXfeN3 [Z KkqB]Իk0O߼;P {:7z-Z8qđJ/BFC ^:eؾD81~&%s/_X%jnXpE|%pKVP? Ѫ|L^ _Ը"#n`.X,W^e1 +Lp1īs^c8sEzB 0 `UDcC*]ih ܫ`" S `ajEVayNWq.M|3&"d250F\$fc/$hwRHlvEm9އگߘAt*hLun8?Uo} Gh7DNZ`+,Ky'yF?d"49f' *4[umqM;ykTm^k_cN5_g^Π6s0V\DVk)ͣ"<4nX +@\2ԧPҖj0]2P02-)F|x5yHrU8Xb;!YY0ōdFr ^DY9,n]YTdKXd 8? \' f&QbX8+Id*0tz[YvfVjY@8e tWN320L}zrU-?PbS^!`U{ :])p *n~fw)%}t1VSmkV~e Zئ5%/+FdQ5!.0-wUOpPFd]Dh(8"S3& *Dڪ#eqr:E)CXr nhڭ43 ĊDPaMˈǸuhsQ!-VN -~Ԡ{)!ue4QB@ω+ JőVw1EVj[e- |hMlqu\El+5 YjӽK$hՋys( PM{<2}׭EK oz8*ī T,1XUĠ[ڋt zK Z_ʈTZrVƤ2BV7_1k8<=P[(& )z!lG&"ؤ)Q,'qh*ۨ[/[̥i`-ݾv1u3 v 6>s5)U0u\mk"(ѩNV"Mֳ&eL+]2R2n]=ܪmaI9 "[s5uIwi )i}D9Yװ"mJߵ>*= PtLU{ӥ`|CH)'%d|D<S'"PxR!;"G rG"`C<=)TG } U͠0s]6bUQE0qkb#Q8kY2lٵhZ> 0;# CDxb׍&Vs5Η{T9c Ƀj=:f;빑ppU~( o:rhj7&K(B̓ߙB^T#ƹ y7RBY3O^Q̠ O <ۙ(Q+e*_ܯlZuB|E.PPw1M5I$Pp7 ` <_ T>m]p*ÀPcV\-_h( '4 :f[ lUǙv6\$£@n̪QT.pi&)[VkdŊC- );bl5 E~U.b]*]0ڵ)t0Kr;`n&} ՝r\HbZ2JZEpn,| q"͞+"j\ClV$-[fEF)6Rtt؆ހ9 JM>!0:y,+JOzjBst(O$qt\TAl Ns!iQ̣1"hWA.`%nf!w0\|˕Wv>@'oY=X #빸p7-~qP!ŋ.Qm`n؏:ܯj]AZ,H a2h`1b<[jXH nB~ `ED* 7IZJ_!6@`N2cg5M5›#gk3@pT,T/moQ n ?Wܸ,N}gॿ/\DXK,L6ciO$̫kEFYƽq ` ja-&7٦Ȍ`!cs]&mXp!IR5A5Am4 -G{|LٍX@R+H9c˷!|՜r_VXUt&AIG艰ڼ B`~a49,qB5窨ET(6(&gr|ʐ& 7WƯĩL_3?5VhԬ^xN^:'*Z0=&JԷu*%_In\1,bҽF`#wW] ƠXE9M"l*B\]aXpeuD;CPUn[ȫjВ%cQ_[|,ofQ޷]ʏnؠ!0y_ u7x YvS[y-$n)ZЇ{uw)}v)g˘n^.$zV FQᄁ亀=n)TsԈS ٢Xa9+&KLeD.,f _uAV g01y # S\0h5jKx! ,IU}cfp=S ޹aՎǪ;A9UY[fV*v65FpU>8AgXƲzxpĸ`ӜzK-8t]>N .qy^s)َi& !*#xϧ卦ѷ⪅ҟ2rCR!0{^) hs,~eAxR!TNYsH@lEa9% 6ˍL@ o4쬭?YVL,<X/Pr,:| V^U<Jv?3] 5 \'Fbv~ K'"muO< +QGXKn &آ3f>ܤ0 tq˟3%eu4f@A+B dp 8#L6e:TL:>+̨$ !G_\ &AImk%s1YIM2"Q7lAeC <g !6é: &僅8E!x3GT倶fF$wVhs iV,3ޠ$u@t?H] ~넶9`TTQ7eg%~)@eȵ>n#٥;V3b,rrRcϘrzQQٶRp$\ . =?Ĥ-0i^X)o/l\DU}*7xsZ*OXb Eǃԣh(jJ4Xi e!`?ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras2.jpg000066400000000000000000000620231231750357400274240ustar00rootroot00000000000000JFIFHHC    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO ! ΀k` wWf}Q)ś\hmE:YmTO=\h#F\ii%zCƈڠ4_ ̼;F6Mr njD|cEãa;rӂ\|u8\ @QsPӑ۶911EDrLi\#pz\V;O{s<7^rۘ%QJKIdw%80jE[wN{LG|(=N_P|v%8uw J-Ɠ(9FFsgz0 =0dJ!- )/ SMOykƗ(S;qVKUIu>f„ J:G؇=Ϭyxg^ ~}BAg}@ɮ-NJ(2BzlƎů elmŋ͍ ͩwrhO&WEa.Ʃbh&zܲ"z|vMEN&%ƻL9vev^%iИ42zt'ph+L(պTkI'.sҳq^pϖ~m}Ey 5Zzũ\2]蕯Z07g3GM[=tYu8cԕ͆g|g/]=.-f>?' mSF GwžآT(se^Θwn~Ԇ4ѫ&kUm[zفf̨!g)6ڤbs>Zm|,1|iLu`i;;wr :̞Ż*mmU˕3.Lƍ%K8yv٧5omt2z8b1IG%{Ƒa浳-.2 Vフ֔fѳɡl1} ZeϟXbARQt4N*睴+3Q[6!Rvbi~ifsю‡q[t|y(y.Pw5·L3UN̓k222Ф1}֦DTm\ځCvTm-Tq8= eZ4~6{ ;Gz8ӝzwБS&͸th jwR=Tfj8_W<)gS-;pէ&uw8fLY oMkXE)#%mGfT&f^:7m^ c챟3.sS-ֱnS.ukcNGB~idzVR^dF~dT2"mv:ΣJЯbuFZ3d)nez\wQCB=ђVND+!"1 02A#3B4$CD.R-b MƦYZ厡6RZ|}OwU}`O1^X1DcC>>:jx2zk ՕsP 9@)R'La S v3lLyy*X86K_~$-{\fPcaAVm?P&<rWy) kzx;fq1 UMKo{'"ƻO(TG=a[UnccgOġEL2)Df̣&_,2oc3&|Ι+;@klưMr k`5X*%6i?~aOT_}e7\+IXN-Տ&2`i_Y[u7gMr s^KOyљӶ.vԀ aEżbW]\42SR*da(sBA#R+^cpG~I3⮙lUӏK\ -\ta:eSQ"ïPճ 56%m 0bfgoOIHj:agNSr5ҺP% p tRpw`dQ jƅBk+{P7+y*A6%0t}??]eWjj,\%L6pWTC6l0溬vN웜YfX֦Unx*i3uĬyVg0 #Ҋ=:UPk+$'? &kIEO!jY fֱ<&S3zzx߿ڍVmz/ETfgtF{s b%:<!ʘ0z{%huKK+MivUô9.+zȳ6 yt:VJ",8 ~_ H.1LKݮ-mobQr^[b uot$ka{B@y.`8Ubj} kvEU{,蕘tjn:NF.q6GZ XLpʿvEP#Zt XX_*Y%}$(D!SjJA$<ū]ejy#ͫ7͓s/c"6NjU 貲'+{VeIFyH܈G>Y-c?BЎȣ"C(&M;v/tBxEz/({;gU,X_L&% x+z隆'DNϤؗq/ULxx#!YelRmOi(N"bc4'{%*;};J%mR-+?"\XXxbR:'(IHbC(NՙIGs?J?NJ:6bW?y9%ώID8\,J-^tXel['v9R? )> B%Q$%K3'Ȫhŋd'6ZHV'#.)f>:ME g YymK!(dc. [>IPdLxlQQ佋3y<]iwDخi8i#G15 䣣Y rd mHoE(lk6E}~5!1"AQa2q 0@3BRbr#?+rLLQOBy.gѦ'V8&-HG^3XK\l,P|K&asJ׹rD),>+l؊Јr~!7SnɕSQcY?r,vGʽ>dj|8$i&=|j!)g I"J?EO^TEIȦE#mܘ1A.ҤmzGصQje;S);iV9ѢDtE'+RJxsW/_ T mr(JµzGRoO_7ȳ'}w?o_u'tHȪvLI3ɣ SMO2r/*Fn\gC?7&|"`?ӅܿH UʥM8}Kw] kΓ7#=#e?QTCy3fVЗN8X.zO9?JS'))}ror!mL-廚I*~bhLTs7%J\dSdDIHYjY2"̳2IH$`!=uJc%?f7w]K̖ɒ/Y-%*=Lnфt8xj܉^K1SȽ;A q.|ފm[[iشQNq|n J̆UYfEHM)?AU-J0[=44y:vRZ>L(+wnD^MfJpFۙ;CÚK"2JO!cH"*#l(uP:jp^Rpp|CW76bܪ2[#)LȩTJlUE0*D-?r)/\.t'ĮR&j-RwQI?1.nE|E?r{z4KDN2Crm'du԰7:M-iԽ%G΄蹲).w$r'v*y=.X"!nՇm뢼0dzر'!1AQaq ?!ܯA"pa#YpɑX_!k,$r/׏KWSMzeFG7H@e[K#TaK܋=]UBl,g09]>}LnsKsM@'^"rE9==8@"8\dH9tiGl};`T^6MhchQgN8_'Sl%f;s^u EM )yc>ōВlGy&]NH'qtΣ Y[S,p¹A({!}MzA'D}]Ls.Ȳ[nҦކ(x _c  PWPIE9M= CKڴ@:HF!"oL\jkЎU*Q(u୫ 'wUB{*ʩp#ͺ VD˚Js<:*y&d-!ݸXŻq?QcLMiM,טWDRsX%\׿LJBx0ϧ24zL) `ua0*>&PnO=^V !U>ȯC%.KJh`Ñjp_ g_O*ΦvVa˖\Lj` ͩ(a]ˋ_*`aqrc`30f'~VQxj h/ 4pޖٷ ,9ۨ%eqxIT QY [gT_O') nDph 9wqd38 T2Ae߈>ofFY.1h~c2@٤V~"،w [)*c{DJg Z5XsAq‡dS<a5R^#PXvX cJ#7 UM}M<=Fȼ%r &-9Bk o_q5|݇} os8E@v"nU9;WqPvK(@ UkʚGĥSLZT<.nxA-s›LA b- |Lf8<ǔ'P+2lĕ2q$/t{J3M|qmad" 25n+ ḺuW)9V7E VtK, KۅR܊+Di*.h3\\7T{H~"xPS.F0}jZSMQ6TtO}*2HjQ•XS!6iz*@п'5dqQ /4H>.AO뙓t`(%^?iѢs+i.-8F=, OwGT0$%"ˑdy*nIz~+:߹I9K0DXtutq3s/q -DII!CɦUB.|4Q}J~`Z"<9]5 ߼zdq==.冮"Q Nwdx!θiA6Wy<9 e3=[ÈrEʎhҫ`ֽgp7qG_*E2}KV%\21UY}@E̾RVDCK=(f\/Vyh)Ry F+?VQ5.dsTw+RxnTw7q +)F9#ZP< he'F&>NJ0*ZVS3Ht&J2Q/S=-ETq63D* K E*ڧQ8 ꥟UVcA}bqMKSz68gTAU5U Xf`Z.]hLu*KhQoڟ$YDk1HyH3LH1jB)8JcUјW"WQ.~v{ӛ(A\@79-..hUlIZƾ=.Cww;J[H*ɸ0"7Sg bʸ٢ P6RSZqrLar,pīYAq;"bg_.W4)xf01(w, q7]? x;О%Tk0^~E.F:.p͟2Zeȏ"\D+BVMgJ(%]r/4hF.*y!,0'5ɧ0 (;g把[5bЮ|Gkg$|1si@OJ`?E!AR^/sf~ b\?"M-9B%ͻxK H (! S\t@0ipX[KnN!+KW)崛ԩt]MAn9:2 WͯY̭Ys A+`raH|1,E ~5s$]& p9LiT{Pzk eᗴ_3;pSD)KܲD懈e2q~5  M M)Q!fc=4SYeZد Sf_RƱ'+jvKA-f||$вOH1v2}MDA)Ws-G>eMV8'yE;jWvD"\"Tg9%|o25qCpj7V);1rp(k1mAd?ZUap) 'kL% o N]T0GMQޘ$g=pAXymJjwK;Gb*.a}u b1\#;IZ΁g>X?ߘ y˂^ (ᮺ.-/~.dkq<.0t T;f >UoiJw 6hϕTyaU^l.D<(1 ZjFSV$ sG qԥwGJ^(L41CK%U#ޅx&ʥq4f鄾G}A GNT12yJ^KBpXrJ1j?D5~JJz"đ0a(`T^y?2Am4W,F"3OnT+Z8wEVj9*QԽk j6l(ث=`5}\6WS1;bƭyD=%"MLbs*`m]Eaj/ Ϥ̀~5g 2+D|mLw-Wľ5k ,&g kX&0#I-_.P;Gs.K}j-AD<bW$)DDhVIG4s[}S%l/{|V08W+@ ]cYy|K2sBԫpL5 Jo1c]QD \ÖP7 /\̣ ='BJr2rԪgg3 oL-19 Uj! .YY2x7'{Rq,[7=^(1VϺ7C?νOt2-PbK'sW]z QoJWgs4W[Kx٘ cCas|$(<6K!7j IJ©kf}~bzmbf3Bݎ@w*9I 2&yR S*NfC41sנHsK(U69 fLX-CZE6rG':!NI"YAjLrILg#nT֙P `v2/ ȨZ6OvA%7~&'#ď "o'A|1|O?`'#77h[-'loYoC>(6vS6[#J~r\X~Dc=dG̓?% "}\lB"^ cݿn_,26nCa{8C^}.%dKvkga!XG#I1`YNzV_:ߑϛl,t̸2NΜuYtp#<{/|#FY<ncKmoA9գ7H H9 2qϗ]ٺ@Y&HGf?-}9oiI\;n2-F7lH6,ggCwPKOVF:N19gANfr߂Be'fݗ)/ 2~'#C# ςݯlxl.Ϝu=O˖C~笟E6eBzM`zߩ($97-lL/8KrzFls>Zum)[mX 'зH<vd? {"?޷^oȍ]7$O`!6r!,am>9{-.Zݐ{ iE ^_8]^$o6Ye-=8I˅mJ|yw\WBnlXo3V_=j7?7_ mf_8.l|Z(q|qbqr/7t0G(.lŐ}//al{/m -,6̛~YN $ .<},͓y-Wˎq<~?7/xla-eS+^^oWD:dˡb_ `˦GgZ l)/XL:lc̀_<˜ ? ɖO~oE/[`/ ~/2"SzyDPaPcחȔO4y &Xs1OeAnPb6\JO9{9v!AٮS^w(&=bY<_X$VW󃎦*ݡvHe: 7Qtes0[@ˀKTbE;Y&kzʐzMbwGN wL{<ΰF}Va85be؀8;ΦCo7f5Au"y= ʨU~5r=>*IEKz-&x엙_ hY8E1j>3jLD?wX' _Y!ںp;*}5p6=FL/Oxa+yb3 :Ř@ܞ-Օr5WS5tֱ :n]2v 6aZe?Go@g T~2T^"-]Itj`|yxN2̟׉)g+dk6`$¼jJ$7 W38sq472Ϧ67@60GN÷ɄQa4p?;a R{.D\'FF+S5e ;$#"L^׼K1x`eƞMTT돜FW`J&u7Ts6-P>yc㞰D~y`+?x1ROfѽZyކHVzq!FNQ\J +..kA=q^({H^&8X-5gڃӈD]88l{;ċ 1iiCbok/`:;` WQ~9{ŠGHg~c!HX D/3VE71 0j~K <v\4~qV$@.NHɈZ'lb]̈r{y‘z.$<!av>n''߄7(gc5&{ ~NgY#0 Q 煌~f"!rβMsk>-/<5۷GB uMq1o<ޣE𫪈ζkG_p 3?xAhP1ga!^ۆ Rxj sMZ{[Ii Œvzĥb(sroaȶ~2cO&@okE܄ Ϝ=ifؑ#M #[c샤B""e Jtp؄ =^JĊ(IG9@"+T򿌫4׷:pX iT<}|zbcQyX&PAvفA:LIeWZ5&|Htdi3fc.+ 6bӂyaq6muqNLn?=c!8q0Lrh8r3Jsu, $BLJgt>Vi;pW3vhZfw*Gُ*:ŝc#޽'E Q~mЛY &q]A}̴A46f?"8ۻ_0,[n4v{󔵾\AKXhk 6f)zr* (vzbe $sp)MM#:l։ÑW>/>2!{ {ǰ'G CI36{+QMxnh]YTrKGBoѓh=ytqxnƬhj0h<9$oAl ^Qw.;#/ԇ`6c`/ dmi@&'2{HPJ]ٕJT(PH8,&y\ &fu('5ju86P4z0 6sR79ڸ߇*ޟ-n'LB(-a (Mи' xbuT WP (EGxqiupA6%V xր9)os"mb^|x_dȚS;\.p.0mѽ"ACgٿYȂYA&We{rp=p4X׺U*h&ё1#:U8%?1yjUkOC}|*03jPȘ(0֢{I0yZ d  jDx}ark~>p18lB84֜<_ȗxHy6RQu>9N81$HUpAִt|7 (Ji 2?9+ x6 E~$L4Eo[ٯXo(q HޜTm]9e> Fp+Es26k.B|Mw$Nhjμ(gK\4]FaD~p{A|$"2hB кw!\>[$(|7,>Qu%`!/ **\Mą 16O?yT } s.VrpI3r DEʢxF߬$TۯO (~8gtW*;*媵x4@1 4Ǖ C;d"nԔTs=piPe.o!te!@aAܿ/8EBTtWOcDUx<~KLS_<- s1 8*sOJ+9ew/{MWFtW@eHX&UZoJn;aٛh.ʤH8?3AՒSq:Y>3@tRtqe$0sxxeoۍbxp8$xŤo;6_ /n?bR2ۖxAx1:D.>RJDȢ*A6fSbj'3ٵ10j94د<1i/& V"NHD _\D@'70aMXһ/ lB|sq悞Q}h7 kMo[ zx߰‘:[saCSX; "x3]5@"p߼2,tyR9{QA XpTpx mߜ1͓k)K[I@嗌M# sfkÀH1*~{bJOuYS֘j0A8< |b G>?°`f}⌳/O.!Sf0lH]ѣopp.l+m8wTڕ}$)RNK5HLWXR Fް fIVр̋)vԙlx$ ;'"h6_c]}pH;O+ Dm9(I^W!B$mP&$z -A01. ~ؤXމi oWhM7ـ1Go}8F:B\Eu:@W.8:@!{=} ]'| 4':FCN5*P~<K˓fSS0AWYһ{Në&ynq#ebbF2.baɀE~&>]x?ㅽn,~::4[󖚬sJő/*ǥ Hf*qPOmy<#pD'! %iboxZu3 L.㜳I`x5t:D2a/3Zc~x ޳eMs/€-։ոtCjyk3W`` N>;(r=hHB=`@ vsIMs-0[.!*&c+q`|8Ɂ> cD&D98Ce11 oO7*e;70rҧ[~h K-9 e]^y(׈3dԺ~eOvD:ccPaa+=9*v ~zLH&bSP8B ʡp{0CyYRk{ )K3(/L6ˏTGQVlQXvp ,\9*suûεָcegnyw󓰴9OS<㣞BV5 0w6zw@^e܅5>PB=&`8cBg~=aRu.[5> Wy Q^ָsq@$xv6?+.teΠc"}W%,D}Sh/?z!HdÉ#N<*/37tU7WfjcV/ Xƿ9 1jf5b*EOm[̹@Cً+hX±rx-('4R]v70V(FP"{ ,FIGI7{=]||Y - F׹I̓*E2%&lr˪i{]-$f94~ </P'.;{~qHo͹o X::L]Ϡ =b9E-<St? ko㚡qɂaLw(iѳ#8O!8~rq+0f | c^ʺ%޷ `*<>٦I_n'3[;ib>pd #z-%qYb-z9 /oND=Lt! ًP">zq"s9'fmϗ4"Ey,9qg|o~~d08|c`]K]Od h9gtUwxd(h 2v$'ܹ2X9ðhJ(l1ٍq]v/2&ףUW-:t+q`"8w0>w͏Lpf)w+ +j{N*c|p.UQ._XZNLנD8G. !4]1!UD뤚m0dK{XAWgae?|nHu)OlK7m>q */5E{8x n\" wqG1#aCzG-SxM㈆. ؚO>0x6j A8AH77ɑk]012ǃ &BU:\s\ht}^/G7 8W; L.di( Iibi^yDz|55 My"KJe|- =8"sׁW˄X71%C 񊛠_;d.Am9^? ")(WgXoSƗnODk mXuNӀnyG~zP0@7ǞNpAM%]H+ bM76[JGyIm$_o5.zUr" +92+1K2v^ANAC8%+~?xta?xX~ζ=X3 Ldm~?xn9<'~h') pHG>#t'*ѻXS忬/+C5Rg#YȒ4q .χ7üiC"YNȼX-ǹpK0ٱ?ʆsvb!^v$a8Bv}hqPmzu3qʍOkganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras2_min.jpg000066400000000000000000000041641231750357400302710ustar00rootroot00000000000000JFIFHHC    ' .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQROC&&O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOH`!0!1A"Qa#2qBR"!1AQ2aq ?Ұ_(m_WB}x{+vs5וrqJ.-a,@$]y\CVn .%HB(HChRRK{YsG?za^$F+CdYwn>ʦfO $Nu9xUHY\~V98wZ608d!zQG|0ǫ\ԓ<_\,*1ǫEY0.p?J{ekw+2Z'TpefBn3Rv7d^a~*I܁cm)SuasH,;>ծȅ 3DPcbqɦO`FA 9̀j%psAܧYΆlIck\BT<[-k8nAL}u?5I1#WXoѸ|H(B9j꣟eQHmm'zaS2Hۚ|IK)pG}Q #rs+\fFU0X}ji Xҵ8&6WbV(C5_M#-&"0v(GYgiLDS'S)DFݝozح5k?FeB{|Pn-.=UV,@C2ɑ?J%a,~Y?>%[;F)Vm9;c*5L>BxKH2qGST$/ݞOTmt> ^B@.8; Ǘ4`fve.hsp}SI|ϧK+aW{\sϴO,>Cؠ ㌒Hکiq%_60{TV%m?&/hW:]ĺaFj}\ bʇ:<%ڳhO :X(SAK!'3UQO8Aގ?u;p"|6JN (FpkR>}ޞCW(4$S U\Jk5JdQLrg;fnyZUJТi59lQݬyTފJ荠|,F7+eūFZs|:OI3z+: D h~o\Z8Ŝ)G%ҡyD(ITSI1GiPR$aʌV^14hlվˋgt+^z%;4+(i#١AP [WWq:RqfUal:Rh[ 2B j fmKRRoy,q ;urcɱXze*LS X'mNwuH2K}Ui,*8$Ub怹/͙jK "+Ӵɗ2I&L)tKh$Jj'0q&yuSJYfڽ-cZֿ:捙Z2blD{4MoNWZF@`՛F9(S=.|g)nj}ZPЙzS)NH²+!6Q}:5feny1mZͫ=YH!^'q,o!<@JwB|c}9jR9gGȌa5LqN: cڼFiz- .e,6ӓэ0h+ 2ϭfJ73#R\"i֗<nr_gL'M , p;Eivߟ9֚cK$gp+WYHZ[i? tp ~=L݉ ӟ$5',JKSLjvQ3F+9Mq9l޽G6)mJgX=}{1HfB0 $RPt۟oQuD͏cN| [BI2 LE̠]Z(N.uI%bNtr0LIi#=*-mSZ<>Kvtv.T|YEJn%eKUz7D2#L^)H0Ƹĉ#'u(>pO;WJ7b/ZQRjJ_7$HcݙH@yͮz4MljAzҰ|f,RTo-V,ـQ0mK銝+*YM$H-Aaliϝ(Iwrιd-Rf&0&4S٫92ky4Ȍ)WLw3ee)EɍY/mZD (!"12A #@B04{ ` -T*S.*"W(I8zTyF}a25ֵۋOV7xi nTGlpx.j+q}{!>k5"/ƽ>s>S'3i~&N/#+P;MoiQLgp0c˹4]{i9 Er+~X)R?(A =Fhzza%­i"؈ϰm.4#r%FfG&a/ hO;d6jD qDX`a.`| P(J{_AB"K Cz Ԏa$Oq-@@h0N4ODK:eb_B+*`}2]N> Z/Ʌ/[ba*epyMyjE^+K X 5肛Z͍1-<1C[\RD==Vn`$Xͅ,rKXtY+.\j 5 CJ-;u9Ròo lR 2O]A1 vcM~T9:|T1 ,"|Ve\ 'V޲+^L,N7xsV ^V`!EP4*0k,-GJ>aف@DmB,Aa֍L_"!tLKѠ=1Ńc3mZ%™ qZr+#S* .!)f(S C bD/OQp!B*3jXCX? J_.oS'*le`cGЃ2-}T«|O}b{C9,%'Zh*\)&vʰl'fHlYTqy Կ?s7|_={Bg`U"eƋsuԣxS9g'pByߝ!~v3fz\pahxLT#ؽ7gR+G&1-+FxNF*@&GT{K/̫G'(p'P,i4ŏ@AÈO+Vdfx؄"K z*5 @nB\K.peE0yJ-1~6Hߏ]Uq'ZY2Pn!(X09=N6.L\[Lְ'"_p'j`-=o(EZp41n==C[|D-ُT*'h@rl"}J>vn Ms>P_YJ>gi&P/&]eTc鮌 Ģ'<4+q X*0'FH=cC#پGYF+6F\ٚ4X8ȪAcT"lGToUjW5+.|FۛouWJ1 MYeL b\\"0 @!1APap?ckYxhMQ CnCl"A(tgx*tnh@pq%(ES5@& f _y5Fu aG6XO P1% 01!A@a23Q?\^bw>>,pPg,E1?J=ct,qGe#be\='EbkZ֪ZP͕>Ҋ/^2PQz6v" Qҋ϶ҡr8jno1y*h P25BZtYZℎYpq;1š+[Q+3>p!4vVeNéovcٓo8 -1t4.!qE kɟb~+Qb̻F]y/~xee ..}mGemc*~QJQRr Kzħ1pPzgFB0!1A "Qaq02@BR3?ս3+CED%RhgKbSky͗w30(w/(ܙ=N(j.-ܶ d~ Rfܗ"!\Z]1,OJJ}Hni?dϽ#R\?)`Թvis&qN(b7p$~M=?ܰܽ'sԻĝD}/%";z]*M?K-׉'"D&].▫Ú\>&yJ|TRsLd峭mHj\bCW, R|ґ*9Xd1K:[2ϸv-TjNS7.Θ#T-c GqԓzІ6>ئ |14u+7##Q+X1YlYl'O 䑭^Itǹv._7kt#q\-CNɚe8T<4,n 7Ů. | 2^feoLQO%56y:Qܱ^D<" "X&"Kn7?LgGFIȃ6,ŧR&+4SԤ.=w;3) v0Yf m?3)ht}0lV/sMm)sZLz䉒H%m9;c?; M=)wȑOsX;riD} ,͋Lnb& [M:=JW.j,KL3IZe6#Jެ &֕918[e5R%HJdtJ/sXDRy,8^,_VXg:t^i-WBû-fft!ZfL44شyMqIx/z.OJ*`TB{iPR>&)SE-`kS>ɎQ)Wr "NJq$CQ{K:D$ZIhK#4ܖHdA= <nj|)ViǓ%/%3ra_562En@'R B,jҢ 33m8Z6 Nbi\2]fLrD/'cNX2zՉ߻vZt؍ӃK$q_#83LxڊD*OqR0sױ :tF$Syd}M{٧^a.v^v*K==:FՐ2Yv3O5"ۗ_-ɞK6:%=.un)si<'*j۞XLti0˖OV?.CçjJ_y0l]޻$B/KQ:`Y1OQI7,]ߖH-"dOsσ=2]KR6/bTܰ2ڐסtKD[ddRru2DQK1b2i֤EdM/UE)!1AQaq @0?! _ث? nĮ|b.[N]u? <P:h55p%"fk陮r:"2rs0c~ LWyg,=xeY:\Җ߰J؝T5( SMkP3uB|g? T^)s-^L1 +Gԝ{Nr yʖP{e'q*20yD Xsf"hiJ #^8NI*i2|(;8~⠇.ʎc;ʮ7Ami[1_p5BSdE >C2&X0z*bo hb뙶a)AK &­X} ]W'0)]Sk˲DqR =dy+>?l2 vfU{\6Q|ZFh{So)qWls}qYoz3a^o@︉eKЍ*}QA>[AIV{pGE'% ZT(;b/ Rκ@0O:%_,(AwA ORieY Zr̡łb? m0VcMaQJ'ob@`߰ALe\?1DSZK/dPCf_s&VHLsNe bޫ#`Plkr:"#OL`VV/rը)EꥵA\CX7|J2,`μEep>`z>DzMrb 0SY *yKRNYt,o17r"+GSYkA^M*R+hrFnr<QQAɴ[nˈa +oւ-ivEnc PY@L9횲VjeUp+[5G<` *Ύ|(nA ҥlwp'JAʳ h^CܥE *cxULܺ Waݒ+H0 <-V[S2)jPBR[u2dzqڔ]JC*ƀZĵ\F.a<8L ˨uX; ̀f- u)%4{gÀp%Y,Јk2/:sW1!y7PQ,ĽUE¸vT VTJ_HqXn+Q:XPi9f=TVRNvGU`(:>e^ggŮs;oy" 1v\k9^Wk"6g>*cJWwŌu1#Vm찋s73W6*、[}e<4Ӧn6JrsJe2{Bʨ*R(pKa5W9\^ظSq+)k:T?b7˪͜¦̡]K8pJcQamt4qa`aE>jbZI(Z!N eȀ(g#HQ^oDubQʋ@ږnc.Yj]]WVsq'EiYXqD(\*@l`+"LK8Kr&wa%.<1C<-])Q,\Uٸhf% ;|$?K #,YQ䍚5Gpp0KW3b.w2oRWFk:,o Mէ( kO3g,GN78 TEbb+w,?2k#unSc6J쮩}1;&?/-6M Ԡ`8oJ2ɉYgFj˓$pE[m]2Tڿ\KZ?#ōd })M5TK3IY~%` EVn|䴏oVN]e|QK{Zl_u|J;uC?63!Zԩ?Q8h՘#)Yd91:iVp¶hq\j j+a}!v?Rͯ,G#[bW^)9f+ڇ˨&"ưun@r+ N9M~cHQg'p?юor#(T_XVoN8M=je/,n\(%ʞ!EN&c_&Jajpfu vgJh1eJ5Qu-H k /Xknq/,4Rj6u<1q,|Lt˕34^َQyJ7'Uf}?+2Qҏ;ŷ3 k]R`l[Ը( {.{x#]krj-y`TU_y*n)|J_Xipl^.a5l6RW@moT}p WQ"C»moR+wq}/p.~%7Q bcܷrXD L`_;^PŲapkaĢd䆬cQ(W*b7<͉z~ q&-7:*%`WuluGvKf, ]4 W|GCeW9jh8Ek=T_-4w,A|JbtA̪yK2[Z,|K)qsI i,ЁCj< "ZӸQgS_ Y瘟lq ln~`.GәaԳe/;Yw.dω0wiF7+s?,3AXS 9>%%N/k9`& ]kš:V, BAmeRWvReM>V'9B}\E]8ao^aFYXb4o*o]*U6IA[3P(aRj5q_r:1 bs?,1.NxKn]/NL6"T*,yN="XЃSf%Z\W-1qnW"'j% 0+`q,-sN8Lc}b0J}%LX}@+طw׈`M2BSqlarbUWkj+]fA Jbfc>%*B7a]KO"/P-*n1Mz'Q9m?Qf4KPo,8fQ >S!i+|-n6uԩq-!.ͶZxxJBð3Iܠ|!sq ,ogȣG4 m_XiJm%YwU\Cˉ+g#6rK2cpyEA, ˄W@ L ]B 6T^^P PLE,LbaV,"o)uW-+B<,W/ƂᇽJٟ `RRqSLc} ϲPLqU(کMb@,M]VU.26qZ.kOL SlV18.,_L8Uk[̖&Q+n1ݴ{+78`vbb0q5E 5; JH~FtfxiU\FKW(iT i 7;pER4S v\ ̱-dLÇ@x S>E4cq|jد3G78}f$(hf|MVD-|le'ss!I:IJrSq+Csԃ25mɗuUӲ_6ΝgS ˶wC/ AlqbiA¶s5\Uޥch5J3{q/cxCd=pA EՒdWp l+|8ATqx/u/HLJeF\9zz0 "ʆqKi%P2Vep}Kjt#ڽJS$huJSsrhǨG=@W%>GaVaU D37^5;kQ#GnVʯ"wr~"ihxbmP&8) 7S ~sƕv7>9]ګ9 lx UX/0P3PUێL u*7cGPoG#`/oS.sL v1+epm\K4*mX%P&lb%kr6{ `f)M^1UaRux.25xNк]G"wt<-~c]4|~24/U) BfcEK[OlB+lF SL] r@. .TʀLw*mM ̳y^(xƠ S#әR&UW9Zab#p!]!ot'1Q ݧő*?w\NF#?\*g.; ^/ ICN]\ `\~h9G?} bu/Q&M*Z-st @z9 EmԄǀ^~f6АC"`1V!ws8} 6Q|:.i ~?OD0 L"TdTr I:ㄞN8Kw/lU{4IrARav'7)5;L8'HAYܸ)0q$7S75+0&.Aٰ..Dᙺ {<)OIOU &\WPh.('b!zOC& A9AbbD!\_G(Bv5C<[,YPĵek` ⵍb 18]z4D_ E[6A~)k$LZ7Z>@NYO DC^(Rk,Q ,˲dSw)~J NP|_RBeҹ } 1(d'O-ǗwI5=; _ݗ.*CNakg8ApZ͂I#lA{Ev,Xq(N\GҞm%& ybD߄zwgcv'P1d;c ξ% sx=ToO {;='eEtHR1p|!1AQ 0aq?I)/g:;OmM?OA.r\|,svgV;"YXoK,(B}A'189$2u%継4`dѐC1Ld24Je/'?>K˼ o?)Kt8%|/rv;9!̆y-e!y&>Ƥk |e3H>7ݒGL/aAñ{Ζv;=e!+~2Xre|ed?[vYB&cf 8Besgۖt nCScyߙg,A?8l'n# υ:dm)o?7|xy>g~|t`-8Bm]~ee.D2BoFi'&2Ą{%m_܄Xu$ۄsǧάu6u3)jߙ&X5geفfs #2W u*bxXo'N0C=zPl_s|!1v}O"Ou,~Y+x6 NǙ&vχd|2 O-O[><#Dcٳ2?1m^ ϟrb6sϡYfA߃0Y`N`dϛ밃&<>mL%~7C~)C08<>WBE }+X#G w5BL2dɓ&L1111111 ]B%+Wk:}a '!S5GCU)9|g .!j~]@Bp[}5BӭYi/|sp)1XSWh&L2dɓ&LLLLLLnPj 4:q3x&ˌmucU" wxbsx Ǐ0蒸Њ¼kX`U0F!bz΍L>./1I!₟ydkL:l"9 "M'qDpR'6RLTЅc#:Āe009w'!y"NaA> [1|ɓ&L2dɉ|(&m/eR@A[^Me}(=-pϭx:3SIx%3xP@ٿxS0zV:p('Ӄ{Ty/%CXx0k ?P ~H;(4kR9a OiEJn*y |$=OYN:#E FML)ju}8b? )2nEr Ѯ 8Tt|1;s0Ȁyх"bTٳ4BԌcn"{>p0+s5PxɓRGa\z.,S90e흾s^V)ہQ5 Vh>FϷLa@ Oy2z:}沖 1dRLOoxdÿy<:t P u5Ap*F/;Q&\GrRi_FU ;of3IDq12{v q-C|-Ӱ>ydU6T}ܢH-(:a/xe(MqzX|lxMi cx+fqD#`>EbGL& QQ-px0(.R_! $¿eIbk&6'?oek`p)(qH;1'pgX%V5@Ŝ4pHic0 'e05 ҏ?X {9`W尌Ku(#h/ F$ߦ%4Ql 7D1ZAt"Mq:9≍^۔]" ugX"pz}9fA"}aȰ\cxN:xyʂ yW!_h[.1Zu=f4Wp^JW[VLxrvos^=\#78 1_i2 Tc@ty5{*[; C]++ax/)w!\󿃬]` VuxtQ(u!ҍSӈz8G#F&qy3@|}%Ϝ{E;Ŀ]3y]b,'}Ix2@KdHU?кV|c)e0;be +CxuxQbPg -]`#64cf$\U& 5ɚktSJs9vLHCvU^dLTd{ DMjD)޹U`Laڳ"]j0RHo[ Ghhx4z!<1w_EAo!^0B+U 8_wz@  X9dѣH'9<`~=e!| uw--(ɳZ0"X߬lCn+h%ٺs@{ּЀ|ETzTi>'B́u? 7Xelnna,/ےJ]>q(.&оsCI<>n(Fcu1W&6w\4( ,/5~ M_}ڴr֑', vYY4|U*|(yw?ZL|o8o5n}bdJ͡"*z"[8b78q #2L ii4p-tQ8n؟r1/v^STzvvMx`E7 ukR 6#mU.CVol\+c"_!)WE~`D\Zt? & ̸=a`49ۅYt[k7 YIPߜ+k 噱~sBٷ 9(%6{mCћ-/;2kª9?>qdI@Wxr}EOn4S>n- 횠[HM) D#gX#)cGX}NJ!񓠏E)q7Cv^4ZbA&\m7E 8*f4er_<}7;2E9ӀQӉ UJ c[pޕ{[VH A6WDߟ d0q}_ &Ô}#V|ioƃb"pKN%';] y6drлz:=#ɻ. 3Tta_P{䓣 +} lr1~@ᇳό녎wAGܱNI"@dx(G5/}@pKz]7gW5ѽt9oأ cژ NYTlS7gK{ߓd(89 m}G/<bCA%a_YM]`Ѵbac/427<6Ox2[ko$DӲǬ`4/1YBcJt8]}`"[C`zb;*Ϭ+9 PC 7_7*D3YɛK\9疌P8/񛊇 כ`}lX zqm 4Gxȉhlpf !2`Vz6Sr6Z*UqVT}\r)h=bF8\WCܿ tF[֎ΑQL#1t$}7mA%Cd#A5sHmfHn@63p +6vcƨ˲-"P>ma٨G}`'HvQk;;-C͆o]y '($nƸ)Dވ[M_'^q9 u g~hO.d@Jե=*NFl$O|4W\n] 13CNƟ i9e(P !z ;P}ÇpwֱE#!7B&szӖNTk *I6cQW Q{T$?XMG@.E3Jy\vx{|+GWh/4qĊWJ0ZIa2EJPˆ >ޱ^nLYč񈘌ȷ΀wE+`Yo"7lNYa#G}u 9]^?%ML߲>*œ!jƲ:Qy-@)*E߬ɻyƥγgy7u柂:V:U~pMb9`c@L[o4ֺѿ )z+x0 h,.r?g\n|x-Z|c&EA&N]bPLHS\󬈙+ۇl\ [0wF~<˃5(sýC5kd((z<;6Կ^0;L0 [.EGTT;qY\`#$J:pc E/>ےҩ> )*Ψ~?lO#DU rR" Bv/Qj2:$#.$Q{Đ>4DJcmT@ތ Os[ɻr&:2` FzDN6Ķ=Z7Ry@!;ݿ0^LT."BF~a/X#5%ؘ^'SUT#0ںg*?;i<8p*}o$ VqX᭟.@_"8`]㬕7D#߮A fSiaJ^qrzs8م m˶9)` ! vqhq/8Dz7 |<ƞ^v@7+?(64gTxĴ:|`. /mL~LD g3Ef MϜe$EJMחKutѧyig O)1C vs=curꚯ@3hqSIwSc.<Ǭ-~>0tͥH ]^ qи{rvE}*h) Pf#o)7e-MTgx{Ys|cC^^>$5}ʢثƋ4DS^3UvLr|P#Qx W{4W?QGxЛatxd. =N>r(P6}tKe(bQI7}5i;"epDRh[r 97ÿX!P^?Q=`P'Z9I&rOHSgrO)u0 !x (G(dYX]ķ^8sSZXHZdEWh]i.!I+=cL̓{ %mpݩv>0,wrR4M \E27_Qyp,0 {hfzq*ܔ,dM>pU)~1W&{r *gҳo],rWﮬW81 m4<9Y7XڇP]gSoŽ#,!͸U_ys$ݸX>5o_*Ih9G~٬$w{a"Prha ,b7Fj\]8N/C&˧lt k. dU嫅1›CSF͸\|Pm]Ǽ7P?Yћ&|C)J=39iF:j:OĠvdQRwlӇnM7LBxnQFl]~-pT'`mVoˀmz| x6?BB+?Wt8`cbIw`aUڻ0V%ߌ nXHm"}y; K#W2Rv%d~q hbuqN2ã,{ 57:cpSęg/1D )mwtljy~]} )*g*" 3ߌR:* xB=ਁE!v+pNV&qW>+9?#eT>ʗW8.Pvx L\ك{/4ow4y,vK @0Q0O;AN Ay{4Fo;A|GJYyplT+ VӟyAK-I`֞2z Ey> 9HTWeO>LDUu` |Rirb10 vy|;>aChGe;~ W

Jʭ>SIiWҌ b?m|:s YVQĀnA!JZMQ9'H 7Y} F̌5;cdv~3 $!tV:#޹0Air0SqIy7ci@u%V;8-s'q4 n,Ԓ-+lPD%NGE& %e=9k2kF mm! :X^((8s,#dǘcsI?3A.2s]ۻpό.qRLKo˩?Pvh?+lr# JĀ>6T"6u捊|]>jCF.r3ړ*܍\CI;T%gplcZDi+O98 z;lXFl=*>W2Ĭ<:տ #ץk'FOK@ΩI]1eRƪ: Kio I|PlkT^tBbY#nZFFuo~md `e :_jRB?ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras4.jpg000066400000000000000000000604461231750357400274350ustar00rootroot00000000000000JFIFHHC     #!)!$%'('+.+&.#&'&C   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& ! &5UaPsAVJ+@E|6Ʋ,:Y 5"YQh((%m5\\PP(!@DF..(("5usThdIJ D`h* s<#VvJ@@DAUijdV-s_^(4DRD,h !fK7; _$:@[`JjA cT^zDoKcNҰҍd@B6EAŠFYcbXQ„𸭹yo.m9WU[+`ywMu@ bbP\\YnKK.T+=N΀@ Ξ\_8Ǥ@A@9QkeFl][9܀e^s?Z:@BA`N3#:%\q]hkd@u#?zKz/N@.gDjO_.lǼ'殚8}lwc\Fnqvwv /sx6WfzLE9bO@,y//S<;ROs-'p*9Pnft.}HcBݯY.Q~Mc+ڲY^ȥ_2ι1KZ%E5CN__OZԸ͝5{-`oQ֖}N9"ik"Gsۙ("He\[*o7sϙLwN$M$s*EB%lvlg"J٥ii jQ{͚9ev^vѲӣ 7.o FJŨA\\trުղ1`+`*T)ۛݛR=9j)HkBԲ bKdA!OE4:{%'==i2cШic<jķQ?>-V"]nJS鍚Zl^z@S)J ldmjG"eu|`tWw/Xk.҈i%5Za +%|lk>6yJ>K;nK;YpmM秸t001*D=!҉pjgAً婍9a#3f8K l6 V͑5lY'>}ggo{Y]=6dk2fz%wη9ӛ˽JloClwùn{w\U^&JESIlF)nGm )[-f~iFL=^Z'#;[bmBYS9քԲ\ъE;] 5d4I^ ˭LJ{Peo:&H[5I5aH8oK3fGʲ<:]>Y$ԧ5}ՃY.[%9܌Y|гtkN=fjO5Od=l>9FCeCFߣpRa]^̥д;enyvnYvn;V֞NڲYtsH/IDӬ%fN%.!"1 2A#0@34$Bp11111鉏\LLLLzbcӉmq1ٕU?NS}˭.D.tnK[?Nff󶜜re[ra'`fh5Ks33333333333333 u;Ez<&JPg3j33?Nffffffff~bc KPuφI :0}$B#NtZ.ىm3d6l&"bs9}9s9sPRjL 9\N/11116Ͱɶu̎8bbbbbmfٶm+:S"\*raS8TxUCz{ ޝat6M/PJHQ^zZݍSKǵTw M̯F&j)[W}™njuJ WVpzkOSԩ뺕E~R^7`S aNY3:=Wzj qIڞrIU;(e?:mg34p;M+[[xlPL_>WFimVmJwjzos\u"hV>Ϭ5iW~koAsh"8}lt'l79ONl鼬U+5XjtM%#]*41͹wZRNٛZm|؇!f0  *b1Fa] F sjUrOh5$ZQmuZ[{6mNĶW$&^_V=,~@ukLm%xFխ٪XhXrⶶXɶMlR24#-Ȱmk~E *3yh@P&ŔY>=2fL꯿PM_!]'af*QnD;IPtMjmwU­irS!Q_fZk6CK0Z/ϯ:CMoRuw0[!,pk"l1[pW1g%會Tpa]߱!f$l;*oRI-g]MV>}wdϣy[$V|C 6Lq,R|/ {p,֎J Q|v)h1G#සs+\Q"ї `',0ˈ>ӛ(2Q˼SO|.Oɛ38'ұ>'> o"F#f>Z0@q1EON':NѨ:R] abK*V-X +ƒɊ' ,*TF5@!BU׸_;yT E_-U J;]:5}WOv`eK`4Í/sD$3aX,xǣ*pCZ7/eb݊5`홥>+yi'/s ȀdbgLGn#2̱VVͻGc/?™H~WueqınՉ%v2b@Y[n8})īk)+Y%'ϩ0Ϳ-9\.+fPܰţsU]YJoGhcl #s+mE=AgoV 6ڳ;bg8ݶ(.Tkxo^#-bȽbZ@7CQhՓ=ֺUZ8[ h8 n;5b۰Mgm#5"=fV۶ygrS m UJ >7"*Ku'ɫeBoHUB5DEQP& ;Z’*D6m{%h,mycj@mk?n;iی{eBGs~6 n f WevV՜}fÐs]n h S(ak)XVrOTJ^Mkݺ gbjʍ/l/jMwr͑(".M~@_fy46ɼWIi[XY1-hk 6EΤ+-} 1FQk`36dS즻Ǿ[DiXqeu%C+7Bb[2J|qbS^|(W"TgJחkewlSޢ=iMZEUz5UFPToj D+[@ZЩm*fJaY|R"!s,SiN s/ -ڟ'0KGwm%4 %v^Si_ԵS{ށ]l+u559IET]l*HʛaFTyT 4U*n-ٱXY\|ܬmMuj W%)}N.>A3IG`agܪl=$+o`)5~Pf1X*VoiK6 Oh!E;8e=Sj*(3h7&ZZu.p{c.*m5-,uFmK<$˜;JqIEkre;dGyaOm+h.2lmSUslvӀnڶY[)nCݕPTmK=nٕcm̓ "%UL ӣ5)RZְ3XQFrc>@0_7\cQjҺDs3Ӎ;,Z% ] Z>  NfUگ0<cs@W- !01@"2AQ#Bp?a7V-HwƗ4&8t4YJ<jtG@]GGS#r[6 O>{&osU!oWѵm0{Pƿ)bp3IL)Ts-KMC>Fs w +~|L)mV*S{D^#݈iaxQx!V6666.'QA7jr(7(m[FA!} / 5(4@Dd# uS(2B B!)5r{06kGlfNnSGܫmH@/BU&!r,T)]i4EL ҂()Qr*(5Fcm- )BkiS\!:؅@*lx(.uDJ'X/)! 1A0"2Qa#@Bp?//R8Ǎ{.YeDoo[H_2[8௎B>W#ضݺD5ɲ$buwI\9~vSJ$O/VrGoH>D]\ hyYCӑu{!/.eOgx#qBy`]d\Eˏ(J-+/#;->keЈv>t>ɺFG,]>2-X? d蛸k$[WRYgi\d0i*Bx5^$d9t[Qde,t3KqrV`D8x<}7COJIi࿸>*/c zTo~eܘFf$mjQQ/kyGvgFVOmYv&QtŴ3DpF3O#K*]dE ͐fK, dZEQ<؄R:DИIQ=OY97?ˡu{5c&7Khtǖ8`I1 K%;!1AQ"a2q #0BR@P$3brp?gGiU1>cub&j7:8Msa >3˧OTHu[)b :8YU5*D:P(IN3m0_ڈKz%xͭqՂOWe# a_vG B#QOU2^#LMIiۄ@7"ʎ,PEq1ѹZ^UMBwDt_ KeE7s2qK|EW SXZPoE`ᙂv3.bUrFVb qg q]JʧITtIQT=ϕUz*ꊎUi*ZF}²TnLW iG4-*ȎFfUJ\r 5]삾U* QyPTsSu:FfH<\bBcLnM;fdJ#"ul.3((iL.SHVMT6CUS|NDDQhvm)SJL.d*N]S(j9B9@4PTe vZ]eSE˔ D.꫘G[)v*YDYp#G#'n /[ P*!QJЉ-)u'eehL벨J#ur6C.iYKm^nM!w^PVE n8)׋N(>TjUʐ(_+ʂ! R (A 1Nahud4RH_f [b#y+謨fv Q=NiDa`V!NKD#6 VDB.d6 Ux\]ERDARtK-M[SSU Y tzf8Z_5BMk>͐.210.uh@12F4WWPelu8yiUkZCт(cZEARA)Neɋàl.R"db JZV*E~KګU,Ĉ҄ΥO &xUϲoZP Ddun?s` nМ\Ik|**C"^..1?>2\)R[d@LRtKEi7MMܔXJIlr\OA-K6( ZJV..!Dg`_~Q8VQW V1GEt_Uк18U$(3 h-"lpЭdKn]OϚg<6"ɽWLe]{(SW/lQv!:vu xDIelFBakDk(_qJ2ďM!YhDm(hGijDqSUEu +K*!S]RO[aPY~ꒆ{*#5(H*^B P*!1AQaq 0@P?!W*WJ+_F1coX*R@o1}K\r.\XesE Wlew.\r.\aV;l(g Mx "紿r\~.\~,aBB/CYf-TYo@ź3ǡ.\rG?b.\ƒE\ =8)߼URɍ8O]]T:&Z:CS1R!w^JJf%eO&R= ED۸1/J0hu({I,{=oo$Y0A w.9*viL@*=J,/䨺кx霿8@SvHBBPw._-n73/RJbP)K.NHh RT[)X< bJ{f-^%';l9E_. iIǟ\=F#&ML *xe٫#u+x9p358FPJ,)8P;AlS wVl9(x;90?0DG WA8tZ=뻄@ YOY|~rɿL͙̒"<T`uEh|4#|˗.\1EiCrplfI <)#T.fGjL[1-huJmDEa{P_;g%fgZf x'Q ^abA".Faco~s9Ksh6ŏ}ϲlc;2Heui*c2vnSNwSU7PkZُ0ubyk6mf8@+hJ0Z:hciCfo0GDa=ۈۛŸj2mLGmep3_p]Y@ qj9ͧX~% ZĤ"Ҵ}uv/]+,Vr>YΧ?g`;H1D`pvͮL_hj'b[9K-AѾ_X\|6S/q" uw8ć y mmϼ& _0v)"Rnls3$YQ@_=Qɖ `l\Vr¾ (ԣ7(aYeS7Regª5s( ]AsZ/Syxy_2m؝y rW \j$c  z*e<2nCZUd:lO3t<\G&\#.eW f͛m- T﹝֠yenXGݎ@cf['p)2 , N[)`k!97N)q6#\ ɹR+mmwpb㵫3h،|%Ь=Fh%4Qm- T_VFDuAV'oZo'){z Y[A!n .z`ld*S/U}@>nY}^C+?h'K#Zx+e2y5&2P0Дpf@ rB'=pymޘ4^%F"J`^,&p**Y1V͡/"|Q%D:?rBT18U ½ޮ(3l0FsKܑCɀj[OB}G@$ɹDR߁J(7P\\|S [FWN* 徧-1ơtZ6删/o`({!p788y0M..wn"Yw/XHVG>%ODU$+ψ8fpbΗVufq/n,Dz8M`ʀ-{L]Ai|Qy)x!/_)ݏyxmJV>-e fitn6Ǝڡ/-qep|~Rvט5in LIi"p.Q$)Z<0e5O S<5Er߀+QqX`D29aqVݣ̾)q8 (TQ 2V齒حa`0r]rV17 9+x3AqXg%)PV{_@(>EKDҗ|+9%-DMS-L5q^^]{!]2|M~HAtC^w+b iStqqa"p@pф0,81PNxmA@YaNT9䀶aD-ih,[e9"Y'ȆImz:N im8MO6AG0p/@2XeF_ YjNVKC.gSS_8*z.615QfoiX&`# @X&иͧġAVS +\ [RSLЊTk3t #<:6eXf̳>pC`P3,̲8b +):~AmQ]i-jI>Y \qMu*ߐHK1I1_|]QWR0އ$l>H&SB%rXRPjbQ6ֽ26**61%XY3NƼKIT#$Ph5(\Ѫht%:\` fA򘁰A Qpoq+H?qHѼQg f? "΅1Rm|W)z s? |YJĉNd jf¦ȃ<̀dB ̨.yiȈN# spJڑjřs,+(9s _JA.i̍5fL`?eȩɆH%D ̅/K h2MU 2axkbATV;'fRI0, Օ3D9⷏4,BQw\e/wS)fjʬc=կ×Hmg)jPڹYq+Zl=>iu~U O/tA4I_ gzkslO+Qs7cư}H>H,ZV2?NEWE}<^*1T#ݼG)!1 AQaq0@с`?++LW^Cpa/TRO^jV*|*T!.0Ð*eK/pxw!j9_Be-{*=J·kQePE=}@ AL|i>.\3[a.{6>( oچ/T0\ZkY?2&YgQWwV yTx<=sa]#P[}¢$=N0bW[D|"hZctJ z;_;r5&3mOfcߍVo'!=F001$vC0w9'qǬc F={ϼ0J=D</u72Mʂq9g &pن0ڂ4ܺ'7ļUpi:DiM} %`Ux{.rp&sUe3h=fCk:XNC4V+)K[ aJ*&>s׆$ Hv?%^Ccȕ\c,EW eaC=Ÿ1*P˦$|%Otc) % 21#+QpJn !ȖBm=J6ȷQ EXifTZVV.Q!umpJ$ $k %anwx;R8bΧRKC.p-WK%ʬ.i?#!1A Qa0q@?|meضmlI3 /!6ݞ#or[>C |ͯqmGe}^e'S3nA$ ~\d!|ׄϲ1ߢiɟ~]#0bgr?/P`Fެ9-͘MǢm06 -a$gvos{#搹[|a. gCe >/Dua>\]_!̀yleDsi۷M;pن}_ M${K;jvAa}]6or??/BoK>dAKkCdm&/D_%< ˖/m ɂycϸr!#=2`H5u"6z- H24́`Xsb'IL~O[`17.ace7~\?^t '#NϬ钃؏ŋKA[ [!X>{yz _VOq%'12 'q1䷖0܄myevoaof.a;#nrqq] n%鳗BR#@w%/N݅a'lblY?{_bڅˆo~il[%Kn>PvFXu :L9gvK Ρ[iZ)qOrdʁ3sx̃ذK~_#Ɛf[qo!$l{$Ceo(!1AQaq 0?-fOI{s1b]8Ж7jV7(Fr@Xd lV5xBpԡD.`F T} \\r˗_UjT%KH̩U~\b8J ~~a,aL(DeNAn%;q*0Ptz?\Yqb˗#2_5 & -㩉[Kq _t#6*eȂqJh7+}u_ŋ&g(a_B~ 9 }O!K" ^S=Qeaj9U(W-FVo~%ՠ0־Ҏd74n0Hó>5a啈10qE0}E%˗. ] ~"9H<ӏSq@Ust.l1m;;JKѡwpXQb6'cV0I1.k/SxmKE (P,O ]T  *^(%Kq}|u0ീP!p35a(juз P#XzֈD1-D [>p@[[C:AtC 0Y"ܐyb pnWJ&]C .igx`gS؞"Ȑ"nNX_}ĂeX*&=f>$0K1>&~W#uX.(sEeKw<@7 ]~! ^7 zCCm1.bVQO){>%%>ʎ瀁pQԣGRJ:&Z(!T:h3eH2 ŶXwp!:Jt,(S]70 n7)AKg>ryrs:[Tbao{~'K)gtavw}q* 0Mj,Ye ,% 8vҳXضQ &ղ>z|J.s%7Y_D9~89jDɘ@sϨULХɚ1Ԥ/3WeSֺr|eUU6"l 9uUځ[w T] g߼Õ jaX(((crP*(My\\p y-lF4=)kO*UC_,/vycN :%Ytѡ}J|GQ_eN!D4ﳹb\5WR{`y(_~,2F+vA27%k'W`4°U6̤q0 ^"-Ģ(y\1TX9 d03R [D3Ld=uJP  XF oaN#( XU,".,#kO0 ~ (%36b iN2X84BD*¾ ,&E+u RFS~eQ/_ .MC"U|A.cEv(>0m~!G',ku"6*q%Ee&U@//FBcJQ Q_"vǼ@V7GE\n8̻l a͆PǀO@ͅ3k/&6V(7 vVh!RHGv2/mpY4~H H "*.[U0*5Jj9|B9㲺L͂|Ժ=[5`߭eTauz! "A9Q,>b66 8liܤ!9ӉF[f,J-ZS)hf,!GxK4||"F*WhppBÓq1MڼcX,()K*%.B>Lr`55^ԇ0=]s^`&*R\CZeF^ cN_J1`] f:Y ߶bd4|EjRc>噩FggJW LnڋYUV+-pjmg!tD`/KKraP^\ l/R.r|J]N^å[]BҠ/ B5#n_1X_(,,Bv~G*ٞWk|Do+KB3$pUu]g/( k[ o;KTcC :Iѭa9*QǨo!8um(UI*K %v@ ɺ+„(ýz{,:#,7og!Q1F5_b"nh\d }R+;wz#pPup,2yp9*a"+~dsG Ư@q˸ȧ1ۼ+Pc4^f)^{ 0v x%iKU6.1[+V0OKŽr:sCMo2`4 aҳng&`t7*2Бψ>.ӛ&-lzf@s 廒Iܵ햣u`[2kw~sVeTaSO,PBy٘&~řJEplKcDV{0*X- gi6 0?heKvmkv]k5Ф|ղ4VDh ,Zo6 ~& 4޲flN.8L~1nNb;K,7ɟ1ObiZ9~|4UAxay⠷{~XR%t @VCᅢ! UDd Wdn+Z`bUԻtdV YYATnsOu Kp- $p6-'NkZGgۖ5bv}׈_Mna֥@ WZuZҘ5 @}BgT1¡m7+Z6AQeR#kZR6hO̥E@wUYW&!JlZe5IVs\;qpc &,оd&i#՟Ԡ*QNzw|uWEdkR:Zo̽ \ӮsifdbstM z6 xJ姪0 mz=~. }@y ռR( 02ȣk19P2ɨxiM׊W5k"]p^4s(kΦEo0AG8 PMv5xw@*I NPKnzft`) 8HSʾ܏}T=/t58ly]`wjqg<^83y0'h7tcFMU h#(f~4d7;_)| 6ݺ;0eb "nMdCqj!?2xU,t92  WVX/Xɔ|9/_n L!Ee"È(Dl2 ;Jk8ϧSJ!;l Q,4╱&.p!è@ s<6JفkD|'kC^?UF\ Ҝ9`o T doo\8 wfϬ.fq1xO$4bCn.ɯ#b+ur c \oY$SB?v\γ*ܭ-qU@4WOB2W,/Ѻj9q\ZyRE}0Cm^.,ayYgAͽEh UJ.fߚvzPkL\ K:@M4]F͜JmDxb35b,pXD l48hqVuPm|?jkQ*]AdVFbY7KPA`(+ƣpEQ& ݝ8;l\,2zV@}}2&h1CUEk)=ے >,yQ1i}+u }UmW^j;"Ek`kE=Pڧ7[KS =81Y;WuPicĪԺC Ubג"il-I v^ Uvl:j$Lb<Ūh̓m9o&gܫ *<̳t[Kꨔwr؃\@TƳ(hV/Ħ*J1O{aGmU[UlBSGS,l+(C.B1~;%2u/S Zbeh5*/wp8:i7]ħHrp1xk$O^u01 zB*p-fpnqBŶlv\}U]멎^^jS<5eQP+,#ADIf@D6]FRq_[(0_C윊Qk6+WX& !emy")Xvx^a@y6h-BV-`BըDqtБh)_C* o )CbyH3NagUh5q(׌,,[cX׾ ,е ]Z@.Q|>uBlLkA%ıEKSGtR>+޸AOqJp#D^8M‚{Tb +v Wܭu۞ MFB޼h ұhj,eĭ b{9B9l^}FHk2٬`f!>8$*Uu"*:B|̙Ǟ.SlHKavFW%+TxBNV'M%>4N}N4[h=L6^\qZ7D)]@0*h>BcMe|[j:2Ώ842ohE--c(Tnqb[7"v@UsyR_]ꈥ3Z4XU= _;SM9`Kv )1-514SN73b2YPoEV3Ga?*$Y&y,7R{ePrwTk(y#~1nWT^^e1p %{_RR;S0*]~!:xp|UBՍ;fAqLƍ-z-@<2n~%AWV>OK1\mA] YLO1Q j"*.Oյ腪,f]X'|Dc0pw*[ ߩ^A?0tlh ..~ heK>)*0)=qeakFD,iT`YUJ=ܨfH ׉?{!-Ul:~FfoZJ4#*P6nUŶCͅWEy;l9}y5zp):/fu+]M ģL\1UFXMc C"A*u~3d3yc z~PmurЌB,[[sEc]6]2 RPšb;?\zJKXKfz-˂ֽ@(yJx'՟y*RjupԂ}=BXWظoIǬganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras4_min.jpg000066400000000000000000000047551231750357400303010ustar00rootroot00000000000000JFIFHHC     #!)!$%'('+.+&.#&'&C   &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&H`!8!1A"Q2aq#BRSCb*!1AQ"a2B ?PFqק[}0NCBEΡbXB$DL$E7|w;b C)L#U2$I;4.I6 ~ζI!ri1CH$S E7Z3s JkOKȿ5=pXޡF-ZͰ0᦮X,DP-T|IQ\+~X voT]=OSjak}V20jI&i)KlGN)=2eZ% }Jdq z_VuDZDy#HEc :ts[kI!צBg_WQTAHkب!o|i R$fX7Y V" QXon=oe1y$#Q9u!71 uIpq1i,۟_!ʲڣOf"|QF9a'aJ49mU-DTNe%{\ߌ"!1Ҽ3NzP|b9|7=ؾ{%cӽcE;3GM9j'bE$ࣺ}=][Q~h*#},(Gɉ@}qe9TϻQ,RHeam7by|Bys(_ 2xl)WTӭA.)/ ܶ5^qHV@~'TbeOSSЅQVB7;0~ϫ71e3?/@`0:1pRYe fĐ[^bL V<pQf뮱s s\txv$ossTI4Q<:8e16V<'˹OG>|'ն4,9$,gh Oe_ NcYp]m~J80.EXb$v? Խ~IӜ(Q)|2c{cweK,BpO 𢡊gQX4%\luoWGHTÙf9H;^ہ e$}m`/Φ*X"8&Fe.hʤ`.ه77pR}q\b@F \%|2ҁnO%}p wXy kD=L |ֶ`}\M+iVɳ)E]<[}vp: ynM<@Cц^ة$N LK6-JPoM49MbIYk "0;ۃl9ӰΧĐI7?ui|eWH ?*ukqbeN$T}D0Oe.!9Sj*7;\4$s|x\eKVx+t&ZW4zۍ0"T!CJts~&'ؚz:m[rGnbesw<[K]Jg]4s-#m̢S}㊳3W/:!1΢@^ݱ|`^~,+&e'Ϧ feԓ41SeU7{Ú JTR>䌎yig/xV2$$Z %ts:bk8d o;ok۞0<\ν4+DT<8K`Wo]Te{{RK k+ckA|hƔ[R^֗ܶAƓiIRuM62LPxf`<ʷێ8TMPMB,[b_opU<"Q)+['팮~˒+R L/LK1#}MN]UqCz_[ -'~ Z47.sΜvԐ!X.F=g9 !f-{Ϯ*&NꎦtyN&Voks,hie@"$IˈhVmGL%6&UѨ֩dw+pI'Qganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/images/high_tatras_min.jpg000066400000000000000000000041431231750357400302040ustar00rootroot00000000000000JFIFHHC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222H`!4!1Q"Aa2q$BR#!1A"#Qa2 ?R6ZW6r+.J(I2pr?4$HWvH=ϧV<^"څX|~RFg#GiyAK6na`It-\3ViUen;\Lqb~!Nj=-]|(>-hM]+I_dXՌ *Y:V\41^$?gxqEj{Vz5]p1bY\ |s2'TzFkEKY!qIKv8&ǪzT͒9xJ c).|wwNG9% Nڙ2w" 6qGWڧ彨hϖzE~#Nڗ"\\}f4d$x`9^2/՜叿zO*Wц(z)jڵG$Pmb P{"awҢSJESr<&cS.ҤI >AɍYlCnP'(Cy5)F+)W) &B֫3h:TGfu_ +[TdEeeyVebR7280zrx?cHcAǘM[m }Ͷd2&ĐǴ.x'shbݏUPdZKC`-ׁi 2I'$yXyfS'=W y%kyTvC)CR{T8=[8 \Qמj^Fa+JLd~ …AӞƞO یwi6N7ǔNcU H_T$6 kᯩ ϛb>,Gx ^iv$ *iRb۔1۩܄23ڵ-n;rs)6&#\|}$t#NiwL/g&I۴(N#V,$ [#A^ &Dx޸?qCе[}KG{[R0u=ǽ*QQhi-HaO"ssh|TznNsum8Ȝi ?Ļ-t,vǥi ~S ytaْ1jz~Sg t@؄d3`*y?kcrQÏ CuQ)n1jqo,kvX['Ң{,6 0#O|!kpv2g=+,uI.idu Ѓ5x7&5޻& U˒wna&F) 0-h.OE;✝-Zڅr/u4ErOH \n:v$/xp|?h֊ecʿ$g" 1&q.$SW- LaV8+ Ω$s%MRq;Be-]K2*쏛ΧjFab> Z!eV;Q֗vsj,"r؟ڣ 'fkk?Gi&8LdznIP ʧ5Km-l#ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/index.html000066400000000000000000000011031231750357400250630ustar00rootroot00000000000000 jQuery UI Droppable Demos

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/photo-manager.html000066400000000000000000000152241231750357400265260ustar00rootroot00000000000000 jQuery UI Droppable - Simple photo manager

Trash Trash

You can delete an image either by dragging it to the Trash or by clicking the trash icon.

You can "recycle" an image by dragging it back to the gallery or by clicking the recycle icon.

You can view larger image by clicking the zoom icon. jQuery UI dialog widget is used for the modal window.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/propagation.html000066400000000000000000000044261231750357400263120ustar00rootroot00000000000000 jQuery UI Droppable - Prevent propagation

Drag me to my target

Outer droppable

Inner droppable (not greedy)

Outer droppable

Inner droppable (greedy)

When working with nested droppables — for example, you may have an editable directory structure displayed as a tree, with folder and document nodes — the greedy option set to true prevents event propagation when a draggable is dropped on a child node (droppable).

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/revert.html000066400000000000000000000031711231750357400252720ustar00rootroot00000000000000 jQuery UI Droppable - Revert draggable position

I revert when I'm dropped

I revert when I'm not dropped

Drop me here

Return the draggable (or it's helper) to its original location when dragging stops with the boolean revert option set on the draggable.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/shopping-cart.html000066400000000000000000000051261231750357400265430ustar00rootroot00000000000000 jQuery UI Droppable - Shopping Cart Demo

Products

T-Shirts

  • Lolcat Shirt
  • Cheezeburger Shirt
  • Buckit Shirt

Bags

  • Zebra Striped
  • Black Leather
  • Alligator Leather

Gadgets

  • iPhone
  • iPod
  • iPad

Shopping Cart

  1. Add your items here

Demonstrate how to use an accordion to structure products into a catalog and make use of drag and drop for adding them to a shopping cart, where they are sortable.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/droppable/visual-feedback.html000066400000000000000000000037701231750357400270150ustar00rootroot00000000000000 jQuery UI Droppable - Visual feedback

Feedback on hover:

Drag me to my target

Drop here

Feedback on activating draggable:

Drag me to my target

Drop here

Change the droppable's appearance on hover, or when the droppable is active (an acceptable draggable is dropped on it). Use the hoverClass or activeClass options to specify respective classes.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/effect/000077500000000000000000000000001231750357400223575ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/effect/default.html000066400000000000000000000067641231750357400247060ustar00rootroot00000000000000 jQuery UI Effects - Effect demo

Effect

Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede. Nulla lorem metus, adipiscing ut, luctus sed, hendrerit vitae, mi.

Run Effect

Click the button above to show the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/effect/easing.html000066400000000000000000000053501231750357400245160ustar00rootroot00000000000000 jQuery UI Effects - Easing demo

All easings provided by jQuery UI are drawn above, using a HTML canvas element. Click a diagram to see the easing in action.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/effect/index.html000066400000000000000000000003751231750357400243610ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/hide/000077500000000000000000000000001231750357400220345ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/hide/default.html000066400000000000000000000061501231750357400243500ustar00rootroot00000000000000 jQuery UI Effects - Hide Demo

Hide

Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede. Nulla lorem metus, adipiscing ut, luctus sed, hendrerit vitae, mi.

Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/hide/index.html000066400000000000000000000003171231750357400240320ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/000077500000000000000000000000001231750357400223705ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/calendar.gif000066400000000000000000000004151231750357400246300ustar00rootroot00000000000000GIF89a9qZ1ise^c\mesl^Yzs[W}wSQ!,e5<Ҿn1q]UEQR=d[nI)*u/8p8kH ]H @w@b~Chff]nprxX^`Gi`G !;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/demo-config-on-tile.gif000066400000000000000000000002541231750357400266140ustar00rootroot00000000000000GIF87ad.#'%84-<=!60:+)?2,d1 $dih$4E,E x @PШtJZ4;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/demo-config-on.gif000066400000000000000000000005171231750357400256630ustar00rootroot00000000000000GIF87a d)-02+6)48':%#<ߴ۰޴Z=N=!PA?8ߵs6;u., d@t@pH,Ȥ8ˤPZجvK(͇@.sz>(CSa>/PIH!m"F DB$ B#D G'YA;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/demo-spindown-closed.gif000066400000000000000000000001471231750357400271130ustar00rootroot00000000000000GIF89a }}}! ,@ EU$؛F WqP;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/demo-spindown-open.gif000066400000000000000000000001511231750357400265760ustar00rootroot00000000000000GIF89a ξ}}}! ,9BR1$B!;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/icon-docs-info.gif000066400000000000000000000003161231750357400256660ustar00rootroot00000000000000GIF87a , S`'vJX(r5[UtRB**ċRp T$\f+n ETFѸ5@`LC3#!;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/images/pbar-ani.gif000066400000000000000000000174421231750357400245600ustar00rootroot00000000000000GIF89a0JΔŔŌsskRkcJBJB֜! NETSCAPE2.0! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP !,0pIͻ"di"l)k1|+plJ cպfxL./f϶q} zy| ¶žȝʰ!,0ЉAǻc di(il0߱| n?!gD:KbyTFaSj" xL.^6|N|q} z{~ øƢɩ!,0I18 AHihZb뾰H8l$|@-Ga1C:bTFuTfxL.ޯ/|nj{N8~ {|w} ý Űȶˤ!,0I)8k !bh辰[8l$|@-G$aC:{eIJ_MjjxL.ޯ/|nj{N8~ {}w ľ Ʊɷ̦!,0ĸ8}ChAplͺrI`+x¤h!a*SXպPCaL.-'Fu{~3 }y ¾ ǻɹ̲!,09Ÿ8}`(a h,jp,Kﳑp&RiD|(Ij z]fK4z-&{p ~ ~~y #  ĴǷž ˳г!,0I8qx`("aHlf,7\ﴑpqI4"wJt80 }} { b z ®˽ɸ ϱԭ!,0I#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c þȺƴ ̯!,0I#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c { ɼǶ ͯ!,0Ik#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c ʻȶ !,0Iubͻ#dYl뾫tm*ۆpH,2ul6K_IBMNi<.nft } }d ­˼ɷ ϰ!,00ISͻ!dil)kI`/H$HK:Kf|F#j5ixxpPnzng`Mo㾮x/> ~~|` ­˼ɷ ϰҿ!,0pઽ8W!DihZHp<4ʸ\六p#v>h<ږ`әJFSj5 ~~|a ­˼ɷ ϰҽ!,0p8k+`"AhAp,&kI}pH,{d&˦t{Si՚fX\(xFCyng4Qrax } }c ­˼ɷ ϰһ!,0p8k+`"AhAp,&kIRVoH,{plJs:FUL*R xL.&.u{~w~ {{}y y; Ǹų ˬ !,0p)8}`(bh,jp,ޭ1{ظG,>g@:eI%FfzŦ[@.h28Lkqbt80 }}{ {< ÿɺǵ ͮ!,0pI8}`(bh,jp,ޭ1{ظG,>g@:eI%FfzŦ[@.h28|5sa{ 0 }} { z îƶʼ ϱѶ!,0pI .{"dDlf,n]q<@G,ȡqyDLyFZ4xr{^l>xٝ~ wyx Űȸ̾ ѳӶ!,0pI+ .{O($ l)t}oe' n$PHP%ZSjyf+nj(,'>xo/ ~~ | {= þƶɹͿ Ҵ!,0pI .{O($ l)t}oe' n$PHP%ZSjyf+nj(,'>xo/ ~~ | {= þƶɹ ͵Ӵ;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/index.html000066400000000000000000000024071231750357400231230ustar00rootroot00000000000000 jQuery UI Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/menu/000077500000000000000000000000001231750357400220675ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/menu/default.html000066400000000000000000000034071231750357400244050ustar00rootroot00000000000000 jQuery UI Menu - Default functionality

A menu with the default configuration, disabled items and nested menus. A list is transformed, adding theming, mouse and keyboard navigation support. Try to tab to the menu then use the cursor keys to navigate.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/menu/icons.html000066400000000000000000000026711231750357400240760ustar00rootroot00000000000000 jQuery UI Menu - Icons

A menu with the default configuration, showing how to use a menu with icons.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/menu/index.html000066400000000000000000000003651231750357400240700ustar00rootroot00000000000000 jQuery UI Menu Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/000077500000000000000000000000001231750357400227675ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/cycler.html000066400000000000000000000052751231750357400251470ustar00rootroot00000000000000 jQuery UI Position - Image Cycler

A photoviewer prototype using Position to place images at the center, left and right and cycle them.
Use the links at the top to cycle, or click on the images on the left and right.
Note how the images are repositioned when resizing the window.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/default.html000066400000000000000000000064301231750357400253040ustar00rootroot00000000000000 jQuery UI Position - Default functionality

This is the position parent element.

to position

to position 2

position...
my:
at:
collision:

Use the form controls to configure the positioning, or drag the positioned element to modify its offset.
Drag around the parent element to see collision detection in action.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/images/000077500000000000000000000000001231750357400242345ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/images/earth.jpg000066400000000000000000000722321231750357400260470ustar00rootroot00000000000000JFIFLEAD Technologies Inc. V1.01CC4   ( =ǖ5aBz\-La@q;N4]jݶ.ap-ʩgG$?MF+07sct~pbj]^}&gNmBT4c]_48259cʗYY/Sw*ec.&,כֿq|Dzս=x}T17[b\mLG;`ʹt'ҧlUXhy66isOZ%;r-usr䕺\wp:)_>glc::q)Oһ5_k{cKߥ׹Z;2t; 96 }m&cq^HvTs_Gƀx?{z)c}:t>WoZGlnDKԎΣ5͘kS,_^_595Ms 3NaaɬzYϳ_Wk5%|nm2Q{|9deLŪ R.W6yն4h젗IӃ]Ėe}piX=k; 4wQuR~;z.o,^{ߍ8*PPu@(H/ f|@|Z7Zw0uDz5]*Z43T߫TЌZ1WV_Ϛ&rl}4zY/+;jݟ^7F%Pu@}j6YnF9~NtgWyP>=:xjVm.۩biecr~2f⴯|?ݎlۓz uM|3:v>B= B3`U]jM=NjsɡIC/EzMt.iZr#ޛf͞kqxO҉tj»ޅɵr"~6:_G'i8]ŊlvHjf]kM6MXi&.Ύ.Y}ةK}n>ЭY6F[rJ~NdzqtVs9>k|˖uU zlY @?o$2UՑ٥s7mLͶYoKSy)仵;n5$mw>Ľ_7VPIzȯ5E@ 6,P_{;=R:/A%aᑢ+6 QEu4=~~_ubu{|_z]{'ۥxƾ{ϸ]._7n73{3̹=oo g(Web@kuJo]Z1h%}IfEyLd#hNl }#,WVyW.C̐G>4-֌Tca7 ~|X;5>_Z\uN.ɦd7Vcy,xm~--I{Yo8Ï] G_{mNs.K7>ڏVy+= >,V{׭M^aS43Is _~کG, z>tg6ƪH}˸/zL=t -޺cUnfW&A}>%YHyzy[m*YItH9%̚[|ϳ3;}$;p)YicMnփq$C6m-@ S7]侢R j2#σ(6dpћJ&kXme)y}-gs&q(F8 0J˙T y޷mcc3f+7ZwN̬Z 7+ƦS8keåt-0s jic sMU N>lcS&[ S2c570j*ScU+"|cB/ϋPl+/!1@"0AP#2$6`LgLNP[pNXS¸/33zgZV$YwC[.7:t_ƸJ{ 6YӺVI+)fӅ=0SױU Q5kvYABA_wÕlH.$uÒhWh}6\;1}3qm#4J"|vΨ˓bJY5LZlzÂ-.M!*J'pO"Γc yjU 8AZ -[5վ+ na=l10vYcZJ_A+ YʸvÌٟ8/nJY' q3_bȢ@|1nEާ+XjSe,5[&VVuljsngYΓ+;Mjr`@HG55lh-> *&{b#q\PMSk_w =@NZKQZ:)jj|;.n3K4մ_Ie,|W{fؕ|h+꤫!eY>ؒ *m+U.ag#ojFBʪLk*V3z:ܟQzzgN6:v )X~8X*?%4+Z,`Hzc"ecE{Pq%AZ^seWvWUf8Num5V'Ӂb!.\8P*R*UBΒ} qaʱذ6K5ֹ!)3Ue(k5B4mO4Q1} ϷïH4l+lx՚]/k+ +%nek3-Oj.=ZuVmeBiWٹ8'C063Z5yg"aپl%O44xYj`g]hTkykp/ 듾06[3g!86I9M^|ѝِFѨ2fK=)^u2dr͈XgVfwH̽9H6 !1a"@APq #02Q$?NlfG<`ȷ Z=P/?j?ZuDlx<,Q/ר.}Oź䖺Oį%|*im6Or?b|籧%ߟQd6GLͥ&j"䋴zBd?Ȗ =3AR\%pMƒ'R!(Q0k+絕ed# ׂ)J85yze(B\ĕ_^fa!G`zN0l؆iQ";hS:ئy/ 53T4ᕷI־CG"`sXCx"2v+lR 6%~G4?3ŨvGFKŗ (L4XRP.֨~$k?h,ԙ\ V+ڹ]{_< uyyݶmKמt{s-<*E>,t_FYN!C7[`%oē"ƐԽl?_y C*'5P,pZ󱫟R;ŶsȦ"X4Iԏb/,3dK{|&^ vиs܎#|I]ܞU˟r y4ufz5Y|j%4xs#矗}4EK1DP""HqՒ Tv HUZ8.}D:/n%mF2`G%%Y h]~kfN¹<:1e{%Pdܷ- # E9J0KR= 9,c!oIz<"5?dlESUєKv,HϹvrvYhRܹ]SKo 5Z|Or[A{LX#F~(FdM.R% Ĵj{ҫ9S?k}< uX TTKOW9}Y\ "25GSۃ8;68  [Q\suXd*hp`_bcu n9B 飤:HmM 0c[B !1a0A"Qq2@P #bBR?Æ[+{l [+oӴr3tti}ѦzOSi u{^+$皋[^371/wi^Zѿف &4줥iO6'SrP&@.b|XBħ65ry~E%)--E) 6BCG)mly&g$ࣨ"L `碩~RPQ4wU4u1b{-PJtʻA2Q{N?%,-,{5Xk)|*JmҢv!.9λx&OIF+#2y5JP6T\%]#G=V5ɦȕZ%5TW,e,_IMuëG,褉O 6UD#"QmAZZS TpIZVٹWytR\xJLz=TQKZ. .\9?ޞbћ*].}UE={5l|M=r暭t͓қ7T(ϙU|>7B|3CdSa0]&؟M)^&n5Hōuᒈ3bf*nQ'L$zTY[weF.Ȕҋe tU0mgcOyyWaMPJΪv bӟE˃?.#C\nxaꙪD* '9QcRdB=jݵdtɴ.^Xz*m'ܪ' Š5lM` *i6Ψ)B,T99gTqSi,A\Vr͕Dzw#])];tiFG!.-B=ԌY Tp9RҖg5;,uB5 ZW-P3b(7 '(tv[&mTEDeP9CgtTA YG /M*Y ZV|>GxgQp3?BZ3GAga꙱UrgEC oÜԑJTGr檣>^':'.T\'\W91Pվ&vY̩#ik'SY}Q9s\[TӼTn5Eu j3â2 sPb2Je1!AFsG mkTtrm=Q|N;v]0`Ϊ\SjN05SHl|B|Xk{,䛩 l{ q Cj֦ez`SQ98]0iLNKBc$iQIED.SqYU_s ?.'^݇Ӡ*8ٍP@vT}4%9渧 1JuSRv՞i5Q@\?,<~h[>ߒAϗUYg$ƣ3w٩ 4edF'-.jdMr&5#1]{[kB!Soퟺ66˪= ϙLPP+<mLƞYucX-2긧 3UNp/h2&sQv渄@sJ@ΝSAN™R:6D"?j%52IеBTTPSJRه?AsɲC`l4g8XM^ؤ.{liMJ`!TC~tN#gp9(n8lD{y-vWZS'5\(vOUٜQUkX% X'<^Üa֘1M/g TU=#jmloϽ  :ѝz*}NOSYq*wWv΋ns~k'B(G@ &0-$6M!9Lls%R4Ju#+g l΋^=z'ퟲc\9AɅ=)丏흝:+wl['g-de-6ZS|N9Bj N'=W `У]As@j8qGO?kn NgfhE)m(ˤ*ӞJ3D]re{wl . XkU#>F%1] "l]G\ >\*6 ꎐUsEM&jT8=&Ah]iΩK7?bnz)αT%gp}]CP9.LlAԃ>j@yb1ϺmϺdE%4U0:*V3~UF**ЛRI%unkrpRLjk\nz)x_x`Rq 碒El,gEs/|"3x9ppVxٗ ׯaB#$Z/燘~j\: P#\õJ,{*vpC~ȷ@ލHÖطEj4dp+̨o}ҕckΉ9)_,% E[1E": H*T ` aI/[t"عT XiՅ;[pmr+!N,;ϔ)J.xN'U5f*UO3($\|IRdFۛsY%O[Ӭsl\ޖb| }-\2.ߥk]J֕aO$h:WNm z$cPG@=z_*$RmE|qҸˊ+XtQDSl5V!P&2뾕o\KZٯkTY؂;?=Z0mhָL Zm{!:8,biѩߥAkj+V_qk^C)`_ J [czr>$-)0XDXx@H"*oA[(ΕdnJuK@ GcRG!brk4nWO2~.z?j{Rmor-:F MźY9r( -¿S}Aޕ'fLr:ADFֶ8LFA"LJ ֳ W'ݓ@~}ʯ-3ͭXL .2@-~36mA.l(U€|Te2@t~| 3JwTڱDe<k;erY|mdf KmA+k0&X *E 7&}lN|]4rtPu=k2|ԺjֵLLls(+[)E?ч*I &!(8=%2sDel3]U[Ҝ[UM' y+|lKZd!zTT( V[V^iO|dP>^*L=:i\ p?ᜟڸ|LJ7m*񾽇Z)7'ZoVj:NVˆ:o3G ֘DQޮy{_θ(?/TQ X|<ДNehZk]ѐ,JP^G=h{-Ty<80ʄal߭b q$j-z2>k2aaf\*a= 붔fy T+ݥ&R)*, 8{ЛRʼn+1fj5ڜP;VtZZ7[ZU\1(tc^< v5ĔX^Մ˧<( `k{۔MfjАFXr)/2mLW:uvu4E;.(; |< :Nvt|Rkҫ6Qvy8iDtP~gl*X.ĠُæuR@aLjMi%S ׹KyRdrU V[ 2l`"ֵ[/8ۭ<ٲfG\(O pկ6654xBW&͠] KX(8?2 ySq L}tAoh[n<7]oi$sZ j3֕V][o$"6Z ,k>&uG*ڰuCFnL4;:kV%|U#rαp>UlܦM :3xX?pM0pۮ: FUsKRjY%Ŭi1_ݼ.+c?B [g!H9Z B:[J-׹:Amt5Ée|(n|Қ)T#lt%;V\,9u&CR1lJHu#K0ulHneh؅u[Eg#' 5>Ă Af;EMoF׈x.,I=CXibu*1w߆/>H[9+ޟ#ӚvFm^P%$]a$2&9 "7>uxb * 0S1|JFQ0KZ:lkH*-L4Ҕ`Kmjx$ʍ yގb$ (`eXȉcSȒ8ђ;GA!<;z͇9&}N'G;Zaٵx\i`}+MJ0?R?a?'g7 Btab{ކfu$}Ě,lP0MmމN0.P]c0{KN*/P;ւᓀs&?LʗV9|CEA(2vU6՝W.?y)z8l"ui끅\

o֣BoZM' /2혏:BIJL LOݰGK;Ԏ(Q` LD\zF +|DiX-ljxe9w6VF+ *ӆM82ĜٹYIdS m_}FS^]zY!^ tҭzޤ+t=0x-ˈN1nTC~1yZu B BxƬ-:0n ~&bdkc?#s~8< "eh.7h?7SJIœ\z֣"'Nu/,R&Ojβi΢lG.#Y̓Z5VF|P/ڎ2LO|C?Pt|G6'u{$Wh"%_֦K+VF7%jL*Mo=kZm>Ve6h 9jfbԌRA"3g2槎xG{]vGR %ڱ"?4$6ܑND6X"+km4,o_KRJ;XL8,̤=7\LG\,%iP<8{Z6/M[ H9Najli"G uK~2ʯU6 Ν Ar!:z8H([f5j׿?[R;P˶S}5ֹ6*6#^VZj~(BI#yc+ >%.Feޱr3ܳS,dB,;\ j!dѕ)($%PkWG[(&؇ҽk }/֔qÜl6 ؛[ʯ Al;$Bmֽ. 50w#q R^VLKK>ltf+|"/@_od]lwF.9f6JkJr o +ujiVe R$w, I9/#ڷ8 2/נEHke$s*(R`E?*yMGJ V k\sE]kU9FfQ\[ւW k\kb ^[f:7+v5hh5Nd9j(!1A0QaPq @?![oǃ`o7?ӏ}?8~,z3rC 8絩vC4odnzYh4P&xT^_s䬻y\>0NgK8v#Fjn=f[ /UIoȉ/9Qd=\eo`H;:ϕJ̯]CrraS :R'\@bL*n{(ٹ[_umÓ.3z;whuG LR5bP.˨?!< g׮|RYܠr7uJC\1Wl؍p'-zKcs0s?lMrPR,EjyZ# fUB噤.ҽR(ٔ#H7h,Og~n>ȫwÿjvNˢ'튑o3Ål,Yb1 '\mpK'48p1݉*֍cpȨܑA7)psx:2S05&`^AߌT3WTƥ؎L[CBi~怒:򷄃&ic3}@9d pquں*rs3 X힓 >/Ahʼn{DhrAF.I/}N7`fJ}7tmg5nFW`0 ;%7۲dTzz|- ; qYzÁt3qy\.볤Yc"q,,L;ȳ51(@[v88G:hi`.2E2Pn2pK᯦SKq9'UJ$,ic ψZC}o]!rT ĕ =BJM0vC9ʀK =Owlrv! ~=.eXE}~3OD&psa SjaBHn+'N۹+K9`1)8QO7y3ڂ,,B>n0d4XkDkKuQ %;+NHz1[`)79IW/;<0bCwӝ.錁^Ü?t~&XѯG9eB{ZqePǺVs7LK|oof\\M9ڒyCAH|6Lki;W搖E%)4>mCVThܓ.,R#z&5|! 0zdnN ]!!o {U,!ExNӜQ(i쁜DY!3,v`ƭp T795@n?1?]q8q fY"8-sُ#~(ݙ!3vHk~[Bv9O}[p0KU|K]ל;^L?(;Xrt"m l$+ &l^>7#C|ZgCpÀ?{hd y<ؑPNY1ÖFAָٍFyRUzN9܊cdY1FQRT|aeba㽇 f>t9vpWw  V`95h$Ox]Lov#4!&m%> *- AZ![#.*'`}\7#1 CY)TYe\h^Kdð07F7Ὑ`{Ck\!P .aseN7V/K'ie/RA -e+.z` դWUTԻ[Q2V^eÀ{Dc୛TP=we~dEWv@L!,7 ȒmF) RhE㞗CaTS("9#˵'X'a0JQGHD7)>bil0g - BCxw*=W<=6'鱞cedsvn%NpѢOHxji%ay_q$6G"5/񝺢^(_;)%N>lB (SƶͺsG~'њJNS"r~>+ q-feͶ6|GjUYq55.ͷg g:lW6C: iS!zm __Kkߙ}㞷?m[_>X}Ǐ $s?6$=jc 8M[C ZPBL%0N V +Y")ړR+(@zԴqZZ=@ ήu` I8="D&hSBPGx,CY4>@ut;l#WZ^s. a,Q e,^CLZ>~~ ac;OU xqq  _=~etkqGb-xqLG=l|)e!V_4w*l~00~.jRü: !؏aN6z88Gð.Z۵#qqɣje>K,,2'N.+RrETqsa"ehh-#wvb5H|:*G5]*@9[ ?lbU|!-{-a: 6(XZheP|5҄J5U7Úx뱖_尕i6  [ ('㋪〮>#k{60* )l=*-+G ۛ9Ɲiב'2FpG&],4;AJv{~!Iӵ0`GŎ3g%jiMcHyz}z4v˷ׇ.ڂ,#喷vu0ZhզwQI?x&1k90ʢiBZW'iڏDll=20eXl/. Zldde-ñ9d`eǬ"4""4e[p7l%pbb okʸ(ޢ?]CRA>ElrPI^[&leh22l~нAļZ z=q ti`q|8AoQzP[#r񠦳BvA8%1xu#/yheCiu!=lEre7:e|+eܑXcK"GnADQrT,/#d9Ug*Y0pYsuŸ6Ѱ%effPI8?ݥ҈ɳ;%4lT @hɀSȤsѽ];Qm!$|h04i)tv>%H(rbH iDz`,_aZ7*1!pZFf}Di~dRB:;{M~!nvN8 uEGbAl}Io+DB,$6E/$vNY%#FA C͔6D6B ^,~P>MZVPCL4GKE0B29:7LlEU].Om R  \{C* M @:] -<4g Hfu,H1Dx,1+p[|BE2ah^ݗNMCVDq 3#IɌg i 4I6RKe 7( yC xMqƨFW~7-Ĉpma樋  1K Pe'S]$(>]#FV,c ͑K Ag Qֶ]IL-@Ãp(OʣQ2*7'o>|xGjp#<2"N l^PFPtYIaEJ]ph3@2|}AY6nQ2(Q3fA!qqCcT&X,3L]o*Ik-{IF@;{ KAo`4oHєW@Z=RCH`PH10IqulL@tMP Drm_eXh~ 'mmQE-"CFt1danf0̈́mࡻ 'ЀB!D3z21FmT!#9Cg^ ~Z1y&@3J`kZ9G XEf?M, Y Мvu_Mq2Q\DvWBGoj0죠\E=3r[ OwT Є%7 3eu+J_8ӥE-˝Ui]:yK455 -TPr$>(, :y8B'B d65AI "K>^>&pѼ#P\1"Lq6l1~YϽq jwNL; 6lvye͚($l Av 5((omqfnwe6aE@AD$ &F w]DQY5FS!  RT6ӷ|Xop|}~ k7C?DL#Ò 5,'PE,[ZouPjm b hb:g#A,p)WC3eEʣ5OyA@Cke >Q =]c~@0щѬ7Nxq%&1cD cQ䲁qC-ܐ:&[! (FNʣDMٔ(6Mۭ j.(CВ\O1y%Qi\*ٿFr q$݆fDLDE5@08rX "QOWB.$S`m2d"oO6/)lTdܢ%Mn<'O]vȵ<3&mDlʉ6@l0N=?g(%g7/MMߏ+!1 0AQaq@P?v]l[?]R(8>$)H C`r# QQ,AO20qsTFaS|Q|=&g K\Sub#h&@H`^ `A4FA@>ZO@iG4CXf 9@O4'S-'rL Q7<=P]lW# `S:+nɬ>{' <߄ : (KhB)`10CʔM&<#mHP FF 4 0K}s- @B;!%/"Wb3b *Z |""Z`s1=2 {CdXmf $ dxX%3Ic.?T"FPfS!g!`C.I:8 qQF&a!71( , (@/- )2#"@;Mi֌Sn@W2v؁Gh@<<-D!/ @$A#bl#TPM]  0f`FK>ij@-,$Ay(м>s.dYL3z(lRuB9Xk6lfYcRbe! (E80;A5ҜJR|z6 NEJ\kF@(hFd&NvGVPԈa iIt0D  ĠCj¤a3 @T;,692y"@ y;pRmD0apA xUc@,\'.s +8 S'< =Mq"J" ;(PAƋ'lH!kH`ԥ!uRt0J66O\0@@ɤW4"VQq,}tE5bS6 kT4TD Ky[̸*wXڼLԍIS 84 6L`!NADBu 6F DK6F-/a} :(6 6+^aG efQ%xޱ+!73ڱc׮cj >:h HQi$2ssx1 H Y` 8Ȅ?qM} Hi05#eA 8 'FdJ$ACSK]*LpLIU'%X7 Gl%_"N#l7 @hPf鋮 JXVmYKJ`H0tECP<ŏ'55( a$ cג $C*XLT>Ϯ~Nd1JBcbF(*AeML0i 8 ]U)4%@"bc+=ha)HVi!P?LCK]*Z%Qq) 2Yi(59F)@1c":'p E Dci"F(Q%/" 4{SL*iA"AETaMԘ)(@@=.C@t *5EA?l3C4 pB t qjIwF@|KFm.tl:pž+"$ɫxC B—c^E41@a.k(yDP08 (d2J2Sp1,]_z@@{O :.N !=Wlzae` >* ( K&&#80TkFm$p]H:&ڜ,mGP: \$!e1 e6@ ]aA^ah@ +!W!AAI=f5Py |Jn "~` -J8XG Lh`G9"Ib%F|H ZSth)֍fr.!x:rHd0>ÃyJ .0>ȹ'G ȩ( N/ PdFX¦,!!2-zBP2%]? */&RlRh27 M& e8/?q=t/]*Ϯ\~J!ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/images/flight.jpg000066400000000000000000001015451231750357400262210ustar00rootroot00000000000000JFIFHHLEAD Technologies Inc. V1.01C   C    3 Z\(,pqKSx'br'8㎄8A7 q9u$TF HpGRWGpQiaŠ,(F(@N0ͰBq (M9&e(XhR Hq4E pŽ (8-ܧb4}V'㎤aq-=YuUL/))p*jic$K->pQÅ!ןa8|(QETѝ9e4`dcjRKÅBӇC&,q-J~n6 (u?hͣqF) CiTF?q,WSˆ!0p0ТS)dm>s3ÅQ1n>udrh`cF K XJZa!o7)ǮϞ<`,>6$QJjq_:k}y`!:Bׯ3w"z4،`1iܨzH)S+*ضAa<:8xSHʾԔ%8xf-u~PuF"M 4mb5(knZ FIqUzUPdKܥFK(|2JF^ce}\DDz9L0h8G%## 0 ELޞ1Z>^p5ƗťERe^]&SH\_X%+**y?y窩M$Im2*,2|8󪒽(hzO8~;;(~wC/je)me%P%v4޸/a=qii}ZZ2ZԹZA"%bqJ͵Y+=^q8ux\^w ÇHާvj}JN^C?}^[|>OL2B2D8,%6zcVܻf~;Taq\(?n_cN:HG:5O\Uzwf>DYx~ 6_ڟyq)CO+Ϣ6=1?Ro=pgNY-$4XzL~Xѽ_YVx0 #$`zնYWz2Gtތ՛z~}/>zy=G|3{rEViž6ME,Nh}E~oחZ}Jy]G̼ucye]J7'nWi9NzlnL1tHRROŸ+@QwK:7l|m+Mvg*7|&gMԇBJK~מST/ ^kApШhj>ݰArzC_Epќݙz49mI|ޙXf5Fgy%$ %YeiDouFis2 pWj@4Pȸ29.|4+ =mL˅?^NSf}>_s, *&%Fca:,5RQ]KbJA8 \EiAMkuRUƿ?ͼ7w1B`J.%`4COD}ybJzJx$C1Be!;΅#$8A:po l~az@{ѷ`G뉱=\=_dN%b\XD"dYF!V-*Ϋnxꬠ,) pC8 F(AmE=g5:]'iê7\%I?w>lyjKqڄrb1GyE9hi,06Fo!Ҿ‰QGSaÎF!1hFRq&ne0G=M4/'-둺}x;|j=|_ʓșN6"%*Ly.q|r : %()i yagQs;jK+_{a\AsU}/8C{h>w\+OSʷ68偫-%:8D/r-,e#2;rS?6LWDܴˢѻgڧٚ?ɦtu xtKw8(~-6JvqrSY8|mQÅ֠`@6D*ThE!!…p;>tZY7æ'9TTA)cpU8AD5Uh^`udD54K}1:Ɣ4AiW;^zs%%ľEVt(vA0eFFq1,YÇ/ p!ò>tt*~Ӂhz/#U H]. 4ŕ>h>:]oxtGœPG 4aԙd$#1G9TQ87Iڲu/yyuiL%yԢ@ޘ38Ohxþ\8i #Q`6p)q^]x.V>!apiYjM^y]Sgpb-ocWmAL]\pyoT Eh @/~F)DBYUZ1L>kGƧSGsM}ZcyKԤB6": !8RR1ppU?\pǣwf/1xX17Q\/n'A^̟ _+ !1"02#3@AP>Llra {ޠE1,[G.zs,d+57,ԧ0,9VqmL3!lEzm50{!4K}2[Mp%5\exfFIs)ObrnSyNJD5R \?/|qV*WQszuijZU㓕&Rz܊kYbZQҪ}w6=UKJo"/#~z/# (VTw;wH%Md`j蟙ۍ{̎+Hطyۜwg ;J1͐P?Zeb}O.VۃL^԰(YJwj✒:mB.UO崥I1/e/,oƳ>WvjoppZ;ieP=K|8#L{ɏfsssssq>5q,5.LA{3(-n Fh;jbU]*$df喵|J3Jwr<+!uBqu51A{Vj[fycT}:G??s+|M a1vbdUBW"&2M{+uU]UCrZ) 6xef7U׀F1;xij| "f9L"ҬIhgy쾟=eXMmfToQ>7SgFpK(Xl8= H6y23m-njtiI݇$0%j?gf6zZ1JXhd,ާ_APk>Cc5-m#L\0ܢ-d N pcx?Pe}s0WYYB^pը&uNx&n=V`" Kʰ.c` s+Wﲸk^|r,&1 ޥXHEwMC1uasL&^7Z}b,4OMKtCS^Rx:eG2U^2ol$07uǖ'c&Xibuvc8A^z4S{~o,Bՙ]SPF3ˇi RSwhXkis[d64<4iU, :P|5XݡAw(NfGxc[d)T{i0G2 9oLF巢O*Xq|jMXSd丶[³r֦{CY4<bB#|Eni?%y _Nve!Gܪ)zY`;o[to  O.ũ k7Co[򲲉aޡWK(=Ws/B3=ȇ}eVyV:If!ja]'_xukk* lF xz\b-#[J&uvS3S~NdlUf|1Bzv٩oWUO%tޣWdԪ J4XbZex#|1m  >SSI[ #e\$q ʘ,7˯9 =7.Lb5NInd'8{ kcdv:'2y-Odո~=gxaF?Iw,RQ_1 ndLoFW*Lfiح-4c{,`n(Æ)-OVjd%%y}~7u3|TjY(XoT٘g8ߗmU-PV+C?ʼnͯc;w nO JjO懂UEQrifhը"` m}5 A7OxTRrʿ,q\1ez U.(yu]nZX7>jx1Z Y_xs%Zf@Y255.²JCSjjkذYUeoxeHTܴh恙cLƺ}l~ )zڬ+,V,!s `TzOSƿS"jjt&xaҫML`r8 N?{:j,>u aFl3Zow˷eZbkK<\`5c0.ޔ[p °+T-+-* ^&:&@y0 ؞m(b2}e d&34LuN c{> u7k"ʒůȘb!y^0^] 3_mmVJjE%." l_~J0 hy-7x8Sԏ) [q3/([2T > *& 0-MMMMMJVuyEٍ#lXŏz ^j' !1@P0AQq2`a"?h:P?Hx@?**L(zx K*bʯ\6m[csa0~\$G8?|J02}[d*۠Z-ȜjᎂWZᤁVyuӡjq?SC&dkX +Uj4Sf%:^~&!'VjHkŚ)ҢM>j?<[#5^N piq ݿ*XlB FeL:Y^lMJjUqWgj\ví̟c*d*>zp稜|cs2b*gmCz'ufVULw;Qj\ݨRDm}#Ͻ+kf"ߩNr~j=8;'*?2odb1#%C0ac7B0  _A C#ZwLL76NVJj=Q(ČF#㧒Y,I&jH…J!|"DdjE12_5'j_G}=/pmn3',t J/bls"DYOէwu]#ߔV_9!1A@Q "02PaRqBb#CS?j3yt{4w #+c!Gn^A9>/q'§V pQᆝy*?4^co[N)pMc[hCYįhET ]tM|Qr)v(4""8 ]{DLRm¼ŭ8q[d(6;784I^Ak-28k<)ҪRtBO\#-g9E2nS!{@tgqEJWTtC,lDjB< 3bj5 _8-`f\0j>3cͧGG8BM-cse`nhd+i 8!D &z,~"p)-LMLu~RƜs[ Z[ wٟ$KS^#U${⋯⚚*Ϣq-'c˟P&<|,g+*pH|1 HکܪN$]j`n2<NV9+ !ENU(CyӊjL)pۮ k"S*۴3 ۈT{z? StrT:Ns)^!; ͏U@EG.×m 1A,M{"Uj{Dޣ#0o qGۼH!h-?mto::?tYf ^"p9*ƚ&:T*yE87-wM@ k;%Ž'-NdhB|ESugڛs^s.0sq~gs?BTlx$ANanX,+{_O;E/n)Thm~M?!׶:f:}HVU6?ߒ/W|[[[skCs9:}T>Bԅ@WBzn4#QN& lԁ/bi︟kCEֈeF9Fiζ*ubn1k5، a}WR(P2`A4bJs{*o$wW=vW l &dv8!uJ>Qo/e]Oy!W*58&0"p)n4ḆGZf(}3QBZne7]1bSi0$_Kv,T{V &:cʎT!'.Ue}kM k.t`̓HTg@ݺ49~81Л^%5сBL/*x!3>JU%W;}dVTn~Djt gU*U0SU^_xVc딞a1UJB3^_]CVB[ÂXPЧ>mTplS%g\Bf~cXl"I>(T<*wUl*nIאTq ֳm.B$8qVVOd׎@*AJ䰲8Z;Qn4(vefUs\>[|dɵ\hF<4O&\e8NVMmꁖg}5e IfrV)j y4 j&U.LJlC[~UZd ʋlᔭb66sOMڒ}2 b*sN;-iRB mD.o/0BHtLGBX 1uYOh/7LT *fUT0B-Fp x 77g8i B0cbIf:jѽhwVPʘ#&s[5u\Jh*YهT!̗ut׳Є#6g@$NVk+7P`ϐ'Jq&ElrVv"N t)<Z91^Ǵ9pLV}&#sCQE1Y9Hi g/F`a*' ćT&, b+0eM uV}aT/ YV&8DLSKj%8 q;c#Zhd tfrF=h/Ul]]+nUQB';,/₇?gW&k:Ϋln]{N[E CW#.r7\F3|2qUhLSP0C8~V;PhFPbJxjJtya/p2ˠX]h`W;1`5 \ Vʱp_ε+,U=P3qO'+Ip9VE9'n?E q)Q9n>or)!1AQaq 0@?!@B6]>x\d] tsU+DOKn}.xI]cvIDAAt_%_t4 'Ĥy\O"## Iu _-Wd/PXidqKm-J\0M֫O1(  Ɖ7^z>S C..R9퐧jFZtA.gM`U|whJUP݉]A@ 5>M.GݗKlD({r]Y> W!y0\IvȮTͯ - cU5y{ =l9.1ՠ$/3e  K$t 1UzhP$AhIALQz:vL[bokꑑsOR7k aKSLe*NZEo"% ,j1 l໶5p*^˶e\}@S A@^ۇCXFވxsv\Q/jΊ9s_bBn'L4RGm$;0eOwQ2d h0hc,jJ*2̭>ܘh'bm.q^LԟI&V "VlLB@k]y(גNGӉiq*;ONTq'.g%VbB_m6=[|k!)^* /ws Uk[i6{[bbbdvPl)*<zvU\?<2|f1hԈ-X: 1 8P%Ď֗II=<0DИ ʪ:Y=S)9%1;4 WqpHFذEm"LLO c],]V9￑y-~%9>Es^d&O{62ø&o!Nk)nтRDÌ]d9'Wejy@^F%PoMFwhhB?qUcw%}@т'3i蹉 ԃڅߦB\&ܗlȍYQ֗aH ͈(feSo2vLsZ/KB-+K4d0D`j6TjjT61f(dWJVtC~E.:p2x5Latc5t@kE"]]Gk/ȪRO;/<*u͓,'MC' l/$=5QGl6N9>NrGJo'ԦNǕ0ۏ'5Nb֛EuE3vt:AoVK@,$m[ 䥡9a: 2 BE`6%J)5G4iGJ~ʞG^M5-˟m}I"02 y?pEnpxM:el,-넓AKnB&1j•[-%򰰗O腳yWgjo5tj^ gW*y?,,:vn1KB81y{PqYjP+>߁hF S~H'@IIG"es~"J&*Z VxȾψI0RjO9Hen{2ByYq4CJ๹fU F&_ZԑB ,JY΢Ahcu똕vRINk)r5M9d̑ n?nϺ V""H*JK]04=eGA=}pxz^'3 QAԔHǕF=Vϳ=Hyf%[1bi+P%8v$a!k6 2R'Y %?LgDΩc)ZKh'bD*ZKoBr}O슉m·"Y@ķhT7!$j uE-Q#uX6h_V|eʿqA,$6'֢ĤMN2Zd1YT"=uyR'}mQD(k"i8ó,2H媁VhEZ#- ݸ|$|!!pWZ=93QY`56tg,еUR.;P06Ip;Qt7B)b (=вV_͌E˃!JNe.xn ?9ȪAEZ1^TrgUDeFg)!MUu,"BVKGO!K**ɰ!׵(e夡I$\DFՍ%xKBoSJ i^W4:i`'/zfM8z/ 45龫U,yVbp] ys3Fp}dғ,zط&6싸Jbr8,7y(Fy"j:eYT';xɤr|GГ|X2ag. 4?d<4KrQ@شDV_.{N3xpmʂyY ,HHoy hthz UMJxֻT5'TLlpLLS]F7Bt!W4SMf-Bd:tu4!C"8+ٔ\\NrL}]1LJviבG7ІrO)jPr2+am\XcN"c%OLCC|e5GB p,J13}l$jƹdK!tOD_ pݑ#Ъ|,V?h35?VM%ź"3 5I:bi[| tfBx1y-AVuBtYVhֳmYUR#N3NU<9_ ӆEa8$JYXYB‰a2ͫAs\&WpwUW\N $I_GX'|_D$`vm8&?#F IZG蝐#`͜C3;He?EXuD\ 0 L6"9g+= ҴƳ"屉II$/~0TA 4NX[BLdъ$ bnQ*h.j'~BnnO"F"_V8Q~|BWAU{g|{PT"D]PՐD4"S}e:/+r^06$W@o#r6<]q@ü%Ģ!$L̟E 3A~ |r S: "ZY97B̒ O,/s"6]{KoσTw?t &CE)_FJOVǨNUюl2Se_'VZ؞kS%`k< %ƢC+6$[ď1#i4Q6UtuN]Q^U(dj 5Ԍ:XC)]'"_K0Q}9;GI ]|^8m ZbD]ȍ7qDo#6ݑnwFWPJF& b3[&{q!)E}j-\ z.Q [7Yyi^83ҹD+MD./>PB" MבIlM$n쫓5!<2; Ej$&hhDκ=D‡l;N_e候Il)t/tn˗eQd+~}nqBDݝţxlZ||GQtA]wz V(ͱ'\F,,$ۯ($:qkۋ2ȑGR"n/jW$mcꔒuY.ؘsd ^hCz-[%ٷd;V7#e߆ ZU3v!D_<BKUq@ F5!-Յϒu414;:129gARDy㍊m_/OIT>FD407%e+k0O0Њҝe穀ILM@0 1j0IAOāRllA4 q =ז IH 2V(!1 AQ0@aPq?D,a?z]Th{StvN8U(L8{vU]$&4 _$sʄ7mI& ^7,r@Ar@ @DvDf{Fҷ D%mn3 m qW\6"."@8! h _QS#N 71`=8{UEP8 DPO0V0"ގA%E0'H20vo4,1eptb_ՋX:'`c9iPCk#a,,#(td`-@ULAp4Y Ѐ0?1E (KD;PsycA"\}Ԏ54L&x0[^m FϰА( 80䈬bo %ms %I.='& ,h⃞X֪ n( ov7T Qe`Yv ``lLc2 TChVzD4scH lBcHfgD7|rL >Ľ*V%E g""*UCQTv4'b@!DH.#b-(q#Ё!i!Lh:]&!PmQ7bC"w~ֱxspUhAC6.x4)"SGd=|AYȢgh(/x <@B0#@a (BG,y .ocqhKAvDSȨ #衼$ W@X #Md㇑ <>6@y* R:v(L7B-cBbs"g ̿P6 h qJ 8S~! Bh1 . ~ᅮ<*OO$3kpwcqރxd dB2! am g?=ț 섶~_#nqG3ɍ7?(S g2 ,bIRLjQ'Z&' WD=1k(4*!1A Qaq0ѡ@P?+)go}9k {[V5@ ĩX+Ipe(PJ½,e e|GC?P]ftsb].\=8jRQ+}*˗V}ܟ H2>JU57 rkM`?MUөgfb_Hk,n\U4;R맱 NIxxjW 9xYL3^z&͡f\vvp>J;{S`:٫seJ*TXFNQ}:эt 4%ʔ2S&-u(U(EyO~+|x}]T ¸(M0@ V._LkZFZ[VQ%5d[ϴ D ^bX\}6ZKѫ.Һi;& sUJa^߮,DDTҍ{Q*v. e4S 9YMק5gOF.\cRCY^!_3B^J}RfnɆ9kL+ByFMF i*V'XḾ;@!`h5"Es^N*de#= cM]O!w0IXJaD6MȚQں@THּ@'W6ko4J˳'q1"nEdٖWk<obJVo6D/][xZԕZF 8Qv鸂:x,i3g3Ӧ\ 4 [Γ[1h@uN]*d0}LLac#f 6CwpHiY~Z9=S wLܿX3;$͕5~ZsGû{,'"whj*/~s46y\h1phՓRʂ2p2S#x ^g,paRH*W˗/4ዬ@D0m_8?&|ze̤פ(n<Μ[̭7^R}S|e ~% 5u۔S:NgyI * oWS99Gs(hʙ4'gnL(d9=::0McSΧTj J?1ttv|m:**7^8n^rdu:{L͔Ir,-\F9A/Q P[~4MxPn+Uߛɼ@3 iD|LS>| ^\OZZOW8\H:.MtxhbP/4#PL.7 aF\6°rv dϺZs.%Ltb9z5Qx _PdmiATGhV?IԈ%%qe1GH],9PfS\#θ9ddG'51ۂ ꘽/?E(ҥEcSɼ@Ɠ5.IN#̀eoSΞCIۗGՍpB#mmW}o)i=oDI_Q=&ZsDQCHeʜgM\?lÜȝbl|}!ʘ\9M%i,Tr\ E3\Rju(8V&wFgCөkRsj}$%:$~ <=\lr">xjvTwrvQx+GT?qE ݔ9.^?6GN}}Fc"Z/Xwlw>@g|f`ϖ?k?-/bi:t765['= 埳+h콐ؔv+_nh?uS^y~d~:{o0//vb<):o_X{ĵCO1/KBgiI\HO-:ra~_(o5O^P&ʏ"-ׂ+a3Bv_kjC!?'iOv fn-e VIskG(_t|cA|뮧e+jŏ"jn7sy}{El`}098忴SεCɀӠ=y>r.yt]P?-o쟱%~ jeSkf&2ôl(GS6â d%#^R9?Lφ|B}lC02 |,@@rWTg4_LTgٹn,^l:X(#h(Q@ ä2*j3 CA;Wpuxf13Mb R_#C_3|T_F}= -m:}cDo=ZֺNgRl&OߟP Dp!]kXŃ:&k»Zp>b2%h=0kR\]n#9G(˖Я/.xw:O7}&8j/SHx0 $#%Akz_&[\lp`@(VwW(!1AQaq 0?!BT*B h_!htkT6Kk؀M.;4ť띳(80 0|C{Zz5Frw sHQ)]}K#|Ļ0|+#X"'*5t5t|4 1 *8q"z@=IP%zz-WӺ=a̦^w}Lr/աzM.4.uŃXt:65f"_eք9χ@eYgލyAlV|2 |Y)B ny{?h[C8;,q"J*JʏHB T@W Q]4;򼮫a*H,lZ5TS;-Ҍz;"䍁u"mt@UZqiޠ?S - !uxIKyZ1]:ЈieS;gŊyNW KpkSeHGcBX|M7/Kzoأ=}<y|;FOC!@TR^v0 `@`Cwv8Zi 0OEiAP{̅WL]n A4Zs@l͝xw*ainH6pșޠG u_b2{,qq XGق{71)/jbK߄Cx-2%JwESiH l]N{L`HTe1 MJñf cr(erd< m0eע֠0} FCKnL2@P j D)1RWUt-tsվLA. l:H^9iuvK5|}@C}.#dҾGQ0 -'gG bM:b~ՇW]X1 ȧ* CȚGcZz͠Q*2iK}c*& 8UXZfOmLi AL ,5C*Jot/ʆjՂЄon[#X_ ax(Yojȑ8W^n1jQ% աk򄔙_Mvc.1%6#?L.!{2%y%,@M@CDr'sb"z cב4 %DXJP5ZVvX8 Q:MZv"!2 3 2X3jJ>XM`.rG"U2#cbaЧGZD/oUwR +=S5d0]* j6 Љ.bo̿avKa|+}v[]ca)3zT81F E 5U;6 =-= >s[x?EM_S2a("F:]D.{1 s Y91p CC[ ί,%Kpz5 !ܻy ԊRm,:YhE"ׅ_0& ip@uL}Ə6 2B\]fGqP0ȏ){:qw_nf04a]\@,jM 4)?OҠ켮^:]+c\oڹ]UeIsKe_.SAYĴbi ^RՍ XK (q^շ|gwS1;lvӴg QFSKh/r$JS4<#r(O(%?#O" q 8m6- rRA![@Sl 2wҨ rZYɟ} b-u\<zTE͜l9ӧF.r2Wx\b@~w CE+-un j< # ePWCp#20`ãBޜ"HK@M@@pըJR.uܾ~ѡDticJ PƘQ7*Dj-ʽl'Y : ̎H`h0TLDoɃ/" Fyzt={zRQ,L..k]8K8a;t0rK;A(=;`kh,n4l̒0A|z0D@tBc5m p75<晖7H5IJV`~vYB+za[)ÀYyWX{B@tũl5af@`i ޙ_?570r/OB|>qF[;uͳkW'?rb*%},Nhpl4 uVNpF1ݎ /'G i|/%թ%]"ްiK:QZUL]:l+ sQO_#[_=f*((!  ij_>v(-uHxo!0ui#8d( jg3_3MHApnb[y$qc#%IxʅLhe+148S ڽP$<k.Jl9;ڕȯ,(b\{~2u";Cƒ\3wy|viaڎ ˦Ǝ2JHԈv< j%tà7-$tPN֛JiOBZ5} 6 wlE[z<'MT$D]i\ -jj-`|Jմi[iL@(?7T}#o=vy}M̓D &q0~ M 4ЦNpSz9X֎Q j2S=#lb` PFbC0{B b\k:0hvGbz[@_60Fᘬu@7vlb3P҆k[;o\ %XU[X@8.%.7{7+hBYB .!":. LjQеVa,9# 1˃~+BOYe5;MUu@WznXn!X3agjf6EUucںi_ 콹,!X:ٗ #;W}!H)rw̲GG1# [N ߳3Vb1Drҥ2 6Z@ zueuevw4Ext0kj^Ě*09>%w1SHA+t#z صN+IB v9 x59i3,GԨh#eyz^5DcHN Ť8I{B@5݌lq" $ZBYa* Kwg-P,«%.X`Pr6[hl{uBE-uK` jg`X#Q,Kѵ ;Qޥ(+R -0kr:7\P.ihŹ1K"eL5'}?̼׊V9.Z5aY3Sʱc5UWE,xGsQ<02U*"pi-:X-ܻJP(U*>6w%IUs]eg0 Zws*3F 1^gƨ'*ה|rwHD~Ӑډ3-Sw~T;h!grPG`>ƌP'.{BV).\!Ac.ufvA|B;3e0J7S\_w2D)H3w\:`[fv&+튏D ANzo xӴ1{4D ТJnM}n5mMM?+Izn:!`4/9mz2J:_f5mۻ(/E2?ce2YiV5 +X&kK_auf,L !j]i+G*IS 7>T{dNϗGriqcGUr} p\_1ǥ@Y1N(V+ R׃SN Y-Y{hxiRe7Kdt :Ʉ蟸Qp&jJ]Gy$lٴ}m*~@yy&0JפP7`xrҶy"-8N  {U FPzˌ8-[+I pp4STE{o6 "SA%mg'u0⣢LI,Vl]qS>Hab@M~3 {j\7jt%n27b˙ox-ȊUP5Z kBִ@b8`hqTQ/URU;ٟڛBX{ sD8cܦ;:9r~BH\/ #kZʀ=~q=@&8Du:w>+xǎcӰbU|d7>ңleBf\((C8zC=F3T=Bc+oqH'볣4rq|QqR\ dM 10mS""X0Ϯ݀`V"a"B u[vj:ٴzĭRp]qf*RpQ!RӉV#ef䠮K ./U!!Hpafqm thތZ; qڨ/(TT^JilTXq}akh8Oz ?}rvӌp6I0D.SDo1AtvI(1VBk:xw~-p(P%עb)::ÇŢ\J_zVaF.  B#L#K~>@. ujuK HKLJ] kofhaL2 .*QJ*k4͑LFdԠd;62tM eX4o7 VAo g F-Tksfd,__X7QCu UAw(fWI.8}a]Xq*{WN83Z_g;+8G#2StAfJzp* ) Uy7K}.ɧ6S< `;Myk)m!ˉ Z)Yڦ\q;$#AW ʚC*ѧs vҲs+,'G[JߘܯZmN dZ\&i` {_G4UҩZ1 \{u:W5 0G)2 UoR^ҩ SWeCɪS ɩ(D}; !(AFRtO*l/p!@P+QZ@MPoAпdJE5'kG(!͆u!֥f0돸=%^~KeGYVk0] hMl: W.;7rJA65[)oX09mh ?I&YwRV q+7_kRv4v1p*SQ^}} ?~O_~Rb @ctMS~"1L(h'X8MGnED5 JyEN7pfNF և`NYPuݞݓ(%dDzڦC"ߤFvmGv_)/ҷzOJɊ_@^-Skuv/b,EQ` FVlK,F mkNihj-[VՕb̂oNvy }DBsIۻs?>R6kpe5G2+7c~Z |=_p1JSGs0_3-h.Њ.M$е-qjfSd~L wۆYӑ8OLaN<5OEGCF4e=K),hf{DMBM&] W) "QĨj Y˂V6YΧAL1oF[%etrS۞%%T`y>?ߨ0j 0(GtxOYi{SHz/ {Anh܂ݹ~S}C|JCWiٕ-5'>>O/0{_5a:9AZj=229αUkQZyULĤ,>_Jru~]z(fkFo˪}Sئ&JQ VD/]Ľsu:jk4l lލf/cY#זm. 5}#`{9a%]A3H0`˯onu)9L!.^zDjD>pؗ'A>,̭U]ސ4`]3&R,GG1N~ ӈ:._ *^-:=v&%L~f }Gnj &i.EwQ|q=2H|^fGVZVvS}Ihlz=Z,xZ3]WW +r8YRV$mz-3)k8֯4xnxlV*Ğ2Rj'WT2|\CңZSܤ^}e~!xL/|XJ&Y]1 -1H̳O \и]}Ԅ&~ U""Yq+7PC~Uj&:qP80AY `ʺR8ίM"ñhgW]u"ŻTˌTrCE4^S)t:8p:@<@Ȃ]G=U6B4/ `D(rN;ތҐ-#},` ތ!:(|g mn*z@_6z Dw @{w\P@&}Bganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/images/rocket.jpg000066400000000000000000001003321231750357400262240ustar00rootroot00000000000000JFIFHHC C     a, ~sO <%6z%z=MR'Dm PCK[UW$[Y/Eaפ@(P$2,F ^9YO_"c,UD2^!!4S a9GaBSi0pQ(0in-#!ixl6( \x~RSDLl{|r ~{\*,uo2`y?3$D'~aǫGjOԿk5ϝ%,{r'^ T̼XԿYVjK9#8"f<<p{7MMTd=ZGog:r=x=4ͯJr],lr(Ryati/;M7F/nO"MnM2WkVg?Ku]ܲMwOtLˉtr^*bTMLm|3'jLZHtr e:\tǪeiRƴٶ9GTB68spfsk5Ssyޙm~Vv7{\54uems-~ꔺ ?%~`ˬ /^o ZGv1 rj+zMer\LvhtnK%Rjy^MS<sp.tͷ$-i_j{U"ƪe䤼Լ٫y.u,CsCqϧCa9oy;-<r+<-rӸ֑rz+)P){AV9|ՔhtySϧ~nZlĖ#?iط>^z?az̟Y9&eyάݩ3UfN?g?@NlK|oÿc{6,IXEH9KW8rg'N=.ZvV9R.rvk_}ǯSӱuLRׄNS?.*,,vvO͡禋!=7=T]aNj.:xcVѱߧSȂ*f e)H(^_.han<5tbko]ӯ6k9jL9bKI%Mݺ^8$|Ilaމn`./-k/<'3!W#dmz%(`k<0S*8oBɨyۮ+:1i2Buq*!ׄШ(dxW3ӛ(IX8H^<泒}2!rh4 UȆ{c^:7X1#w@x0_=^2xF,֯T͸ZvϝV9OÕ<#<`1H" 85-Sϵ.LUΖ2j^:;:$;#ҏ)ylG7779NS7ܦ#=D77Ђ{g;l}'XL. 6sh?s&m7zm~SsB.S Zl{zY9NP?a jm8=~'ylqbgnz-S}vf{D.bX*''}5ʲxXNhg67.ss7/ɴ4!SpCb3'gX{f#`z{ P>ݸ0 }q<ӄ,85#Ҭfh9@+DZ?̛e;p&?_uxV N_'\,C3Y:TV*Q3u#p?]Ka:o nr",ҙрDYn;eeSfg5DA'%+[=?Q9I Qlpj'AX" V9MckpFǕXDDQ-Hg7%`IeURGJh-gF;Z?L|ȷ̙υWM:Vk82b?RqfMqsq^DQF[h2C,[Pרb2r-eRx\),]un7aXiRm2%gZJk\["UF O$8WAϻ~FDy8$¿S')ٚ2ECIun\kϩ]&] D 0"5 Y*{6z*g7G?໅ Ӕ Z)_ؓ=.o^@=qSfvL"DXJ+RVȷw ۮ3jYnizz O.̭*Y[7<7klaw8w-fWV"Jqt"U{g(RMBPTVVx넭F#)]@pψRd;/58ԮQ+*J=qiu*e1(ʵ OԦ f]ˋUlэ2 My4J֤k_uU-1jbW&C5vqk>1jmS}Ɍ]Id䊧xJhdZYn7aWULZĦ%Zi LJPB{VbSZLGoxj7I˲ic3Ϙ6mIURretz]EP 5Ǵ(b8J&u.GNU4jGP 6{ XsF/3-fB{mʩԮsS$#ة8-c$D[-g4bщ>Iݹ ?0 V}W1N D픻UgoLM/SL:[Oef`;ΰ=OTN;Y1 ,+[k19;hWH(y㙙xIdd5ޟn FMt݆I{`N2z1 8 Xvuܺ^hs!#٨U83(,:jVjΥER'ks_ֺc0"V+sgZ,0bweq8H<ޔT:3.zFfuMmFTyەWj*r"lŧZg 1LV8l/V`4lfZiEjkdv5c%Sh,s )2%aCttaUG v6&Mxy+8߳S`jޘfYxBJ]s9mBy (%,ӔŮ׳&r#&Z`еWqLƧ+&\O{ H+_%x~zt y fb|kz.p=D~8S[P"fŽ =]`HU}C qj ,+UD -bUZ`6zg,6w Cٹl]pTˮQmhzv~Ōr *ʫ#k7hK/t9lWE%C1$WD>WdrDWj{)GGmLLL<&6_tQ/Ho*#k9ǐ/|6/llmވ!CG'5kXceڰ-ۢu142%EQXQ5#*dD7b6B(оMYą9gQcbDy_h%YO yضG6ts_WgE2=S)e2LS)grnOe2$e2L,C?' !0R1@A2q?G39gs?G39gs?G39gs?G$If}H9'rO/_G39_/hZ-EkQXHR)iE"_Dz,^V_et~,>_y/]lflF,lJ,suMzح2+֊Y!f!g8GAb\zrlWjꇍ鸼,ˢzy ;8Q(^kщe^eJ5x7"lCT<xԅ4,F9J.Y^hQ!̢BimeُzV=Zt,.ߝ+PYb-/얹-KF$4(UފǛ -cslFhسȉJEbYcl"[)^D(>")a$"u aa,X+'ȝBXDžҲĄ/6юB4KsefnEQF@m(H26Dr -Zt^T,֘,J-F͔m6K,Mi:$܄(#hmYeYeVGHZUE!!DP6) Ї 2*eL-+cff]ES,Xn,_{z>6>eVih fmebF7iFlDq3d%ii[e<3#Ilkcti g^ؿBLYe,Yg-$ŢG- ,,4?.҇]D!1AQ "2a3Bq#R04CSbr$@PTcs?!|<]<ʮ73>͇u9uN]Q\guK_1WJP}+wu8/| ϊ]I.W]C~*WQzB/u.75\CX(H]D/C~فDKww>j~u\VQˈ7j\\yUsV T~qQJؿbJujzՎUWSjXw*T+bؔ[Uj%jjUږʡexT*ڪٯ^5']WT2TkʋWBfWSR*TSjlHFVw 899*Cn/|Z2^#;/7BmJuD /{566ҞrP%WFcy_]5tʐ_E@ĩ1V`A.l:S&kf3h8atfpx-Qm,wIsSHrS^JRr6dԚ*|RԄ=Y*,up +eߴh ')bhEY{6*z"@jEl'rR)-8+fj_E@N]]7:Sci] GJveO!De.lwZŚ~ !u}(t6ԔϣoިM)\Z|*)2&S O )^[%!bj)+0ڈo*D ;EW ึ-0XpW"$Lܡŭ5o?B| J{}٩ۘU/*ڤn_u]\ibPlOz hj2vSSԢCk 0Ects7FY d:lr#zUA_U-&iZ[fXpz >(g~C빢0wtSf)؝JW*JW dg T7HnXq稢FQtA@⿆mngUr'KnS%OKUT@wG9`oz"S8¬S%eM@z-i5ٛE^J vb64]*&xjM%0-tA3%. w06CZaI +<—OU;}Dhu=f=7xl&J1a;x4{c;p.n Lp5=7p01v D|M%NgbQtcZ{{UR5H jt:Y3AC-ؖ7{yp)Q7ݮ!hq \@>b$Y?{c C5z6I3 `n0rSY ̨{‰@VD}a=:P-πZCsUbNSO$Z#\7-%W)ٿ ;K-f# % 犗DPIkKf^a([Ѻ#KXUZ%͵}IxPggDR?HWXb,3fΑ@Ҙ׺ԁU]{?5j&#MN_U%X`3"7g{\T4k|5jRyEIJUwbe]kljsg$;3:1QCvSt7ϑ_K^U/=A !D3TwXoE4o3]]ڹ䅓>Z&W)I]X h+3#@>ߨsp2)lop6(yض3D+!I.k|+ՓxTUi޸.3~Ee(!..i Dwv踫U u3L5\vBm\Y/tbTʹpcI7#m+.s9.8 U~.:ڃngp冬k_I @[,T_EXmV@J @wE3&mü:4C`pRtChꛮʣ!RS vprhQwb N*x7FqʠRxuڴGIh`& o(FxݖY ۇho!r?FS[?6nNi+M.BcX䷼ɲ|f/DyT J4bt^aBvkO2A!{&iAd9`n!5j,s9*rlN<.4C=M4ks{GLPcOvrG4vAcXڅi#Yp'GяUhqShnPw_ܭ>q*n`l ;rjL١$&VO׫4&Zk ?Ew=t703 IQEk4{8b9Ve>%JÈ0˚Wt\R{9W=.i鋓ao9IW|WKke3 -VMRnM % ĪU q*įWI\EDFq$t! l7^bw bSD18Lݪhn?$̮NkjFeŸ &3ƅJͦ$$鋦&WMT2Q䮽o g·j C"L)ݾ^;*VIlHAl&T컕Ns- 9 @/p_˒˚cil؅rmP|B,0VaD+bEȹllw)GQTX/|)^,H$zٕC7fṋx7E}ь;ԛwIn!~'Ip |0wVZDF~'g .]{[P2+%vY)> U`Mu%NC" Xk4tpIO=-:ø+-JAwсwYM+J`ʅoEcr.+L#.E"xwmڱDZ*rRPP;PZ+x>VwX7Z>IR SbSsFbhtSES> f'L6It^Jls`qE +4r[%G4qUZsVZ e5jfԸfHf.ّ&p7y._(QgJ'cɨMl}y}KҶSҷvѢ#23qRw.jw?ΧGZ+" ZN2g/R'_J??'!1AQaq ?!7?}!aG!C[i_J9O{?;?;?[S?OSԻS]K& jO`iL=N9#.Jvq~Fz  ?d?s' uw`z/d%eC̣&CKxu?2+O0}GyHKܣK $o3D̖Fڋ6cKw(=%jctxcM+>)v(P<\^|iW9̶ŗԿEK$[Ss$gz1,Eٕ}zԿx/ @W@P!_${Y \\=Sw@/GO-G|{]F/(rGZ:9Y38"/reANLFq@7M)VY0[Z5 %`Lg/S^"[>D^yt'rbJX7?J 0F՛z`T5SLh NrNF-`t+"fsYL$`t]Y~mrsFb-Xуz%Ep-{l}PW凚NmN@U& }O?Jc<"r썎iJ 8Iщ6׏Xxڿlka̵ަx]ݳ%'U-TK6MsaRuyK c`~b@")r.a6ٖc&GzCizC>c\ѱk0[K(_3XkBqMaS'ljtu|p˂9R%vUZX @~ߋ {nLA,+vAq{V6/s>aqGgmVJ_wp|rRhV"cw0ԩ0X1%eS] 2P=UBX- q-.c*pݕsKŬ'PAAʷY:]o=L+l7<0I**e \<2 YÉed?SWXnX^~;("+3 ^bR|qpŠ9T/שf#` (C &`t ]\K$WlX֢z?yv#LH f/WޭuT۽WXSz! Qk{7  gىCGR@0eeA-qT"*̂=i(l`;0}<)%+9XOMG|m-{Z.y6ģ̧J?eEY^K(j -ЉQSJpF14UʫU+\oY2UA9'Sc+BS=VJSN~D7zq@>L,\Sf?i2X<${G0YT nAeH \9.[j0[s.0&Vu@eD pᑔ]jvJ\<xRilBnC,u4X»ւ8 ǡ)%5%Wq4r{,zS~8A{|-tyKw, fHG|#R0NM]5_U%9~~s]7eG C =6ܻ+yHX%p^QVe/pv]6oUJCO=OwrIm,:= Ŝ U^^bJwb3ԬUꦒ3f,;HxTB|#p:Uv%ķկޔzsY/? !r6\Z-@/0?Aq1IQi l[.:nNW_RD aX }?0{Duy%ܺ19i/; (<EoY:Ub+N{^v;#*O5 ҥe2d _’ȰeFxfkXaX_lmܾbWAȏ™F7SF^zثuw3/h3TM:Lʆi`+.L7Q.DeÖ>VQE0]QX0OY7 YoCYZ_8QvsL -܊'~byl Ir5,_)z&5w9@a+<=3qҶ="-! 'J%c+4bas2g^s\Ah^*R%׉@eOvjyMoǭNGW{@љb D.cc1 ?@R B[*r9pUXvԘarj% L1s,,pL`% ab?i/YehD4j #*;+&<fW8eKc6~M}' 7#Mr.U2݌O-4kRpYj4Rr?%t#˘gDPx!/8qQ`0N+ =ra\AW{Xs }e3L{M76v T}!|779/6N\=~Hm|+ȯ..>aT붭+B(s [pޣC`!+ yz%/<˒>Y,*efP(;0^HOo#r's/OobRn(WNJ*] Zw{ŭo>n#j٢qgDErb}!\uf_yf&o\A\1YK P^2R0wEA Oo[+*_Ưx_q]7 [zCG8!bcH)hh74o6(8R0^UMq/s*l'*. ؚaQԿ jg,Ebޥw: =H3#X[8Ѻ^*z=fJMzqDW%9[*syK{DX$7y2ZzE5b))_CD]%"LnoW^qY߃"Ҩ2sFgϘ~& k%i&_/bi  :evzocSq`f]n&3\)bFSo;G W4 s\* -U)JC/nOmqRT*{4fJZk0v.?="AܭyB1lEWUʭ J%l+->a]xtb;t-q} х԰ƺOpebWߩApICoFL1J`/a3c5,h/WGG2۾cU-?3a jB0x +-^@$I$鸪+'^ v 'G] sxɑ`(\EjƢD]Zڋ6%`C(\~u,r}*ܵL^Uk^^=!|KTvW!6Բ-gh*yfd"IgIrM+ŸƳ J9DQrƞ!U0e%Hp/6L`C|12QW. 6Ew2 թX;WL~LX !`uoxˆ(73zWܩpܬ!?0 vC8L)+/n,%Hj 1EEXrQ9ꡀC%%3 xYy}9Au((P`G^~r\ 3`ܭ8. ])J,!^R8}t>1eeX4aUSbPS+nn+7 3(j|v͜GUfO@z]Tz'>HZ5-;EON}]{KhܸWDzדZτMz^+OYn!c9(̃깘ՙvkD(cSd]'NW,tiK{U4ܢߟyO~=BMMm j_qg Ѭl++莿rҵܹפ5k/sݽFN u0紣?7."15m%dН`7v& ]fxf+j& =H9288rTG; ]u1W{?7 02 _P^Hrd{9!w3tm{!?t9TDPx']/n .#%1/3g:]zfWL bW:}-eԹ ̴;PI^7 <(/7Uyl(Usw j??A})WmC9Na}7cmQs;մu1? -߽|]ȖKv,h ED#3(L%r5Uf2c}ۯ鴛)dv). dYArkvʃ=)wO=TM#Q6jBhA#l|eZ< d e[ZMv|_@cGoquϣ$}\?8F%BhfVHdS G5{tSK pLO،ck|ԒDSMДgD_H;% ߂bmр~UQR&kF,DT;7n-a Dી@;A贍Q$_L-zeHDƼcC&dkDԬ2 9 5z_(1o@OMf@LEv*o\$xEmxbF?~$7ЌIKڹ߿Aa)܃MdPM>Ђ&%ODtswUma:(ֲQ#0-Կo`D#.oLؗ f>EHb;R>i/$; -tg9.H7U Ҷ8T2J&!1a AQq0@P?:::::::Bz:N?t'O)I!:_Et6+_Ew3+2{'d$o#.d!" c{yn߉]3)?&!B!lOR$^ _)!cQ. 4 ܈R׿5|T7A'V`R;BBHhĎT#ѠRfl!2 ׂW$qؕQI3WY̕s>LDEgKd#pze44^pX Ђ—2==~IN PX)I,]!iV" 4X:s #6)1O-Ol} _9Y~ 7=,uXc&3O\͌afAD6DT6Sk-}xS>qV;i8ii=A\:lkfU5;,n&Xdl>3E)Q!ω5O LR7E>J+l+VC@Yi>`I 9Gble,j `*`5e2kXY2!W3 ,QX9rATZRf\VЁpZaRx%qUHz~1Fĸ8cEQ7QI cBk7GHG0|Z!(ZbTHkb ї(bB4#KАc׊A 8;9.I2dx2$y8 Bππ%$=b* Aa 1522X'0 LnqTJiz=M& B7fb򵡴RBZl| Hgx^ 8E̙9gƨR_ \1TKtd—Jf%"5`Ȏ^HhL[P0”(n *bzŏoE{1n= 6 x `e>#b)zeU~ap3}cQSPJ4kƜ@H%. d%r9E,LD >Kߧc3.J4lY44;гbzKG 9)bZ&))9 ̖%ʡ" ir-Fr5 < }{ТAL2Mބ7p(- ĨKE**[ąbGN *!욃U DsÑQ= 3x&iJ7FT(āĭ= 2|wA'a9/:e~w;֫u?l?T|?}>_G俛8?&!1a AQ0q@?흳v;gl흳v;gl흳v;gl흃?`Ov>X읳vNt}+_Bwt} _E_ *_E}U𾊾W/K焾8/OEG7į:K_:ѯ h!BpVVKȑ 4B@&9Wʫ×qJR)Jr~>e)|)0^ïj ӞI;tX=%C䌌dd~ OU1 ȷu,@\z>Dπ= \7)5בGCBth؈C/e1^F'(757aXb!W"Rj(6쵍˂F>A 0$BEٔ1nV|d6gbCږ-ԡCo !%3x9‘4$C|hM#оq;ACL[8*—eFfl/bB ”YXJS,TQ pmr!y/3PxKGiAAxBFMIӁlTCF%+}_-0B2!v&A CT4lm7KHN,A"20%C,*qpe'hXXKx$ |"FzĈ$aҐVPZv Q@<\7bepBP \ M&͂Ӏ"D ЗF8G:F5|c{E>)$.0bTy,x{ƂKP&qpC{bP|`% 1Fc# Zq FB Fqڨ~bQ %|i" ⍱[6-k|xO VdJ&-3erE>G,7 9 ~ 54ncp&)Ǹr*"`hbIX(YΖ`/X](G9 D•l'{(yޅJ12OC,5 o/-!W 17po \!$YVr4z5# 6VX04! bƘ=*1X0Ȗ(;BQBlh8^ W0&\1z6jiiQͧ{zXL&4UYk -%ɥ]$%s.< 22k8?3G, o&RsG"{bAB"A&12 FҥMj%LFEr.6l+McgȀK lNmzBmi2 <5M- ؏B@InPܬI9DKLx-ZB/0[hѯbiD ݊'LaLӡ;>ob%<}H?O~~_uO_%t>?IM?G?MNE|~ׯ~藯z'zO_5G^><&!1AQaq?.;HZNxis.!_:r3>#TkJe8̵,75$tT|l\ڟ j/5#A|FdcEK})mTصoU& E)60#3$UleCwBF|IAΐpe#lvʆO1Kx4[wU2U6G:TY*o AUvWJvw7cKNVc\kp _ 9Kb '\E!D(8q-j^8(:۝{^o QtW+/o5}1?nXhh}ф3ld2)Y(ugC穷>9\߹cXBYkAN.o»c 1nЯ`y)WPkH!-=p/|\|Fj5 ĭ&:4ݿ ;+֔UBŒ^ ؀kVE2̬6ˌ%s"X-D(QUX 4RC'>ͪ -)I[:+Aw9ET`Q_P&+\A*PՀ`H [X@CX LUdykg[ĸ4lv*:ͮbWߍ"[Zia+ 8a\=.1_c,- D2ZZ©Sr@n@4 zXbm< A0ÉjX11{Y ݽچ @/+ɓ* X{ Э%2-2 [Hz)B $ET+qVQ2 SJ?/":y(Y`r> M5q~^Mj̿46NXkw^`!bp1,Gؖ%'nb< Pet!5VqbJZ+;[=9)bǘZ!}jXtK.Mj3xֱQP> :NKth}\s \Z U0=렠} X^IWv\Nh-f@hS < /\3 pU JN n+¸떡 Y\!ugǿ๕j(ıvkл7\w3Wzpg!F+ [dVfTY,|\VkW<,@jV1[5SQ#dm.㈀nBMWr0mPѯp5 n >%%ٞc/:s5GBҬ `l}1: 5ګƠEF 8u E/sB]8-.)ĹyQ򝬞w22"<@ŽW/!RSĪC*mԡʛEK6ߩjmVϨ)6@J8U5AK+n} n#+R3] *h+5tӺ^: Wj#8u :Dt.vl-(kTlA*5 f>1 ]T:[ S.>#qN K5 T@,))2")9ĨK/ ˨Z^+=p7 Zy 8GpSn3OAmu)sc]~`[m",H TnPΆZ\.^sh2/ocxwJ3 rUR2|\%[ǢJdqs]M֪so ۠‚U>hS0lZIjs g,CBKFXyeV6W'}=*t}Zbx_ʁ`_Hrc!P\fX`8eo?mg1˨ج"6+lWbޑZq+qa8㭳C(bMKSP`vfi[^0'7hn~a-_P4#=! !ϐvjU)1. !5/ N\Uk@6\)fr!fz ϸvd1 Lhl_y-*7-Ĉ2U _)q)sxJT\^}s;;}\CQqV}+ޮ7 @[YBULam=Z;gQU[;0@EFp,'Vy m]qpS/ѯV-e\ bQѤڿOGюF V\pkչs#O+ (;sfnt* 0®xdQPrk15X>%9we(͔Qf)U`Leg Vfg_p1t+cE:w+YŇwmWHh_l/ [r\ʭ]*1f5Xs Y}c@#,D j`cd70t3( ";-Z)p/~֭`^}tveX^},Ix{V|[ܶ';kQm'_ R&5tO'y8r,j Á7d " }Jܯk Z|TJPr9վACHvz@{ -jD TltݧT){3d8o,iTs)r,5Vn8x`t4yJ]8v&'SiW{&oEwW{kwX# -]3eL2Yppv_CdV\xl"] kUV4zs1sUJ8,ѻRw="fnZPX!44\:U #()_`REJ0VcgXզ ; {~(s)BINl#w`9#,/N*Lh b^@[2,Dc5Pv5{"+ۄso$a^sce`B\PO@PօAs՝cEk0`S$RUVdyEZl>j?E(j*~" ^Na)`Tp.cMCCG0k>co˳_=L1"gԸܰ)LH#B,iu `%1e3f>3Z.hQEcu%*f̼u.QR2hҀ b^?M\g#q"V  )hq CR߸mlTmu0CkW.] B6T hS"Te՝MxΙ,TB,ݕ2겛\y%͌#ü` UfVEޢˀPr,('(vAei*CuA+x("ڏ *2ic. 6GUZ";&r9&(XAcpu2^3%Qz+N;`P9Y- x\~ۊ^<, ޓn8`:(A* E*on11s2+O96ۘd@mڎLԨrjYLWQB/rԳn(a] 48Qi]7ݰ50GהmTpġ W4y ҏܛ2RrXq`7Yf?2VT0 Po%/Q4-NmeS ֍J9uP/8;j\ *+[fv0EB(%X4Sb0&0s'm )(`LApOḵŘb]3vx Be)bAk Ku+XV[akC;Mc0ݴ^mE9<:RbU諺ZXkLBمIօ;VUb rBۆ΃J =+]ԧv6w"Vט{@T`!ij)iP\j=%%s [Kg9xAb2e|UMBg3PU*֚92mdJ²oҙADurq.WlLx= *(ύV mчnck/p^Xjl}^"w}qT8Z-Ere[?5¡yquxA`zs`Khr x06*غ~%QV6W4<ƸW*jnB+*?ܧ+7DʂHLA6%(ibT 3iԨn5 }$ZBqT mzrO H,D,NÕP ^eq,O0Gf\1Cv}ԩWz^;62R]}yh.k}')Fs-\_c%N^:}̳I[_;TÒW !v g%/P/7-eoMAh-d>װV58{QG@\4)y!LA:@o, 6(&3*T9\!AC'nk#3c`+ #[C$h[.+G-̤1qT¡S'ULܲZV1( [TRr")?.p7^g4VxQE(MHG/5eGr쥧Ԥkqn9)Wէ`|#v^,1 f()c?+ 1C&/$BS}hR' ԋ^oەb F=}cu.iks\:-՟*)v;K >_繼UhJ Ul!/Lr+&uFB$ `]xLTZV01ĵā :J}](a0`z6;# 9t*HkO\̎E=.d01!2MY@5Ķ*Ì?RRmjhhsϛܧ7ԡ6|^!_ZP(,Yh4Pb$%pR_1Q\Z/+` _@ O9<Gxn-6D"dc^_y)-ך1HjP xi눃ɷn3`|, /Occ|QA/[ZXJ8@dP|QA3Va_qGQb% r5qgW'4.ZRB!XjP-3B 82XwM H2T `gA oP6Q8 9 GLfU dil|9$/ho&{7#LV-eoE'_{8)4Qk\ cZQq$Fcݸ "a,Ĺu  `pb-]> nX*/17F*(e@+?><-fǡke<1zHZ}D3gەڳn'dU0kp7X*AnLSv;H-5}kׂh<V_3N]uQ80Wj({Vˈm @`ʀr\b;t@mSn5K=o%ޚ,_@-⬳uIv% J3mkiP*VPȥ c$MlGJ\?(2Zr!U#hfj0PZq&6aT0davy|J\}t8hۘk׸h%p@:(նe\%J @E1-`S%{!_ 4/u+U$2)]A+@[γ QګOsyWbLH< BYlnW╡b*n6,Mfʫ2R |6QsFKmPGtyTl:}LVOk"=-v1%&h }̣d5ͼLb6Vq4Q 2xfCNBQ;Ju-K}z`>y5kZ)`P +RUZQ,ViX^:&v6uztf{[oԡ0ysn&qQ e,*Wy~eυY3T:lsZg")im'1&\Q9uKS2t!#7QqlAf!z ]$wKh2UU Dr B ,-|qʶVs.u"!Q~dnn*$.= GAgU az%ug9X/ `g$hE7W6bX`. b?EDev4Aq miY  6TAH5ViD\0U`q qWweE!pbzh( :X<7DD/`;N@@d>%]Hxs7@paCyzeȷ8(e"f f9ujG R7). 9#d[x^tce UF+\ص,CRbI1VZI/I h3*`t榼~T>AY_QA+e p`'Bɮ#%[G-iQ@CgT_pɏi8@6E < 0eL2Y㨺_j l-y"!X׸"7: R$y \ko9#0 UwW ٢|sT}1(ɏTWV[>aѯJ9?$ &(/~RL.&0ha[\X̭@౫e8XxKqע Gx\+E.rH)rPw-M})u9ɢK/F3kx8s6A *..sb70gwWй/#ťU|4,R s/'.1/!B29 @zME et@e!qƨZߒHl@,&993"\PrRȂ? 5~M` UtjmTZS8{fO@KmK'#o Aٟ0r˿ib uO1P-"PiA k{HPY$AfR;,Pl1+cFʼnlq ̵ĢJFL/g 2/I erYqл7lBIEE*`j2El9^ \dnU药 VP7)WeSъ],$':/mE)nOLA#Y`vWZ x3gP*jj V`p bv ?qIly]iOp6j!ac5:߈Cf LyV?>CʃWn$]3sGCxT0K 1h.34 ݸU1O~Weu,\98a,䜝U6W8zO9}a8)Bm[əp_0j̾=ʀx%bqš3 4_uz"2bx:UKJUy\0&#ck3HJ}E*)"%Gl]W aC -.7 NK?4{g?ii8^;aj__h6s_k46?/?ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/position/index.html000066400000000000000000000004021231750357400247600ustar00rootroot00000000000000 jQuery UI Position Demo

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/000077500000000000000000000000001231750357400234545ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/default.html000066400000000000000000000012611231750357400257660ustar00rootroot00000000000000 jQuery UI Progressbar - Default functionality

Default determinate progress bar.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/images/000077500000000000000000000000001231750357400247215ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/images/pbar-ani.gif000066400000000000000000000174421231750357400271110ustar00rootroot00000000000000GIF89a0JΔŔŌsskRkcJBJB֜! NETSCAPE2.0! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP ! XMP DataXMP !,0pIͻ"di"l)k1|+plJ cպfxL./f϶q} zy| ¶žȝʰ!,0ЉAǻc di(il0߱| n?!gD:KbyTFaSj" xL.^6|N|q} z{~ øƢɩ!,0I18 AHihZb뾰H8l$|@-Ga1C:bTFuTfxL.ޯ/|nj{N8~ {|w} ý Űȶˤ!,0I)8k !bh辰[8l$|@-G$aC:{eIJ_MjjxL.ޯ/|nj{N8~ {}w ľ Ʊɷ̦!,0ĸ8}ChAplͺrI`+x¤h!a*SXպPCaL.-'Fu{~3 }y ¾ ǻɹ̲!,09Ÿ8}`(a h,jp,Kﳑp&RiD|(Ij z]fK4z-&{p ~ ~~y #  ĴǷž ˳г!,0I8qx`("aHlf,7\ﴑpqI4"wJt80 }} { b z ®˽ɸ ϱԭ!,0I#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c þȺƴ ̯!,0I#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c { ɼǶ ͯ!,0Ik#{O($Il)t}ov_pH,y$d"=`s*|BԬ:fX\(xFCyng4Qx/: ~~| |c ʻȶ !,0Iubͻ#dYl뾫tm*ۆpH,2ul6K_IBMNi<.nft } }d ­˼ɷ ϰ!,00ISͻ!dil)kI`/H$HK:Kf|F#j5ixxpPnzng`Mo㾮x/> ~~|` ­˼ɷ ϰҿ!,0pઽ8W!DihZHp<4ʸ\六p#v>h<ږ`әJFSj5 ~~|a ­˼ɷ ϰҽ!,0p8k+`"AhAp,&kI}pH,{d&˦t{Si՚fX\(xFCyng4Qrax } }c ­˼ɷ ϰһ!,0p8k+`"AhAp,&kIRVoH,{plJs:FUL*R xL.&.u{~w~ {{}y y; Ǹų ˬ !,0p)8}`(bh,jp,ޭ1{ظG,>g@:eI%FfzŦ[@.h28Lkqbt80 }}{ {< ÿɺǵ ͮ!,0pI8}`(bh,jp,ޭ1{ظG,>g@:eI%FfzŦ[@.h28|5sa{ 0 }} { z îƶʼ ϱѶ!,0pI .{"dDlf,n]q<@G,ȡqyDLyFZ4xr{^l>xٝ~ wyx Űȸ̾ ѳӶ!,0pI+ .{O($ l)t}oe' n$PHP%ZSjyf+nj(,'>xo/ ~~ | {= þƶɹͿ Ҵ!,0pI .{O($ l)t}oe' n$PHP%ZSjyf+nj(,'>xo/ ~~ | {= þƶɹ ͵Ӵ;ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/indeterminate.html000066400000000000000000000030601231750357400271710ustar00rootroot00000000000000 jQuery UI Progressbar - Indeterminate Value

Indeterminate progress bar and switching between determinate and indeterminate styles.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/index.html000066400000000000000000000004751231750357400254570ustar00rootroot00000000000000 jQuery UI Progressbar Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/progressbar/label.html000066400000000000000000000024721231750357400254260ustar00rootroot00000000000000 jQuery UI Progressbar - Custom Label
Loading...

Custom updated label demo.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/removeClass/000077500000000000000000000000001231750357400234065ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/removeClass/default.html000066400000000000000000000025631231750357400257260ustar00rootroot00000000000000 jQuery UI Effects - removeClass Demo
Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede.
Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/removeClass/index.html000066400000000000000000000003171231750357400254040ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/000077500000000000000000000000001231750357400231035ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/animate.html000066400000000000000000000021611231750357400254070ustar00rootroot00000000000000 jQuery UI Resizable - Animate

Animate

Animate the resize action using the animate option (boolean). When this option is set to true, drag the outline to the desired location; the element animates to that size on drag stop.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/aspect-ratio.html000066400000000000000000000021251231750357400263640ustar00rootroot00000000000000 jQuery UI Resizable - Preserve aspect ratio

Preserve aspect ratio

Maintain the existing aspect ratio or set a new one to constrain the proportions on resize. Set the aspectRatio option to true, and optionally pass in a new ratio (i.e., 4/3)

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/constrain-area.html000066400000000000000000000024231231750357400267000ustar00rootroot00000000000000 jQuery UI Resizable - Constrain resize area

Containment

Resizable

Define the boundaries of the resizable area. Use the containment option to specify a parent DOM element or a jQuery selector, like 'document.'

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/default.html000066400000000000000000000017641231750357400254250ustar00rootroot00000000000000 jQuery UI Resizable - Default functionality

Resizable

Enable any DOM element to be resizable. With the cursor grab the right or bottom border and drag to the desired width or height.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/delay-start.html000066400000000000000000000025341231750357400262260ustar00rootroot00000000000000 jQuery UI Resizable - Delay start

Time delay (ms):

Time

Distance delay (px):

Distance

Delay the start of resizng for a number of milliseconds with the delay option; prevent resizing until the cursor is held down and dragged a specifed number of pixels with the distance option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/helper.html000066400000000000000000000020511231750357400252460ustar00rootroot00000000000000 jQuery UI Resizable - Helper

Helper

Display only an outline of the element while resizing by setting the helper option to a CSS class.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/index.html000066400000000000000000000014111231750357400250750ustar00rootroot00000000000000 jQuery UI Resizable Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/max-min.html000066400000000000000000000022051231750357400253360ustar00rootroot00000000000000 jQuery UI Resizable - Maximum / minimum size

Resize larger / smaller

Limit the resizable element to a maximum or minimum height or width using the maxHeight, maxWidth, minHeight, and minWidth options.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/snap-to-grid.html000066400000000000000000000017751231750357400263070ustar00rootroot00000000000000 jQuery UI Resizable - Snap to grid

Grid

Snap the resizable element to a grid. Set the dimensions of grid cells (height and width in pixels) with the grid option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/synchronous-resize.html000066400000000000000000000023701231750357400276640ustar00rootroot00000000000000 jQuery UI Resizable - Synchronous resize

Resize

will also resize

Resize multiple elements simultaneously by clicking and dragging the sides of one. Pass a shared selector into the alsoResize option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/textarea.html000066400000000000000000000015701231750357400256110ustar00rootroot00000000000000 jQuery UI Resizable - Textarea

Display only an outline of the element while resizing by setting the helper option to a CSS class.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/resizable/visual-feedback.html000066400000000000000000000020751231750357400270220ustar00rootroot00000000000000 jQuery UI Resizable - Visual feedback

Ghost

Instead of showing the actual element during resize, set the ghost option to true to show a semi-transparent part of the element.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/selectable/000077500000000000000000000000001231750357400232265ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/selectable/default.html000066400000000000000000000027141231750357400255440ustar00rootroot00000000000000 jQuery UI Selectable - Default functionality
  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
  5. Item 5
  6. Item 6
  7. Item 7

Enable a DOM element (or group of elements) to be selectable. Draw a box with your cursor to select items. Hold down the Ctrl key to make multiple non-adjacent selections.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/selectable/display-grid.html000066400000000000000000000030701231750357400265040ustar00rootroot00000000000000 jQuery UI Selectable - Display as grid
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12

To arrange selectable items as a grid, give them identical dimensions and float them using CSS.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/selectable/index.html000066400000000000000000000004751231750357400252310ustar00rootroot00000000000000 jQuery UI Selectable Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/selectable/serialize.html000066400000000000000000000033171231750357400261070ustar00rootroot00000000000000 jQuery UI Selectable - Serialize

You've selected: none.

  1. Item 1
  2. Item 2
  3. Item 3
  4. Item 4
  5. Item 5
  6. Item 6

Write a function that fires on the stop event to collect the index values of selected items. Present values as feedback, or pass as a data string.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/show/000077500000000000000000000000001231750357400221035ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/show/default.html000066400000000000000000000062051231750357400244200ustar00rootroot00000000000000 jQuery UI Effects - Show Demo

Show

Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede. Nulla lorem metus, adipiscing ut, luctus sed, hendrerit vitae, mi.

Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/show/index.html000066400000000000000000000003171231750357400241010ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/000077500000000000000000000000001231750357400224055ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/colorpicker.html000066400000000000000000000044661231750357400256210ustar00rootroot00000000000000 jQuery UI Slider - Colorpicker

Simple Colorpicker

Combine three sliders to create a simple RGB colorpicker.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/default.html000066400000000000000000000014161231750357400247210ustar00rootroot00000000000000 jQuery UI Slider - Default functionality

The basic slider is horizontal and has a single handle that can be moved with the mouse or by using the arrow keys.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/hotelrooms.html000066400000000000000000000026001231750357400254640ustar00rootroot00000000000000 jQuery UI Slider - Slider bound to select

How to bind a slider to an existing select element. The select stays visible to display the change. When the select is changed, the slider is updated, too.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/index.html000066400000000000000000000014401231750357400244010ustar00rootroot00000000000000 jQuery UI Slider Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/multiple-vertical.html000066400000000000000000000033771231750357400267470ustar00rootroot00000000000000 jQuery UI Slider - Multiple sliders

Master volume

Graphic EQ

88 77 55 33 40 45 70

Combine horizontal and vertical sliders, each with their own options, to create the UI for a music player.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/range-vertical.html000066400000000000000000000025351231750357400262030ustar00rootroot00000000000000 jQuery UI Slider - Vertical range slider

Change the orientation of the range slider to vertical. Assign a height value via .height() or by setting the height through CSS, and set the orientation option to "vertical."

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/range.html000066400000000000000000000024571231750357400243770ustar00rootroot00000000000000 jQuery UI Slider - Range slider

Set the range option to true to capture a range of values with two drag handles. The space between the handles is filled with a different background color to indicate those values are selected.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/rangemax.html000066400000000000000000000022241231750357400250750ustar00rootroot00000000000000 jQuery UI Slider - Range with fixed maximum

Fix the maximum value of the range slider so that the user can only select a minimum. Set the range option to "max."

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/rangemin.html000066400000000000000000000022261231750357400250750ustar00rootroot00000000000000 jQuery UI Slider - Range with fixed minimum

Fix the minimum value of the range slider so that the user can only select a maximum. Set the range option to "min."

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/side-scroll.html000066400000000000000000000122441231750357400255160ustar00rootroot00000000000000 jQuery UI Slider - Slider scrollbar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Use a slider to manipulate the positioning of content on the page. In this case, it acts as a scrollbar with the potential to capture values if needed.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/slider-vertical.html000066400000000000000000000023531231750357400263670ustar00rootroot00000000000000 jQuery UI Slider - Vertical slider

Change the orientation of the slider to vertical. Assign a height value via .height() or by setting the height through CSS, and set the orientation option to "vertical."

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/slider/steps.html000066400000000000000000000022331231750357400244310ustar00rootroot00000000000000 jQuery UI Slider - Snap to increments

Increment slider values with the step option set to an integer, commonly a dividend of the slider's maximum value. The default increment is 1.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/000077500000000000000000000000001231750357400227365ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/connect-lists-through-tabs.html000066400000000000000000000043651231750357400310260ustar00rootroot00000000000000 jQuery UI Sortable - Connect lists with Tabs
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5

Sort items from one list into another and vice versa, by dropping the list item on the appropriate tab above.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/connect-lists.html000066400000000000000000000033711231750357400264150ustar00rootroot00000000000000 jQuery UI Sortable - Connect lists
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5

Sort items from one list into another and vice versa, by passing a selector into the connectWith option. The simplest way to do this is to group all related lists with a CSS class, and then pass that class into the sortable function (i.e., connectWith: '.myclass').

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/default.html000066400000000000000000000035171231750357400252560ustar00rootroot00000000000000 jQuery UI Sortable - Default functionality
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5
  • Item 6
  • Item 7

Enable a group of DOM elements to be sortable. Click on and drag an element to a new spot within the list, and the other items will adjust to fit. By default, sortable items share draggable properties.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/delay-start.html000066400000000000000000000033341231750357400260600ustar00rootroot00000000000000 jQuery UI Sortable - Delay start

Time delay of 300ms:

  • Item 1
  • Item 2
  • Item 3
  • Item 4

Distance delay of 15px:

  • Item 1
  • Item 2
  • Item 3
  • Item 4

Prevent accidental sorting either by delay (time) or distance. Set a number of milliseconds the element needs to be dragged before sorting starts with the delay option. Set a distance in pixels the element needs to be dragged before sorting starts with the distance option.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/display-grid.html000066400000000000000000000027061231750357400262210ustar00rootroot00000000000000 jQuery UI Sortable - Display as grid
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

To arrange sortable items as a grid, give them identical dimensions and float them using CSS.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/empty-lists.html000066400000000000000000000036441231750357400261250ustar00rootroot00000000000000 jQuery UI Sortable - Handle empty lists
  • Can be dropped..
  • ..on an empty list
  • Item 3
  • Item 4
  • Item 5
  • Cannot be dropped..
  • ..on an empty list
  • Item 3
  • Item 4
  • Item 5

Prevent all items in a list from being dropped into a separate, empty list using the dropOnEmpty option set to false. By default, sortable items can be dropped on empty lists.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/index.html000066400000000000000000000012531231750357400247340ustar00rootroot00000000000000 jQuery UI Sortable Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/items.html000066400000000000000000000040041231750357400247430ustar00rootroot00000000000000 jQuery UI Sortable - Include / exclude items

Specify which items are sortable:

  • Item 1
  • (I'm not sortable or a drop target)
  • (I'm not sortable or a drop target)
  • Item 4

Cancel sorting (but keep as drop targets):

  • Item 1
  • (I'm not sortable)
  • (I'm not sortable)
  • Item 4

Specify which items are eligible to sort by passing a jQuery selector into the items option. Items excluded from this option are not sortable, nor are they valid targets for sortable items.

To only prevent sorting on certain items, pass a jQuery selector into the cancel option. Cancelled items remain valid sort targets for others.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/placeholder.html000066400000000000000000000032201231750357400261030ustar00rootroot00000000000000 jQuery UI Sortable - Drop placeholder
  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5
  • Item 6
  • Item 7

When dragging a sortable item to a new location, other items will make room for the that item by shifting to allow white space between them. Pass a class into the placeholder option to style that space to be visible. Use the boolean forcePlaceholderSize option to set dimensions on the placeholder.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/sortable/portlets.html000066400000000000000000000052601231750357400255030ustar00rootroot00000000000000 jQuery UI Sortable - Portlets
Feeds
Lorem ipsum dolor sit amet, consectetuer adipiscing elit
News
Lorem ipsum dolor sit amet, consectetuer adipiscing elit
Shopping
Lorem ipsum dolor sit amet, consectetuer adipiscing elit
Links
Lorem ipsum dolor sit amet, consectetuer adipiscing elit
Images
Lorem ipsum dolor sit amet, consectetuer adipiscing elit

Enable portlets (styled divs) as sortables and use the connectWith option to allow sorting between columns.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/000077500000000000000000000000001231750357400226015ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/currency.html000066400000000000000000000027101231750357400253210ustar00rootroot00000000000000 jQuery UI Spinner - Currency

Example of a donation form, with currency selection and amount spinner.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/decimal.html000066400000000000000000000032621231750357400250700ustar00rootroot00000000000000 jQuery UI Spinner - Decimal

Example of a decimal spinner. Step is set to 0.01.
The code handling the culture change reads the current spinner value, then changes the culture, then sets the value again, resulting in an updated formatting, based on the new culture.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/default.html000066400000000000000000000030321231750357400251110ustar00rootroot00000000000000 jQuery UI Spinner - Default functionality

Default spinner.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/index.html000066400000000000000000000006521231750357400246010ustar00rootroot00000000000000 jQuery UI Spinner Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/latlong.html000066400000000000000000000026271231750357400251360ustar00rootroot00000000000000 jQuery UI Spinner - Map

Google Maps integration, using spinners to change latidude and longitude.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/overflow.html000066400000000000000000000021671231750357400253400ustar00rootroot00000000000000 jQuery UI Spinner - Overflow

Overflowing spinner restricted to a range of -10 to 10. For anything above 10, it'll overflow to -10, and the other way round.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/spinner/time.html000066400000000000000000000036661231750357400244400ustar00rootroot00000000000000 jQuery UI Spinner - Time

A custom widget extending spinner. Use the Globalization plugin to parse and output a timestamp, with custom step and page options. Cursor up/down spins minutes, page up/down spins hours.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/switchClass/000077500000000000000000000000001231750357400234125ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/switchClass/default.html000066400000000000000000000025031231750357400257240ustar00rootroot00000000000000 jQuery UI Effects - switchClass Demo
Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede.
Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/switchClass/index.html000066400000000000000000000003171231750357400254100ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/000077500000000000000000000000001231750357400220545ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax.html000066400000000000000000000041011231750357400236610ustar00rootroot00000000000000 jQuery UI Tabs - Content via Ajax

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Fetch external content via Ajax for the tabs by setting an href value in the tab links. While the Ajax request is waiting for a response, the tab label changes to say "Loading...", then returns to the normal label once loaded.

Tabs 3 and 4 demonstrate slow-loading and broken AJAX tabs, and how to handle serverside errors in those cases. Note: These two require a webserver to interpret PHP. They won't work from the filesystem.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax/000077500000000000000000000000001231750357400227775ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax/content1.html000066400000000000000000000042531231750357400254240ustar00rootroot00000000000000

This content was loaded via ajax.

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Mauris vitae ante. Curabitur augue. Nulla purus nibh, lobortis ut, feugiat at, aliquam id, purus. Sed venenatis, lorem venenatis volutpat commodo, purus quam lacinia justo, mattis interdum pede pede a odio. Fusce nibh. Morbi nisl mauris, dapibus in, tristique eget, accumsan et, pede. Donec mauris risus, pulvinar ut, faucibus eu, mollis in, nunc. In augue massa, commodo a, cursus vehicula, varius eu, dui. Suspendisse sodales suscipit lorem. Morbi malesuada, eros quis condimentum dignissim, lectus nibh tristique urna, non bibendum diam massa vel risus. Morbi suscipit. Proin egestas, eros at scelerisque scelerisque, dolor lacus fringilla lacus, ut ullamcorper mi magna at quam. Aliquam sed elit. Aliquam turpis purus, congue quis, iaculis id, ullamcorper sit amet, justo. Maecenas sed mauris. Proin magna justo, interdum in, tincidunt eu, viverra eu, turpis. Suspendisse mollis. In magna. Phasellus pellentesque, urna pellentesque convallis pellentesque, augue sem blandit pede, at rhoncus libero nisl a odio.

Sed vitae nibh non magna semper tempor. Duis dolor. Nam congue laoreet arcu. Fusce lobortis enim quis ligula. Maecenas commodo odio id mi. Maecenas scelerisque tellus eu odio. Etiam dolor purus, lacinia a, imperdiet in, aliquam et, eros. In pellentesque. Nullam ac massa. Integer et turpis. Ut quam augue, congue non, imperdiet id, eleifend ac, nisi. Etiam ac arcu. Cras iaculis accumsan erat. Nullam vulputate sapien nec nisi pretium rhoncus. Aliquam a nibh. Vivamus est ante, fermentum a, tincidunt ut, imperdiet nec, velit. Aenean non tortor. Sed nec mauris eget tellus condimentum rutrum.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax/content2.html000066400000000000000000000041541231750357400254250ustar00rootroot00000000000000

This other content was loaded via ajax.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nec turpis justo, et facilisis ligula. In congue interdum odio, a scelerisque eros posuere ac. Aenean massa tellus, dictum sit amet laoreet ut, aliquam in orci. Duis eu aliquam ligula. Nullam vel placerat ligula. Fusce venenatis viverra dictum. Phasellus dui dolor, imperdiet in sodales at, mattis sed libero. Morbi ac ipsum ligula. Quisque suscipit dui vel diam pretium nec cursus lacus malesuada. Donec sollicitudin, eros eget dignissim mollis, risus leo feugiat tellus, vel posuere nisl ipsum eu erat. Quisque posuere lacinia imperdiet. Quisque nunc leo, elementum quis ultricies et, vehicula sit amet turpis. Nullam sed nunc nec nibh condimentum mattis. Quisque sed ligula sit amet nisi ultricies bibendum eget id nisi.

Proin ut erat vel nunc tincidunt commodo. Curabitur feugiat, nisi et vehicula viverra, nisl orci eleifend arcu, sed blandit lectus nisl quis nisi. In hac habitasse platea dictumst. In hac habitasse platea dictumst. Aenean rutrum gravida velit ac imperdiet. Integer vitae arcu risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin tincidunt orci at leo egestas porta. Vivamus ac augue et enim bibendum hendrerit ut id urna. Donec sollicitudin pulvinar turpis vitae scelerisque. Etiam tempor porttitor est sed blandit. Phasellus varius consequat leo eget tincidunt. Aliquam ac dui lectus. In et consectetur orci. Duis posuere nulla ac turpis faucibus vestibulum. Sed ut velit et dolor rhoncus dapibus. Sed sit amet pellentesque est.

Nam in volutpat orci. Morbi sit amet orci in erat egestas dignissim. Etiam mi sapien, tempus sed iaculis a, adipiscing quis tellus. Suspendisse potenti. Nam malesuada tristique vestibulum. In tempor tellus dignissim neque consectetur eu vestibulum nisl pellentesque. Phasellus ultrices cursus velit, id aliquam nisl fringilla quis. Cras varius elit sed urna ultrices congue. Sed ornare odio sed velit pellentesque id varius nisl sodales. Sed auctor ligula egestas mi pharetra ut consectetur erat pharetra.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax/content3-slow.php000066400000000000000000000042221231750357400262270ustar00rootroot00000000000000

This content was loaded via ajax, though it took a second.

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean nec turpis justo, et facilisis ligula. In congue interdum odio, a scelerisque eros posuere ac. Aenean massa tellus, dictum sit amet laoreet ut, aliquam in orci. Duis eu aliquam ligula. Nullam vel placerat ligula. Fusce venenatis viverra dictum. Phasellus dui dolor, imperdiet in sodales at, mattis sed libero. Morbi ac ipsum ligula. Quisque suscipit dui vel diam pretium nec cursus lacus malesuada. Donec sollicitudin, eros eget dignissim mollis, risus leo feugiat tellus, vel posuere nisl ipsum eu erat. Quisque posuere lacinia imperdiet. Quisque nunc leo, elementum quis ultricies et, vehicula sit amet turpis. Nullam sed nunc nec nibh condimentum mattis. Quisque sed ligula sit amet nisi ultricies bibendum eget id nisi.

Proin ut erat vel nunc tincidunt commodo. Curabitur feugiat, nisi et vehicula viverra, nisl orci eleifend arcu, sed blandit lectus nisl quis nisi. In hac habitasse platea dictumst. In hac habitasse platea dictumst. Aenean rutrum gravida velit ac imperdiet. Integer vitae arcu risus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin tincidunt orci at leo egestas porta. Vivamus ac augue et enim bibendum hendrerit ut id urna. Donec sollicitudin pulvinar turpis vitae scelerisque. Etiam tempor porttitor est sed blandit. Phasellus varius consequat leo eget tincidunt. Aliquam ac dui lectus. In et consectetur orci. Duis posuere nulla ac turpis faucibus vestibulum. Sed ut velit et dolor rhoncus dapibus. Sed sit amet pellentesque est.

Nam in volutpat orci. Morbi sit amet orci in erat egestas dignissim. Etiam mi sapien, tempus sed iaculis a, adipiscing quis tellus. Suspendisse potenti. Nam malesuada tristique vestibulum. In tempor tellus dignissim neque consectetur eu vestibulum nisl pellentesque. Phasellus ultrices cursus velit, id aliquam nisl fringilla quis. Cras varius elit sed urna ultrices congue. Sed ornare odio sed velit pellentesque id varius nisl sodales. Sed auctor ligula egestas mi pharetra ut consectetur erat pharetra.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/ajax/content4-broken.php000066400000000000000000000000531231750357400265220ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/bottom.html000066400000000000000000000076401231750357400242550ustar00rootroot00000000000000 jQuery UI Tabs - Tabs at bottom

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

With some additional CSS (for positioning) and JS (to put the right classes on elements) the tabs can be placed below their content.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/collapsible.html000066400000000000000000000060301231750357400252320ustar00rootroot00000000000000 jQuery UI Tabs - Collapse content

Click this tab again to close the content pane.

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Click this tab again to close the content pane.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Click this tab again to close the content pane.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Click the selected tab to toggle its content closed/open. To enable this functionality, set the collapsible option to true.

collapsible: true
ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/default.html000066400000000000000000000062151231750357400243720ustar00rootroot00000000000000 jQuery UI Tabs - Default functionality

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Click tabs to swap between content that is broken into logical sections.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/index.html000066400000000000000000000010261231750357400240500ustar00rootroot00000000000000 jQuery UI Tabs Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/manipulation.html000066400000000000000000000102261231750357400254430ustar00rootroot00000000000000 jQuery UI Tabs - Simple manipulation

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Simple tabs adding and removing.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/mouseover.html000066400000000000000000000063201231750357400247670ustar00rootroot00000000000000 jQuery UI Tabs - Open on mouseover

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Toggle sections open/closed on mouseover with the event option. The default value for event is "click."

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/sortable.html000066400000000000000000000067001231750357400245600ustar00rootroot00000000000000 jQuery UI Tabs - Sortable

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Drag the tabs above to re-order them.

Making tabs sortable is as simple as calling .sortable() on the .ui-tabs-nav element.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tabs/vertical.html000066400000000000000000000076631231750357400245670ustar00rootroot00000000000000 jQuery UI Tabs - Vertical Tabs functionality

Content heading 1

Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus. Curabitur nec arcu. Donec sollicitudin mi sit amet mauris. Nam elementum quam ullamcorper ante. Etiam aliquet massa et lorem. Mauris dapibus lacus auctor risus. Aenean tempor ullamcorper leo. Vivamus sed magna quis ligula eleifend adipiscing. Duis orci. Aliquam sodales tortor vitae ipsum. Aliquam nulla. Duis aliquam molestie erat. Ut et mauris vel pede varius sollicitudin. Sed ut dolor nec orci tincidunt interdum. Phasellus ipsum. Nunc tristique tempus lectus.

Content heading 2

Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis. Sed fringilla, massa eget luctus malesuada, metus eros molestie lectus, ut tempus eros massa ut dolor. Aenean aliquet fringilla sem. Suspendisse sed ligula in ligula suscipit aliquam. Praesent in eros vestibulum mi adipiscing adipiscing. Morbi facilisis. Curabitur ornare consequat nunc. Aenean vel metus. Ut posuere viverra nulla. Aliquam erat volutpat. Pellentesque convallis. Maecenas feugiat, tellus pellentesque pretium posuere, felis lorem euismod felis, eu ornare leo nisi vel felis. Mauris consectetur tortor et purus.

Content heading 3

Mauris eleifend est et turpis. Duis id erat. Suspendisse potenti. Aliquam vulputate, pede vel vehicula accumsan, mi neque rutrum erat, eu congue orci lorem eget lorem. Vestibulum non ante. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Fusce sodales. Quisque eu urna vel enim commodo pellentesque. Praesent eu risus hendrerit ligula tempus pretium. Curabitur lorem enim, pretium nec, feugiat nec, luctus a, lacus.

Duis cursus. Maecenas ligula eros, blandit nec, pharetra at, semper at, magna. Nullam ac lacus. Nulla facilisi. Praesent viverra justo vitae neque. Praesent blandit adipiscing velit. Suspendisse potenti. Donec mattis, pede vel pharetra blandit, magna ligula faucibus eros, id euismod lacus dolor eget odio. Nam scelerisque. Donec non libero sed nulla mattis commodo. Ut sagittis. Donec nisi lectus, feugiat porttitor, tempor ac, tempor vitae, pede. Aenean vehicula velit eu tellus interdum rutrum. Maecenas commodo. Pellentesque nec elit. Fusce in lacus. Vivamus a libero vitae lectus hendrerit hendrerit.

Click tabs to swap between content that is broken into logical sections.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggle/000077500000000000000000000000001231750357400224045ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggle/default.html000066400000000000000000000057121231750357400247230ustar00rootroot00000000000000 jQuery UI Effects - Toggle Demo

Toggle

Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede. Nulla lorem metus, adipiscing ut, luctus sed, hendrerit vitae, mi.

Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggle/index.html000066400000000000000000000003171231750357400244020ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggleClass/000077500000000000000000000000001231750357400233725ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggleClass/default.html000066400000000000000000000023671231750357400257140ustar00rootroot00000000000000 jQuery UI Effects - toggleClass Demo
Etiam libero neque, luctus a, eleifend nec, semper at, lorem. Sed pede.
Run Effect

Click the button above to preview the effect.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/toggleClass/index.html000066400000000000000000000003171231750357400253700ustar00rootroot00000000000000 jQuery UI Effects Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/000077500000000000000000000000001231750357400226155ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/ajax/000077500000000000000000000000001231750357400235405ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/ajax/content1.html000066400000000000000000000000711231750357400261570ustar00rootroot00000000000000

This content was loaded via ajax.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/ajax/content2.html000066400000000000000000000000771231750357400261660ustar00rootroot00000000000000

This other content was loaded via ajax.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/custom-animation.html000066400000000000000000000032661231750357400270010ustar00rootroot00000000000000 jQuery UI Tooltip - Custom animation demo

There are various ways to customize the animation of a tooltip.

You can use the show and hide options.

You can also use the open event.

This demo shows how to customize animations using the show and hide options, as well as the open event.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/custom-content.html000066400000000000000000000052101231750357400264630ustar00rootroot00000000000000 jQuery UI Tooltip - Custom content

St. Stephen's Cathedral

Vienna, Austria

St. Stephen's Cathedral

Tower Bridge

London, England

Tower Bridge

All images are part of Wikimedia Commons and are licensed under CC BY-SA 3.0 by the copyright holder.

Shows how to combine different event delegated tooltips into a single instance, by customizing the items and content options.

We realize you may want to interact with the map tooltips. This is a planned feature for a future version.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/custom-style.html000066400000000000000000000051121231750357400261520ustar00rootroot00000000000000 jQuery UI Tooltip - Custom Styling

Tooltips can be attached to any element. When you hover the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.

But as it's not a native tooltip, it can be styled. Any themes built with ThemeRoller will also style tooltips accordingly.

Tooltips are also useful for form elements, to show some additional information in the context of each field.

Hover the field to see the tooltip.

Hover the links above or use the tab key to cycle the focus on each element.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/default.html000066400000000000000000000030021231750357400251220ustar00rootroot00000000000000 jQuery UI Tooltip - Default functionality

Tooltips can be attached to any element. When you hover the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.

But as it's not a native tooltip, it can be styled. Any themes built with ThemeRoller will also style tooltips accordingly.

Tooltips are also useful for form elements, to show some additional information in the context of each field.

Hover the field to see the tooltip.

Hover the links above or use the tab key to cycle the focus on each element.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/forms.html000066400000000000000000000032321231750357400246310ustar00rootroot00000000000000 jQuery UI Tooltip - Forms

Use the button below to display the help texts, or just focus or mouseover the indivdual inputs.

A fixed width is defined in CSS to make the tooltips look consistent when displayed all at once.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/images/000077500000000000000000000000001231750357400240625ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/images/st-stephens.jpg000066400000000000000000000424741231750357400270540ustar00rootroot00000000000000ExifII*Ducky<+http://ns.adobe.com/xap/1.0/ Adobed        !1AQ"2aq3B#Rbr$4C5ᢲSc%sD6!1AQa"q2𑡱B#3Rr ?k$9'd, 57y$jnH$jnJnJnJn 7y@IC9j@'|؇d^$oЮ?>. 7y$jnpAf?> *Kw$jn y@Àς$n'Œ87(LF`0`zx.cшκ*+p<.]?z^5c 5=)D0 +LT*pW ɧkanV~%+L2H8E08TE0R07yB&USm!Rm=Oo{qg"HH WقPaa OܭwʴEc6ǒMz Tzm4O{khLjfE0H  V ;i"=qPK2@ZqQ+VL3Zv5W_S1dE0 >_ qa>JVdF V}DSLT)1O}kuݯuǹ,!22qn=޵_@lB#AǻZz*.QE0Z`) EH2#.lpmCMZ"S UI/4X u|b{*H :A.upcϢhN]H^28ՙ:xS{`y DDSL#`) U%\d=jMḴz` FjzŤ5kj}Ai^c(U ABk=Z<1eǠq A`TAѶ헛VV'G`O`Ϸ'U:;CYA6'm#Wv珛m4=w|vHKq̔DŸF1׊bV_NLKG[`W^-ZFF^E~O_oSͣSu|ߌ냩0~=y2H T")d 45ҕ4C~LehfJlP{:*;5Ȓz~E2\|v=GI̲B 1ՋUs{=Nyh<Մ0SLE0`HλܶHm?M%3~/qE=/CgMtm\2\\+n%$X\iTljNwK&QJo .b<)oP;֬dƃOVZ5'"5WSM;)0c'ea>}R<&E0>#eNLE0.`)\5]Ytv~~~z믕L|ƿXxZd")EHaZa0Sk*c[p۶ F@"s9cqFnw.UA'UK|69;_ՙl&?Q:,*`ڷ!.E1G** Ԯ)ÆZ-.nkY>A$JN yt=~+?x4ǦpLE0aR0`ۤDs̻# 'KA<|ejfzz?vۉ"K'l4j8>awk#c%ڡ ^ӂu}\M%晎fABY5}>ep00 ``)+yPW/o7f:yC]@9pZ,Fgb.]} D<&1 | &}lW@) 򦂬_/j2L ?VHxDHǼ8תUϳ5c>#F"R(8WۅLn8q%J*VL=%0_[g"p9&v^ӥ:vszY,QĊ|j M{yڬW(r[G u1T+p;`nbNtkK3Bj+B cT@\Ӛe~rj4>> T؟V-ܒ9R ԙcߧe̿W oM dLʸꯙWkx#4{14%A$q)sFD֛OW+ģdsC/us㎺vj9j 03&#f#iuhk9<ٿ|cOb Er8\r-&ݻJ]2/H T3~(̛-ƊB@PBؑ~'uqZ*Uhi F ur)4vu!=N4ۘsLO +ǰCn.bOE*He<w&+P}L060v c`*rϼqju;WS뭻fڭ`Rm7WzNc)a !oTH@h ^#GjAxiqp;б9hZggGVEX5󴎡hcKmP/z.:2WXV^tutI;Z3Vp8Jzߣ}BoBVo܅%5xr)~.~ZrtRq[ȅyۙ$f  4 qۍ\kkv"@H;Gk -ڼ!FCh$.c6N$)A k";h+ `bVZ駶~%%N)? 6ܖk FA,1 UIgٞX{cΊ7vۭKzkWS Th-RVVY9ഥ<#CJᙈ819㿯ȥŜuF]%*MG4~s_ щvm5\̓1JiPoŝݔ2f TԖrk~LiE;m䊠 S#%}\WO`j|K㶝_Qy, IxbL>ah^WKXxEu:4YeHc٭/jMg!wwtSbcEyh^4[ṉ.G5:kbcm% YVՐW_oUy=7bmbMo[;9!1I! =2 cgklA=ԁ䶜<1">! ׷2cmu-Lo &V864[XvH&hpG ӏ U2+ۻY&15?fK#݌гvp%͔v5/-0CA卞sAufV^WȌ[RTk fEr, Oɓ&Gh E臱&8:Fkc$DiqK *Fu.9;ľBnM\]ܡ7 oɦZUiHm|JxZ3'>bBַOf0H Y>NUp[ֶwkݷ ki2K:U|ACHA(/>*0lbM7' $Ǥ u+Ox&T#\mwA;iE_AƂ)~mmC5E2EnI G"Mf8ʽ.0jґ []ᣂ[ɖ&=v9.}_=μ sZŗK`j +o-SRUeZm&"FOO-}dh>Bqg뤍1GMAfVp -oCBG*t'FȔpFyv7n(,<<&. ˋs8izgJ=j_Iie[jFՀӔvg2;R~<`-p٣'׶s1ZmH;x]bEM$D2J_j)Ruѷm4eBaQ _#ꠑFj+=P-ӷ0E!M- SnW7lEf}qЗl1 ݷҜ>ZWYWoև̚gZ JwoV`Aއڥ97Z)#?̃Qb 8+kfy-yE8:Uc/Y,vkehۢ{%{y6Gc ~ieޟum%MDֈ+}YGq]UUӕu(6˾\֑hW = &躢S B3%Ffu>ceI,w2&y\`RAsQ9OVXм[bHݕ",V9W~^}KUkolIykVYEk_V6"kbg5ђ'3PrV?dz#nuXx[FA[D^CjU"8tRO`ՙpϷ΂KkdgzRu@B++X#.`Va6Hja-Qv[,|QD^Ql( jS(L9 "T qjA!UPrFL\k91>GG8dlHJy:0HG KC[{u%Q-V3@&WaA" #`>6YZ^od*C Lkԅ7HRM)/[@8*=/b`\6}6emv{Yahh0f]!+5 ?c? zvw MJe2W0NenK ~lL7)V0]ƴ̍ dFxү /.>οiZ4jOcH؁bYw.uJ jN=c2ȗw]U(?ÓDms)5x9Cji}kpJ}k=h%9kx!m]7qkFr 7$HjR41jLm\vvI%-Z>Y Գ$q*cF8\ʓ+RN;b<-6Ĺw0E5l.OX]mc/ԫ&+AMt,)W W&u5ˬ5Z#bQaډhfܣwOn]mmA32X:^BmVo? !x;ru?>2}pښ9zK{9`yhZʿ2 kuZK+웍|Ӷrr]47senϓ ⳰3rH ACFV͎]]J"r =>!\ͽ@jT"O; h jLj*5IRr2]OҺweg0ZScUܥ'?-}DlRky6dDΪڳZd붲oطI.+-i-Ă+Ƅ0KBw?[],Y)1} K*3_)˿e(}$\ӲZ9ekpFնQN.w+rM1w~¾4cڥ;&mb=M Riw9\i\ RzW3W:t٧n4{)mX--Kq ;Q}N:)`;Q #S8eqK,nd3kz2en~5Iivp]}2.T?Tc*p뵸 DZY[N/绊in|g.4RT =6Њ,vv M-5h%n fY{q:ܒk%Nq0C,rKp%|M@X޼F*N6?xrcpFB2ɳ18P8̗09I"8^Bk6ӻC[Qᕾ݅4#=͋Oc+(rKZ:jAnq4ҽyو e\ zVK[6D#TlEhe|e޾3io߸6ṍ.rJN^=ceM7f7͛`lR.o7D-UpRq!=T7vRFN:)Ӣjuh/n"-<+jP ~0MM 8U)uF8 ZF0]^/x~mIyzB+Iĺڣ46oOOy €Tʒ98&KvdTKW "]A+ طTIw8nv{/H#Cx^F"eM@ dKBIo@%xOIvKiiV 9ZidJ$^Y[]Kkcƭ#-j L"Q'<(rzg}w2.OtFoNpan 1⹺Glxc::,d9j}~|a/ a~Rf]`MFȪgsT3Y%Sܣi֠,B@If 7FJT mMx!H'xLф$fJ31[%r~ [Z}Quohm#"fDZĔ+1[sbZ]rDJdhf*@ԫf;*ClNɼ=tv 1-]#ᜳ:Z} P;z莡ZOZl-R@BUVecӫUz9m˺m{o&mhZʏCo.&f12RG\Sء(\f?ɕRAÁWJ&%Y~;(YlbոI"չu0E1})"֊ R[(ĥ|bXЊT֤p+SŗATkK]Ԛ5s$F4Ϫ[ik;j#"#8x*-Zpn.)9@ɷùil]cx|-|$Ph+ao&cwMYJegc eR{U01]=v20o?K]ºi^0k0ѱ ;)ߋvq ,Z]LVX2 8# gPv{7\q{c1rh>5|kH~-zRgWp5rv`8ͽ߷[M$Y^[i2hOj3E#Ư̺jG1UTM4״cA2K^1g0eGH L& *?5eupʜo:_E[gO0V[VB *Jm a =3uXvDIי-Vvo_d.BV'⎃iqEQیi#^liuOE< ~{6}duRspRѶj-dyKuV".eGCg" 6ٝa=i~M^G])Jo-n O W&DSZSۋIԎ}{LZm2]X$Ǭ Į ʸjY:H&˸4Y9G*SM^c )5lvv{]ԌM4 MAǎ'V/{+uX!锒g] ƴԋn XB ci34|okH|h5$S߄vEB!% $@2q)wnYZ8٘E\u9Rq*W(*ro8M3h59TgL\Yn,=v{QXLIm23)iNn%6(?u,qm 3QQ*yذ'$}Iciw[{HjTjkk6rt[Ֆ *s-X')u n_VٱaBK֬8. 9 s`%B֮X"B1o MM,n#wxL\X N.4F6dO=<]rk|^[\-IǍ3EVb߾{y"MpĒ5crٳZSqj$fMs}]R(|59݈ {ohO-PP֖݅IzK{]0%KIQ}Ⱃo&ke-@TvaFzst*6ik c~\ duNlWw+>.fm f3 ;nJ;1C9U~6 }}a5"_?@Ew #p"݇7(_m:Has3!JF~g7#}w%leFohN^2:Uیa m\Z\7Sh 2]-U29ݭ-KЋ[;+@Vg{}F/MʭIo)iPVj W @jUЌ]|:g~ rT[ CGA:V.kݖ!LvlKZ& APO5D)exh$'4@#o]CIn7Fъ=cJ?Td>bKG--[y}I C"ZRvc~rO9O:O*p D{o.`6 HY]Y RRInF&ZA*"Ģ^[Lª}kmZAIdh W5̤tjh xAj6 "֤Yv%<XcJKaU|lcfW0250;(FhP3:tν*N麋4ܴ1 lK l؅^zek$E vp*R%ii^$qF?ntuTG`c?W~aJ,B8!So]d.-$LN 8Ӛ1k͞ XCAe KnבQց5i0x]A*4 ܰ\ \XZiE)@e^%?*a}N^a2IP>\r85vUV Sƕk]b hKQ wa{ˆ#c\Mk6vmq?ZnKr>}+ t4h@8Y'"P3Bju#Õ0Qo:ĒsSIYv)-Йv$_@=.:2ޘep|1T wc/eķ+-a?d4/ 4io@k⎙v=1&.Uƈ͝c{8? Ws^4iCQk#۟όR'KorzdG\Kl726m$M+FV SAXH9=EwF,X4 L F:r(h<~Sb;z TS '5[嗰H퐑N )#U^~ m:ztSp"s$С 3ҿhl$ ,!{hhVU^38'_bcAǩz襁+Oecaq:TUeѧ)}W" +־lZBb}GiGS.4Ba ILr2Kgq:tTmwicgTԦ?\_ɊhհXThܲG5\'Y/ +:.|8.@Fv04S[f[pĺ~,LdRҮyZgSGKܷ[ Y,&.*3st$x48c(88ܮT뤭Zf0YHy<!o'n#Bb5:|΢N Z̕T)2=X[*ˇr40$ {@?gEG ̊>`o6(*vCnDzPX9 ܗXڜm,n3pA}0,+u39ѠhAj./1]Ieoex4 t,ʠW8aw>mmz6I'i٠EB$",4`Q7ݼzo?ҕ{i_˅Σ8ͥg&*=co Mnc@G#v:Nc 4&Ji_KUz}ur P}lbgT6Y$%B͎b/,k"ءkYI9_ǷO-&#fbc'02jPFBAE({l n?\h,ڛd2(AZv'Q,ෟ\sF,J\) wx;`P3Z`ы+5\+⢼EF8U2V?bVw?-m#Y.ξd(ӭq2qUMuZPˀ f=wW4mdQ(Ea!>q}8 V%vɮI;+Z%3m+Fy$+wJVJ3aqM&ڢІJɞ*,trĭC}4IwC4i@2Ԛ`~>ur9}'k˯+^]rիgncT"mKI5=~+Q'=3IbUWЗҿL;UQ,ExPiOq:M(ֱJ-!#ۖ!$3uM#QVXhcb@yWE$I?#Vw5vL7k7Z`l wYO6!jP;\iTK=ZmcHl7I(%>ըխjt^]Nߥf1) FU38̠,H;M`}⹴07!͞]g5CgqV%xxpم]ùEIYXRe.WΘ˶V]z n^ eC֝?ye&Q#ptŹ{$up|$%PQnIo}kR@@4hZRSۆ+{h[ 0݊k+,w9 P; fV&PZF~Q\6!mQ(@~l13e7ڑ+9pZIC-> fy2?T$ EbB ``u{iX4]jTV.%ܷ-7KU]F!ij&Z@uGH~thnmB_HƖ$fb&UN,~$ƑnpHX9xgWt)7;Ny~-vSFu[/)cLpi=@Wvv͒ۦgY-#Y'$QԦ450u䶱UX\Fe֪e-q?X߈WO-o&PDqҡXZ# nAk mqTT[ ]-jM `]WOW%OxmoI*)#R3'/Ŧ(&J SR+aA{#⌫_F a-YzԑzafoO#]_ C1քfL%O1KA3\o& \ ~m цc.EЗAԂY,u,qV9"x/>DpxèY[&\K\UtԲҀ+)o eqX9o~- 5w ˧oT}(lFC# U@^rP(%RHz0So2۳Ov!6*rmkut!Z)XZqʤt `jEG^!^58K %,BV:f)"[2oʥcJER*;q-P]1׵Cb#Y(:Ö *©6#.i]H-J\!/8YiݶbCCSZf7%nr*((iVk%՜OkbJ4;xFEA=gu΂hzQI՗o,.0%AUэ2ᆶd{R\o,@.4 :ȁ]UoaxO:1Cb|ǀ3k a+sK8E<a퇞G)J+94:( P?STYtBY nimn;}rPi֢c M Y# ڀfhDcSRLMm;pOcM3?F) ZM3X=ؤC"$YxY!s&P5=n-Y@3U8ceYa-Qsf z6ڭphZ?LSl2mӠGHh ūۺHF(2)Ӊyoɧo21;?~8) ]1/'[- ӽ{~0_]| yG~w's0]oW-1*b,d䮾Nj$Lh ԟ5| oG통?ȏ珇8h{d:c|c"E&|S?ㆄۿyǟ;:Wl1/|,r/oӁ~v>N?0??8LIganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/images/tower-bridge.jpg000066400000000000000000000366241231750357400271710ustar00rootroot00000000000000ExifII*Ducky<+http://ns.adobe.com/xap/1.0/ Adobed       , !1AQ"a2qBb#R3$r4ᢲCScs!1AQq2a"Bbr#3RS ?z^N2ɴُΩD_[~DЖ9n$ 9աWR-3iXMk3>5p.YC  \}gjI35PQy?3U&s-5P\?f,4P Gi2 ݅nj@2],0 εd ڬssTj+H瑆o%lR mA;2oXjt*+(fzݥ3Tcûko[t.$ٰ9+SN WebZ ٔQ* ̤z(ȚpEH+6V Ih tad#Zˮ=u0pZ[r=U",*0s+$.trl=ig3}ϕT.7߉)9$l$~MT.Oȅ;do/:b2d $w-PF"BȚMQdLB&Uh]QS"u4ÕĉY]+0ʮaRq*bX;cE`-΍XM[P. f{d6ͺZHӭB\C6Z^<ͩe$ғM'I ~ 6ҪCuKm/U!t$B5,QQ+&? eiu'¯иR6B}W+m@n1f§0 fm{)Ek+H拉m/!̊RmHR6LDɉr6 Tز6=>u7I01L",FNFLp %J+'u}@ `JifRЫo҉1vDc}QF&c9[dbP*n$؞r9XqpmzF"m!.#[zbPf{ؿ Ԯ92;n c;E$uީE̓ټ/B)ÔbtnT]@͈B7;ۭ:fȲnumHNxZЁҊ@i 386QuW)ˇ,dtRVePJ)(*$D-BABf<;ͺ[ZE`X7~4 < ʶ,"<ј<=5ӯRvi 2)$b 1܋gӲuc}9'/1 nh[C ~AUIM`΄0lYL2n:ՋЉp5^@3[z ;7SzWIrGZH|w ar7- 1q#3n6^J/{NMśp%VC:څԵaG &Yok5RedK-kZ߲06p@'T'H; EʗM(ڦ+PKllGTkQ PȔ$ U$U>tXTAW[4j`/ "eC*6rFWدcavn7*xL܇ɏ (IX-3ԴnKxƴc>HFK&dB}ujG6ڔd0a鶃[Q2*u[^(=cHc^9[ZUxHMo/8P$dV2Td%u~uo:YK1ʆHdi&rB[@:R̖$uOe|?o`M"etA J-ԸuqA"Tk=#׷u+W`]zנt `:x,79xzPC4;e{$w9`NǨ4E >tڀ ҲniOM60,.6anlW'q;xu`ҟ%@rN. H'5hĸF<)3Btzwݨ!EA܍ആYKQV!уk{+^AWKZkv9&luVEHh=]p=nE{wXi^O;|j& EfDRJY5 m%Sa@ꤲ - *4`{Ɲ>|)NjC0TLxGIXfnZ+踆?X_Λ\UFkoo`9k'4u>Ks&3uUa{Εn#^O+>=A`kaRR4Y䑷!WM Ux܍DAbĞi#,|!jWsHt,dbʲXu=|/Oq{pJg%qư#8cVYjt9yؙ<#]ʃw,=76"mǶT">òFKj{Z<[/"|kck#C>ĕc(`~ WoqVԞvo4@t5 Cjc/1C/@CsGiی87_ut5&|&ca ͚hDq+\Xm+YGo8:.nWIdr.[,="DSw4f%zVK'sqscEimXs-Zz=dy>kg~3\y$wg%v-Ye9,I׸"3aqxl$,R6zݎK3Q ˧+̈x|/V_yO!|1,>kzWlxݫ֓WyWcGIZ9_[#> H;U-$SVY\{o lIF,!BdVЅnA^4+c~!cj6- Zzε{!^P։0Z$I*3!TYs eoS9b3:2ciǑ*=QHȠƝE`5Z`mI mW"9%źޮJh?Ŕˉ" eck0⎞(iGXG..J vRO']I%;nA+Xլ xnS0ꭷzJ&d˖Ҝ#mRZdށc\W#\QA-2t);_)X晒0y>mZtol}O^yjz:u#B'&i;X6!]ckjZ5 47ij6>BbIR5&zNCW7:q:qrʊۼVUmmtccFcF>W[2~;~=Ҝv8}c$sۧR,|psnkt.n]7cBOuр(I6 h Jn1*Ln%"`ɝIs.k_,۫xkZF`n7.¥0dgr;?;yҴmzߥYXefہ߅U7jac>}(`l z"垳f{KĔerPoLIlJ"},'0Kܺ*x7SO~e.&CB55gt{FTD]4ǝjuq|z[\hɏr_~~T2$z .RjIi W9< BJENWcikL.c{'fSf8̹= ]nkgִي庛>*#BFϑU'⵬(#_&@ZGNkbYM|' RXb}dz{|}M|k%a-=}ow_ L72zT)zцcUȬvU`2aqHÐ7q"@t+yu_1_sܛ \ yp>+ҽD[p/ mn >AvoOA{|[5~ed9lV~Ds[Gp\?ſ}t)𤻑-n-ژɞtRj- 귇ǮŸJrirNvBMeO,xѐ< kYtsc]hrC ,lG7ySo^ۋv*r89Xs:ƸyQ..H[/z*y͋f apP:yWJ&8f}p GSù=A--0boBlX\w \+(?]sW/mJA5c7^'AH;KI$I,S|O{8#CcEZFkdP"GfAj[yza~3(HFH@l,m}Fč?c' ~W6)5c*@s!.Fq~Sk@_yT( 9^"6:Gst[@iOš&\53+YlddIdoZ|l@ /Oҫxk|@ى7csNhf8$I+ᚫZU"qU!$pZ}&qء8>`#&$e[1=-gr$㱛"Iv_*n6ubUY҆ ٓL,t5J 쭣eSi$4i$S5]'VvSkTtlȽ*䮔Vrƫ%Dxdju_A1|u2Q$5[`qO-ȼK}6=G]?:v_衺UOX%X _0㷷di~M?{s0!B$+i6/SR+t Z32I!TA(\Zv]:hh4=M*ȧIE {N˭O•Sa_M<8Z樴kzɕJ!<R/HBm T|y^;vለL)e? z65{OK؁y+fɒF'lbʭn >hOiE3Fxʷ^H $W, 62w%ъvszZnВ-⪃&>6W iqzKsXOkgn |ucɍq@ yz*aVv|euc`Xۗk#bOuthEh]C6SKi‰zb`Rs Ro{ipˏuiW >o=sŜ.[c;>NUuĺ =$rWXb+#:qԮ5LTJ]EpV|$8D)j5VX=v?1^nMU۰/%vy`!mi_: ;ێZ5,ȅXBsr4 ioƇ{xb5{5gH 2V1t<d!Wq?**Uԍ:Jӣev&[jBs^7OmMoNuEa-Hˏ%4}lB iRm9 kgj_UڼQ"Lq|lS*醧GVF~F8uǍʩ(H _ʕ{4#rdརvS[RMkwU]]}{gidvqV*kzƾ}yMV1v$e?\ ,Ʊ {0$w 屻d+F&j{VaLr6ȏڎF#[=VNbUmGKmY{?C'$E͞ ^#/)^ܱ^CkT~SodLMꄜvtRH$jobҰejӴ݋%m ~E+q}6ҖƨlgbMov1wl}|`y_|-zV]G!vAx8F+lY#Aa}Ŕ,Kt*Be Wt݇{MTj=rN$^+}Qi!3#wz`b_R!. u$2̅HЃvU[<#%$m-{Wa#UD[$wk59v_H=.iz[)(5d {՚'=7j<۷ @Fm%]ۯu֑W>#-N\"K,&$Zncp;K&#dv.uw֬7Êr-svufMC'‡,Up-MEyL5z|)WgHK.G؉l*=εV\E#,{QXrmVO _/KEԥj26n[N*߱uli(/XZV 0Ƶ'Y_`9#8#ii'_HJgEB\fin~b_jtM(wx3Jq\qN?y']ѦzpJYg2εwUIU\vt8@g #KyPm:6{Ji,ϙp۸ R[G^(K~1lj/X>:k\侵Ʒv1`NC w̻g]J 噞<+sE^RHrFr3 :FژkF d "6>1[5۠*XM/#ЬTޚ>x{y&Aŗ HNI):|"RiJYi,nSfUFW]\2&]QNˍ>cmc Rqf>5E,ϓj{7ڜw+|d$$(Xnd_SΗy]UlNgC>S*,hdJ XO OY_==>S s8?l“G${:)$^VBpK_xϸN o1n׿Mh\ jAW%۬Em**B)brro"Q}MKoi*:-u(9{ E!a}b|MKjź[*r,xGE:uWY.$ՎO>^C|G,jmF +&YxmU 1/ a K{˙ +e멡`W/'7)+&fcdc v^=ڽWĖۺCNO17YYµ/PMzv<3Y3q仒7mҟv#f=XCG܊~SqNMP1DZ㤱7.M]1Y &[>#eٸQ$EJKY6Sҵas]%eY33"y'~]/7Uֈ^,d}9LI=@OM"VD::H+U +*KY Ɩ9؉VĩsskEjd측$99%"(d"۽EGZϓk v#|kޜc41aLmiu29K"y%rSc",a/Ծ ^ Ϫϊ%4]W٘\_sSE.;d,ԮŻYMaĒ>?_HH{pȥ@[+_W^-E?F{+>8"8]i\we[U3b'~;&:k7U"{]jIU,Xɏ!2P]:뤵/ǎlsrī :Ӷzh*'fLY25uYr搘]iƻw&{m/uφ79!NKn/uk_1AL8S;;e"O$لɷ[Էv^*_/n)oJ֣[>߈u(8M{dJ15a)>ODk֧a~/B|a }At|~}<)Okcf(\5ċ!DpV&R}\$M||kw߷]b;#?wI*\_k$3\K|1Lx&baa{M0tKsD‚XƋ%;t28ذ(ܴ+ͪ2s:,XmU+HQº5ۗc!#v m#3E"3m]l]ڶ)(ebjJ>4+ qOf;F*Ův]:Q>2~ߊV*@Hq:xsLkPo@@P5? "k#]Pl n5ʱd-N^fH >9|Z m$bۭkMkif==*ʧ-w)[Oح{qxx_ƙrǤE2C)$7emw);o݉7o1LιxJ0u &j4eJ'dmUchh4# QE j)?أIϕŰPᤛ2 {~UKg5`֣/ ܱIl$xd p:X5]9V5/Tڵb[#V>%.֣,|OԶO}_m7~zgZx})/$YZʶA$q@=We {B.'ee`Ghv}V0ʛjS-6S)Y$)_) XJX~#Bp|&:McU/m񶔛v;92P- , mgEdJ"|nGݤ0 q71ҳgƫ-v^O(/8"HC7'OP ڟ_SmOH>ubd{MEVOME\Jwq7ȡmQҩ,U]dmuf/Km&w?(&jRUz֡2ƈB@ km0fɓ2 5(<~u3cQ7m .8OA^-8&&7q'hʲI ۼ>\-!>YriU"3NS(5$II7,na P6Dj&-\Vj#qe'K܏ƺ>R_ +!,?-X/#5h`sE.f_I{t]de2.LJC Xahdy558~C]%mņIګRr^z1- 3Õfuw^|brbaXmµ1u:0J6~U"*wk5JxiU tʍd-Wܓ([sf_ 6.37GNLU5]KIwNN< e'bRTK:]# >]*H0E[$24p$xZźƣ)\"KpUZ߁:2$g E!*|~ޯ맷Yr&&՛]f}Ek>|Mő2 0ڂ#dE/Mr/2maդI@(Cڽ)|N%ݯQō"o)lN"ΆpاRgNHmԏ#5ϋV.FeFYWjOҟ)XKM$c{[FtG1oR5mAPܙ츱ܣcEj4CbS-sAA[NJ4ɎI#sad[ڂGփRECC(]HSe0kTPLj4#_(O7sa4qLiH[ F% e?Un>k2M~E.'*4r#j?OdwM/uUL'#Vk] vi#;@bNtֳfӊ@y222 jQuery UI Tooltip Demos ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/tracking.html000066400000000000000000000030621231750357400253060ustar00rootroot00000000000000 jQuery UI Tooltip - Track the mouse

Tooltips can be attached to any element. When you hover the element with your mouse, the title attribute is displayed in a little box next to the element, just like a native tooltip.

But as it's not a native tooltip, it can be styled. Any themes built with ThemeRoller will also style tooltips accordingly.

Tooltips are also useful for form elements, to show some additional information in the context of each field.

Hover the field to see the tooltip.

Here the tooltips are positioned relative to the mouse, and follow the mouse while it moves above the element.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/tooltip/video-player.html000066400000000000000000000055371231750357400261150ustar00rootroot00000000000000 jQuery UI Tooltip - Video Player demo
Here Be Video (HTML5?)

A fake video player with like/share/stats button, each with a custom-styled tooltip.

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/widget/000077500000000000000000000000001231750357400224065ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/widget/default.html000066400000000000000000000112441231750357400247220ustar00rootroot00000000000000 jQuery UI Widget - Default functionality
color me
color me
color me

This demo shows a simple custom widget built using the widget factory (jquery.ui.widget.js).

The three boxes are initialized in different ways. Clicking them changes their background color. View source to see how it works, its heavily commented

ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/demos/widget/index.html000066400000000000000000000003151231750357400244020ustar00rootroot00000000000000 jQuery UI Widget Demo ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/000077500000000000000000000000001231750357400216365ustar00rootroot00000000000000ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/globalize.culture.de-DE.js000066400000000000000000000035521231750357400265100ustar00rootroot00000000000000/* * Globalize Culture de-DE * * http://github.com/jquery/globalize * * Copyright Software Freedom Conservancy, Inc. * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * This file was generated by the Globalize Culture Generator * Translation: bugs found in this file need to be fixed in the generator */ (function( window, undefined ) { var Globalize; if ( typeof require !== "undefined" && typeof exports !== "undefined" && typeof module !== "undefined" ) { // Assume CommonJS Globalize = require( "globalize" ); } else { // Global variable Globalize = window.Globalize; } Globalize.addCultureInfo( "de-DE", "default", { name: "de-DE", englishName: "German (Germany)", nativeName: "Deutsch (Deutschland)", language: "de", numberFormat: { ",": ".", ".": ",", NaN: "n. def.", negativeInfinity: "-unendlich", positiveInfinity: "+unendlich", percent: { pattern: ["-n%","n%"], ",": ".", ".": "," }, currency: { pattern: ["-n $","n $"], ",": ".", ".": ",", symbol: "€" } }, calendars: { standard: { "/": ".", firstDay: 1, days: { names: ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"], namesAbbr: ["So","Mo","Di","Mi","Do","Fr","Sa"], namesShort: ["So","Mo","Di","Mi","Do","Fr","Sa"] }, months: { names: ["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember",""], namesAbbr: ["Jan","Feb","Mrz","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez",""] }, AM: null, PM: null, eras: [{"name":"n. Chr.","start":null,"offset":0}], patterns: { d: "dd.MM.yyyy", D: "dddd, d. MMMM yyyy", t: "HH:mm", T: "HH:mm:ss", f: "dddd, d. MMMM yyyy HH:mm", F: "dddd, d. MMMM yyyy HH:mm:ss", M: "dd MMMM", Y: "MMMM yyyy" } } } }); }( this )); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/globalize.culture.ja-JP.js000066400000000000000000000054531231750357400265350ustar00rootroot00000000000000/* * Globalize Culture ja-JP * * http://github.com/jquery/globalize * * Copyright Software Freedom Conservancy, Inc. * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * * This file was generated by the Globalize Culture Generator * Translation: bugs found in this file need to be fixed in the generator */ (function( window, undefined ) { var Globalize; if ( typeof require !== "undefined" && typeof exports !== "undefined" && typeof module !== "undefined" ) { // Assume CommonJS Globalize = require( "globalize" ); } else { // Global variable Globalize = window.Globalize; } Globalize.addCultureInfo( "ja-JP", "default", { name: "ja-JP", englishName: "Japanese (Japan)", nativeName: "日本語 (日本)", language: "ja", numberFormat: { NaN: "NaN (非数値)", negativeInfinity: "-∞", positiveInfinity: "+∞", percent: { pattern: ["-n%","n%"] }, currency: { pattern: ["-$n","$n"], decimals: 0, symbol: "¥" } }, calendars: { standard: { days: { names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], namesAbbr: ["日","月","火","水","木","金","土"], namesShort: ["日","月","火","水","木","金","土"] }, months: { names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] }, AM: ["午前","午前","午前"], PM: ["午後","午後","午後"], eras: [{"name":"西暦","start":null,"offset":0}], patterns: { d: "yyyy/MM/dd", D: "yyyy'年'M'月'd'日'", t: "H:mm", T: "H:mm:ss", f: "yyyy'年'M'月'd'日' H:mm", F: "yyyy'年'M'月'd'日' H:mm:ss", M: "M'月'd'日'", Y: "yyyy'年'M'月'" } }, Japanese: { name: "Japanese", days: { names: ["日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日"], namesAbbr: ["日","月","火","水","木","金","土"], namesShort: ["日","月","火","水","木","金","土"] }, months: { names: ["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月",""], namesAbbr: ["1","2","3","4","5","6","7","8","9","10","11","12",""] }, AM: ["午前","午前","午前"], PM: ["午後","午後","午後"], eras: [{"name":"平成","start":null,"offset":1867},{"name":"昭和","start":-1812153600000,"offset":1911},{"name":"大正","start":-1357603200000,"offset":1925},{"name":"明治","start":60022080000,"offset":1988}], twoDigitYearMax: 99, patterns: { d: "gg y/M/d", D: "gg y'年'M'月'd'日'", t: "H:mm", T: "H:mm:ss", f: "gg y'年'M'月'd'日' H:mm", F: "gg y'年'M'月'd'日' H:mm:ss", M: "M'月'd'日'", Y: "gg y'年'M'月'" } } } }); }( this )); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/globalize.js000066400000000000000000001303001231750357400241410ustar00rootroot00000000000000/*! * Globalize * * http://github.com/jquery/globalize * * Copyright Software Freedom Conservancy, Inc. * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license */ (function( window, undefined ) { var Globalize, // private variables regexHex, regexInfinity, regexParseFloat, regexTrim, // private JavaScript utility functions arrayIndexOf, endsWith, extend, isArray, isFunction, isObject, startsWith, trim, truncate, zeroPad, // private Globalization utility functions appendPreOrPostMatch, expandFormat, formatDate, formatNumber, getTokenRegExp, getEra, getEraYear, parseExact, parseNegativePattern; // Global variable (Globalize) or CommonJS module (globalize) Globalize = function( cultureSelector ) { return new Globalize.prototype.init( cultureSelector ); }; if ( typeof require !== "undefined" && typeof exports !== "undefined" && typeof module !== "undefined" ) { // Assume CommonJS module.exports = Globalize; } else { // Export as global variable window.Globalize = Globalize; } Globalize.cultures = {}; Globalize.prototype = { constructor: Globalize, init: function( cultureSelector ) { this.cultures = Globalize.cultures; this.cultureSelector = cultureSelector; return this; } }; Globalize.prototype.init.prototype = Globalize.prototype; // 1. When defining a culture, all fields are required except the ones stated as optional. // 2. Each culture should have a ".calendars" object with at least one calendar named "standard" // which serves as the default calendar in use by that culture. // 3. Each culture should have a ".calendar" object which is the current calendar being used, // it may be dynamically changed at any time to one of the calendars in ".calendars". Globalize.cultures[ "default" ] = { // A unique name for the culture in the form - name: "en", // the name of the culture in the english language englishName: "English", // the name of the culture in its own language nativeName: "English", // whether the culture uses right-to-left text isRTL: false, // "language" is used for so-called "specific" cultures. // For example, the culture "es-CL" means "Spanish, in Chili". // It represents the Spanish-speaking culture as it is in Chili, // which might have different formatting rules or even translations // than Spanish in Spain. A "neutral" culture is one that is not // specific to a region. For example, the culture "es" is the generic // Spanish culture, which may be a more generalized version of the language // that may or may not be what a specific culture expects. // For a specific culture like "es-CL", the "language" field refers to the // neutral, generic culture information for the language it is using. // This is not always a simple matter of the string before the dash. // For example, the "zh-Hans" culture is netural (Simplified Chinese). // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage // field is "zh-CHS", not "zh". // This field should be used to navigate from a specific culture to it's // more general, neutral culture. If a culture is already as general as it // can get, the language may refer to itself. language: "en", // numberFormat defines general number formatting rules, like the digits in // each grouping, the group separator, and how negative numbers are displayed. numberFormat: { // [negativePattern] // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency, // but is still defined as an array for consistency with them. // negativePattern: one of "(n)|-n|- n|n-|n -" pattern: [ "-n" ], // number of decimal places normally shown decimals: 2, // string that separates number groups, as in 1,000,000 ",": ",", // string that separates a number from the fractional portion, as in 1.99 ".": ".", // array of numbers indicating the size of each number group. // TODO: more detailed description and example groupSizes: [ 3 ], // symbol used for positive numbers "+": "+", // symbol used for negative numbers "-": "-", // symbol used for NaN (Not-A-Number) NaN: "NaN", // symbol used for Negative Infinity negativeInfinity: "-Infinity", // symbol used for Positive Infinity positiveInfinity: "Infinity", percent: { // [negativePattern, positivePattern] // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %" // positivePattern: one of "n %|n%|%n|% n" pattern: [ "-n %", "n %" ], // number of decimal places normally shown decimals: 2, // array of numbers indicating the size of each number group. // TODO: more detailed description and example groupSizes: [ 3 ], // string that separates number groups, as in 1,000,000 ",": ",", // string that separates a number from the fractional portion, as in 1.99 ".": ".", // symbol used to represent a percentage symbol: "%" }, currency: { // [negativePattern, positivePattern] // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)" // positivePattern: one of "$n|n$|$ n|n $" pattern: [ "($n)", "$n" ], // number of decimal places normally shown decimals: 2, // array of numbers indicating the size of each number group. // TODO: more detailed description and example groupSizes: [ 3 ], // string that separates number groups, as in 1,000,000 ",": ",", // string that separates a number from the fractional portion, as in 1.99 ".": ".", // symbol used to represent currency symbol: "$" } }, // calendars defines all the possible calendars used by this culture. // There should be at least one defined with name "standard", and is the default // calendar used by the culture. // A calendar contains information about how dates are formatted, information about // the calendar's eras, a standard set of the date formats, // translations for day and month names, and if the calendar is not based on the Gregorian // calendar, conversion functions to and from the Gregorian calendar. calendars: { standard: { // name that identifies the type of calendar this is name: "Gregorian_USEnglish", // separator of parts of a date (e.g. "/" in 11/05/1955) "/": "/", // separator of parts of a time (e.g. ":" in 05:44 PM) ":": ":", // the first day of the week (0 = Sunday, 1 = Monday, etc) firstDay: 0, days: { // full day names names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // abbreviated day names namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // shortest day names namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ] }, months: { // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar) names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ], // abbreviated month names namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ] }, // AM and PM designators in one of these forms: // The usual view, and the upper and lower case versions // [ standard, lowercase, uppercase ] // The culture does not use AM or PM (likely all standard date formats use 24 hour time) // null AM: [ "AM", "am", "AM" ], PM: [ "PM", "pm", "PM" ], eras: [ // eras in reverse chronological order. // name: the name of the era in this culture (e.g. A.D., C.E.) // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era. // offset: offset in years from gregorian calendar { "name": "A.D.", "start": null, "offset": 0 } ], // when a two digit year is given, it will never be parsed as a four digit // year greater than this year (in the appropriate era for the culture) // Set it as a full year (e.g. 2029) or use an offset format starting from // the current year: "+19" would correspond to 2029 if the current year 2010. twoDigitYearMax: 2029, // set of predefined date and time patterns used by the culture // these represent the format someone in this culture would expect // to see given the portions of the date that are shown. patterns: { // short date pattern d: "M/d/yyyy", // long date pattern D: "dddd, MMMM dd, yyyy", // short time pattern t: "h:mm tt", // long time pattern T: "h:mm:ss tt", // long date, short time pattern f: "dddd, MMMM dd, yyyy h:mm tt", // long date, long time pattern F: "dddd, MMMM dd, yyyy h:mm:ss tt", // month/day pattern M: "MMMM dd", // month/year pattern Y: "yyyy MMMM", // S is a sortable format that does not vary by culture S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss" } // optional fields for each calendar: /* monthsGenitive: Same as months but used when the day preceeds the month. Omit if the culture has no genitive distinction in month names. For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx convert: Allows for the support of non-gregorian based calendars. This convert object is used to to convert a date to and from a gregorian calendar date to handle parsing and formatting. The two functions: fromGregorian( date ) Given the date as a parameter, return an array with parts [ year, month, day ] corresponding to the non-gregorian based year, month, and day for the calendar. toGregorian( year, month, day ) Given the non-gregorian year, month, and day, return a new Date() object set to the corresponding date in the gregorian calendar. */ } }, // For localized strings messages: {} }; Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard; Globalize.cultures[ "en" ] = Globalize.cultures[ "default" ]; Globalize.cultureSelector = "en"; // // private variables // regexHex = /^0x[a-f0-9]+$/i; regexInfinity = /^[+-]?infinity$/i; regexParseFloat = /^[+-]?\d*\.?\d*(e[+-]?\d+)?$/; regexTrim = /^\s+|\s+$/g; // // private JavaScript utility functions // arrayIndexOf = function( array, item ) { if ( array.indexOf ) { return array.indexOf( item ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[i] === item ) { return i; } } return -1; }; endsWith = function( value, pattern ) { return value.substr( value.length - pattern.length ) === pattern; }; extend = function( deep ) { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !isFunction(target) ) { target = {}; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && isArray(src) ? src : []; } else { clone = src && isObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; isArray = Array.isArray || function( obj ) { return Object.prototype.toString.call( obj ) === "[object Array]"; }; isFunction = function( obj ) { return Object.prototype.toString.call( obj ) === "[object Function]" } isObject = function( obj ) { return Object.prototype.toString.call( obj ) === "[object Object]"; }; startsWith = function( value, pattern ) { return value.indexOf( pattern ) === 0; }; trim = function( value ) { return ( value + "" ).replace( regexTrim, "" ); }; truncate = function( value ) { return value | 0; }; zeroPad = function( str, count, left ) { var l; for ( l = str.length; l < count; l += 1 ) { str = ( left ? ("0" + str) : (str + "0") ); } return str; }; // // private Globalization utility functions // appendPreOrPostMatch = function( preMatch, strings ) { // appends pre- and post- token match strings while removing escaped characters. // Returns a single quote count which is used to determine if the token occurs // in a string literal. var quoteCount = 0, escaped = false; for ( var i = 0, il = preMatch.length; i < il; i++ ) { var c = preMatch.charAt( i ); switch ( c ) { case "\'": if ( escaped ) { strings.push( "\'" ); } else { quoteCount++; } escaped = false; break; case "\\": if ( escaped ) { strings.push( "\\" ); } escaped = !escaped; break; default: strings.push( c ); escaped = false; break; } } return quoteCount; }; expandFormat = function( cal, format ) { // expands unspecified or single character date formats into the full pattern. format = format || "F"; var pattern, patterns = cal.patterns, len = format.length; if ( len === 1 ) { pattern = patterns[ format ]; if ( !pattern ) { throw "Invalid date format string \'" + format + "\'."; } format = pattern; } else if ( len === 2 && format.charAt(0) === "%" ) { // %X escape format -- intended as a custom format string that is only one character, not a built-in format. format = format.charAt( 1 ); } return format; }; formatDate = function( value, format, culture ) { var cal = culture.calendar, convert = cal.convert; if ( !format || !format.length || format === "i" ) { var ret; if ( culture && culture.name.length ) { if ( convert ) { // non-gregorian calendar, so we cannot use built-in toLocaleString() ret = formatDate( value, cal.patterns.F, culture ); } else { var eraDate = new Date( value.getTime() ), era = getEra( value, cal.eras ); eraDate.setFullYear( getEraYear(value, cal, era) ); ret = eraDate.toLocaleString(); } } else { ret = value.toString(); } return ret; } var eras = cal.eras, sortable = format === "s"; format = expandFormat( cal, format ); // Start with an empty string ret = []; var hour, zeros = [ "0", "00", "000" ], foundDay, checkedDay, dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g, quoteCount = 0, tokenRegExp = getTokenRegExp(), converted; function padZeros( num, c ) { var r, s = num + ""; if ( c > 1 && s.length < c ) { r = ( zeros[c - 2] + s); return r.substr( r.length - c, c ); } else { r = s; } return r; } function hasDay() { if ( foundDay || checkedDay ) { return foundDay; } foundDay = dayPartRegExp.test( format ); checkedDay = true; return foundDay; } function getPart( date, part ) { if ( converted ) { return converted[ part ]; } switch ( part ) { case 0: return date.getFullYear(); case 1: return date.getMonth(); case 2: return date.getDate(); } } if ( !sortable && convert ) { converted = convert.fromGregorian( value ); } for ( ; ; ) { // Save the current index var index = tokenRegExp.lastIndex, // Look for the next pattern ar = tokenRegExp.exec( format ); // Append the text before the pattern (or the end of the string if not found) var preMatch = format.slice( index, ar ? ar.index : format.length ); quoteCount += appendPreOrPostMatch( preMatch, ret ); if ( !ar ) { break; } // do not replace any matches that occur inside a string literal. if ( quoteCount % 2 ) { ret.push( ar[0] ); continue; } var current = ar[ 0 ], clength = current.length; switch ( current ) { case "ddd": //Day of the week, as a three-letter abbreviation case "dddd": // Day of the week, using the full name var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names; ret.push( names[value.getDay()] ); break; case "d": // Day of month, without leading zero for single-digit days case "dd": // Day of month, with leading zero for single-digit days foundDay = true; ret.push( padZeros( getPart(value, 2), clength ) ); break; case "MMM": // Month, as a three-letter abbreviation case "MMMM": // Month, using the full name var part = getPart( value, 1 ); ret.push( ( cal.monthsGenitive && hasDay() ) ? cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] : cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] ); break; case "M": // Month, as digits, with no leading zero for single-digit months case "MM": // Month, as digits, with leading zero for single-digit months ret.push( padZeros( getPart(value, 1) + 1, clength ) ); break; case "y": // Year, as two digits, but with no leading zero for years less than 10 case "yy": // Year, as two digits, with leading zero for years less than 10 case "yyyy": // Year represented by four full digits part = converted ? converted[ 0 ] : getEraYear( value, cal, getEra(value, eras), sortable ); if ( clength < 4 ) { part = part % 100; } ret.push( padZeros( part, clength ) ); break; case "h": // Hours with no leading zero for single-digit hours, using 12-hour clock case "hh": // Hours with leading zero for single-digit hours, using 12-hour clock hour = value.getHours() % 12; if ( hour === 0 ) hour = 12; ret.push( padZeros( hour, clength ) ); break; case "H": // Hours with no leading zero for single-digit hours, using 24-hour clock case "HH": // Hours with leading zero for single-digit hours, using 24-hour clock ret.push( padZeros( value.getHours(), clength ) ); break; case "m": // Minutes with no leading zero for single-digit minutes case "mm": // Minutes with leading zero for single-digit minutes ret.push( padZeros( value.getMinutes(), clength ) ); break; case "s": // Seconds with no leading zero for single-digit seconds case "ss": // Seconds with leading zero for single-digit seconds ret.push( padZeros( value.getSeconds(), clength ) ); break; case "t": // One character am/pm indicator ("a" or "p") case "tt": // Multicharacter am/pm indicator part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " ); ret.push( clength === 1 ? part.charAt(0) : part ); break; case "f": // Deciseconds case "ff": // Centiseconds case "fff": // Milliseconds ret.push( padZeros( value.getMilliseconds(), 3 ).substr( 0, clength ) ); break; case "z": // Time zone offset, no leading zero case "zz": // Time zone offset with leading zero hour = value.getTimezoneOffset() / 60; ret.push( ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), clength ) ); break; case "zzz": // Time zone offset with leading zero hour = value.getTimezoneOffset() / 60; ret.push( ( hour <= 0 ? "+" : "-" ) + padZeros( Math.floor(Math.abs(hour)), 2 ) // Hard coded ":" separator, rather than using cal.TimeSeparator // Repeated here for consistency, plus ":" was already assumed in date parsing. + ":" + padZeros( Math.abs(value.getTimezoneOffset() % 60), 2 ) ); break; case "g": case "gg": if ( cal.eras ) { ret.push( cal.eras[ getEra(value, eras) ].name ); } break; case "/": ret.push( cal["/"] ); break; default: throw "Invalid date format pattern \'" + current + "\'."; break; } } return ret.join( "" ); }; // formatNumber (function() { var expandNumber; expandNumber = function( number, precision, formatInfo ) { var groupSizes = formatInfo.groupSizes, curSize = groupSizes[ 0 ], curGroupIndex = 1, factor = Math.pow( 10, precision ), rounded = Math.round( number * factor ) / factor; if ( !isFinite(rounded) ) { rounded = number; } number = rounded; var numberString = number+"", right = "", split = numberString.split( /e/i ), exponent = split.length > 1 ? parseInt( split[1], 10 ) : 0; numberString = split[ 0 ]; split = numberString.split( "." ); numberString = split[ 0 ]; right = split.length > 1 ? split[ 1 ] : ""; var l; if ( exponent > 0 ) { right = zeroPad( right, exponent, false ); numberString += right.slice( 0, exponent ); right = right.substr( exponent ); } else if ( exponent < 0 ) { exponent = -exponent; numberString = zeroPad( numberString, exponent + 1 ); right = numberString.slice( -exponent, numberString.length ) + right; numberString = numberString.slice( 0, -exponent ); } if ( precision > 0 ) { right = formatInfo[ "." ] + ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) ); } else { right = ""; } var stringIndex = numberString.length - 1, sep = formatInfo[ "," ], ret = ""; while ( stringIndex >= 0 ) { if ( curSize === 0 || curSize > stringIndex ) { return numberString.slice( 0, stringIndex + 1 ) + ( ret.length ? (sep + ret + right) : right ); } ret = numberString.slice( stringIndex - curSize + 1, stringIndex + 1 ) + ( ret.length ? (sep + ret) : "" ); stringIndex -= curSize; if ( curGroupIndex < groupSizes.length ) { curSize = groupSizes[ curGroupIndex ]; curGroupIndex++; } } return numberString.slice( 0, stringIndex + 1 ) + sep + ret + right; }; formatNumber = function( value, format, culture ) { if ( !isFinite(value) ) { if ( value === Infinity ) { return culture.numberFormat.positiveInfinity; } if ( value === -Infinity ) { return culture.numberFormat.negativeInfinity; } return culture.numberFormat.NaN; } if ( !format || format === "i" ) { return culture.name.length ? value.toLocaleString() : value.toString(); } format = format || "D"; var nf = culture.numberFormat, number = Math.abs( value ), precision = -1, pattern; if ( format.length > 1 ) precision = parseInt( format.slice(1), 10 ); var current = format.charAt( 0 ).toUpperCase(), formatInfo; switch ( current ) { case "D": pattern = "n"; number = truncate( number ); if ( precision !== -1 ) { number = zeroPad( "" + number, precision, true ); } if ( value < 0 ) number = "-" + number; break; case "N": formatInfo = nf; // fall through case "C": formatInfo = formatInfo || nf.currency; // fall through case "P": formatInfo = formatInfo || nf.percent; pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" ); if ( precision === -1 ) precision = formatInfo.decimals; number = expandNumber( number * (current === "P" ? 100 : 1), precision, formatInfo ); break; default: throw "Bad number format specifier: " + current; } var patternParts = /n|\$|-|%/g, ret = ""; for ( ; ; ) { var index = patternParts.lastIndex, ar = patternParts.exec( pattern ); ret += pattern.slice( index, ar ? ar.index : pattern.length ); if ( !ar ) { break; } switch ( ar[0] ) { case "n": ret += number; break; case "$": ret += nf.currency.symbol; break; case "-": // don't make 0 negative if ( /[1-9]/.test(number) ) { ret += nf[ "-" ]; } break; case "%": ret += nf.percent.symbol; break; } } return ret; }; }()); getTokenRegExp = function() { // regular expression for matching date and time tokens in format strings. return /\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g; }; getEra = function( date, eras ) { if ( !eras ) return 0; var start, ticks = date.getTime(); for ( var i = 0, l = eras.length; i < l; i++ ) { start = eras[ i ].start; if ( start === null || ticks >= start ) { return i; } } return 0; }; getEraYear = function( date, cal, era, sortable ) { var year = date.getFullYear(); if ( !sortable && cal.eras ) { // convert normal gregorian year to era-shifted gregorian // year by subtracting the era offset year -= cal.eras[ era ].offset; } return year; }; // parseExact (function() { var expandYear, getDayIndex, getMonthIndex, getParseRegExp, outOfRange, toUpper, toUpperArray; expandYear = function( cal, year ) { // expands 2-digit year into 4 digits. var now = new Date(), era = getEra( now ); if ( year < 100 ) { var twoDigitYearMax = cal.twoDigitYearMax; twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt( twoDigitYearMax, 10 ) : twoDigitYearMax; var curr = getEraYear( now, cal, era ); year += curr - ( curr % 100 ); if ( year > twoDigitYearMax ) { year -= 100; } } return year; }; getDayIndex = function ( cal, value, abbr ) { var ret, days = cal.days, upperDays = cal._upperDays; if ( !upperDays ) { cal._upperDays = upperDays = [ toUpperArray( days.names ), toUpperArray( days.namesAbbr ), toUpperArray( days.namesShort ) ]; } value = toUpper( value ); if ( abbr ) { ret = arrayIndexOf( upperDays[1], value ); if ( ret === -1 ) { ret = arrayIndexOf( upperDays[2], value ); } } else { ret = arrayIndexOf( upperDays[0], value ); } return ret; }; getMonthIndex = function( cal, value, abbr ) { var months = cal.months, monthsGen = cal.monthsGenitive || cal.months, upperMonths = cal._upperMonths, upperMonthsGen = cal._upperMonthsGen; if ( !upperMonths ) { cal._upperMonths = upperMonths = [ toUpperArray( months.names ), toUpperArray( months.namesAbbr ) ]; cal._upperMonthsGen = upperMonthsGen = [ toUpperArray( monthsGen.names ), toUpperArray( monthsGen.namesAbbr ) ]; } value = toUpper( value ); var i = arrayIndexOf( abbr ? upperMonths[1] : upperMonths[0], value ); if ( i < 0 ) { i = arrayIndexOf( abbr ? upperMonthsGen[1] : upperMonthsGen[0], value ); } return i; }; getParseRegExp = function( cal, format ) { // converts a format string into a regular expression with groups that // can be used to extract date fields from a date string. // check for a cached parse regex. var re = cal._parseRegExp; if ( !re ) { cal._parseRegExp = re = {}; } else { var reFormat = re[ format ]; if ( reFormat ) { return reFormat; } } // expand single digit formats, then escape regular expression characters. var expFormat = expandFormat( cal, format ).replace( /([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1" ), regexp = [ "^" ], groups = [], index = 0, quoteCount = 0, tokenRegExp = getTokenRegExp(), match; // iterate through each date token found. while ( (match = tokenRegExp.exec(expFormat)) !== null ) { var preMatch = expFormat.slice( index, match.index ); index = tokenRegExp.lastIndex; // don't replace any matches that occur inside a string literal. quoteCount += appendPreOrPostMatch( preMatch, regexp ); if ( quoteCount % 2 ) { regexp.push( match[0] ); continue; } // add a regex group for the token. var m = match[ 0 ], len = m.length, add; switch ( m ) { case "dddd": case "ddd": case "MMMM": case "MMM": case "gg": case "g": add = "(\\D+)"; break; case "tt": case "t": add = "(\\D*)"; break; case "yyyy": case "fff": case "ff": case "f": add = "(\\d{" + len + "})"; break; case "dd": case "d": case "MM": case "M": case "yy": case "y": case "HH": case "H": case "hh": case "h": case "mm": case "m": case "ss": case "s": add = "(\\d\\d?)"; break; case "zzz": add = "([+-]?\\d\\d?:\\d{2})"; break; case "zz": case "z": add = "([+-]?\\d\\d?)"; break; case "/": add = "(\\" + cal[ "/" ] + ")"; break; default: throw "Invalid date format pattern \'" + m + "\'."; break; } if ( add ) { regexp.push( add ); } groups.push( match[0] ); } appendPreOrPostMatch( expFormat.slice(index), regexp ); regexp.push( "$" ); // allow whitespace to differ when matching formats. var regexpStr = regexp.join( "" ).replace( /\s+/g, "\\s+" ), parseRegExp = { "regExp": regexpStr, "groups": groups }; // cache the regex for this format. return re[ format ] = parseRegExp; }; outOfRange = function( value, low, high ) { return value < low || value > high; }; toUpper = function( value ) { // "he-IL" has non-breaking space in weekday names. return value.split( "\u00A0" ).join( " " ).toUpperCase(); }; toUpperArray = function( arr ) { var results = []; for ( var i = 0, l = arr.length; i < l; i++ ) { results[ i ] = toUpper( arr[i] ); } return results; }; parseExact = function( value, format, culture ) { // try to parse the date string by matching against the format string // while using the specified culture for date field names. value = trim( value ); var cal = culture.calendar, // convert date formats into regular expressions with groupings. // use the regexp to determine the input format and extract the date fields. parseInfo = getParseRegExp( cal, format ), match = new RegExp( parseInfo.regExp ).exec( value ); if ( match === null ) { return null; } // found a date format that matches the input. var groups = parseInfo.groups, era = null, year = null, month = null, date = null, weekDay = null, hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null, pmHour = false; // iterate the format groups to extract and set the date fields. for ( var j = 0, jl = groups.length; j < jl; j++ ) { var matchGroup = match[ j + 1 ]; if ( matchGroup ) { var current = groups[ j ], clength = current.length, matchInt = parseInt( matchGroup, 10 ); switch ( current ) { case "dd": case "d": // Day of month. date = matchInt; // check that date is generally in valid range, also checking overflow below. if ( outOfRange(date, 1, 31) ) return null; break; case "MMM": case "MMMM": month = getMonthIndex( cal, matchGroup, clength === 3 ); if ( outOfRange(month, 0, 11) ) return null; break; case "M": case "MM": // Month. month = matchInt - 1; if ( outOfRange(month, 0, 11) ) return null; break; case "y": case "yy": case "yyyy": year = clength < 4 ? expandYear( cal, matchInt ) : matchInt; if ( outOfRange(year, 0, 9999) ) return null; break; case "h": case "hh": // Hours (12-hour clock). hour = matchInt; if ( hour === 12 ) hour = 0; if ( outOfRange(hour, 0, 11) ) return null; break; case "H": case "HH": // Hours (24-hour clock). hour = matchInt; if ( outOfRange(hour, 0, 23) ) return null; break; case "m": case "mm": // Minutes. min = matchInt; if ( outOfRange(min, 0, 59) ) return null; break; case "s": case "ss": // Seconds. sec = matchInt; if ( outOfRange(sec, 0, 59) ) return null; break; case "tt": case "t": // AM/PM designator. // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of // the AM tokens. If not, fail the parse for this format. pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] ); if ( !pmHour && ( !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] ) ) ) return null; break; case "f": // Deciseconds. case "ff": // Centiseconds. case "fff": // Milliseconds. msec = matchInt * Math.pow( 10, 3 - clength ); if ( outOfRange(msec, 0, 999) ) return null; break; case "ddd": // Day of week. case "dddd": // Day of week. weekDay = getDayIndex( cal, matchGroup, clength === 3 ); if ( outOfRange(weekDay, 0, 6) ) return null; break; case "zzz": // Time zone offset in +/- hours:min. var offsets = matchGroup.split( /:/ ); if ( offsets.length !== 2 ) return null; hourOffset = parseInt( offsets[0], 10 ); if ( outOfRange(hourOffset, -12, 13) ) return null; var minOffset = parseInt( offsets[1], 10 ); if ( outOfRange(minOffset, 0, 59) ) return null; tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset ); break; case "z": case "zz": // Time zone offset in +/- hours. hourOffset = matchInt; if ( outOfRange(hourOffset, -12, 13) ) return null; tzMinOffset = hourOffset * 60; break; case "g": case "gg": var eraName = matchGroup; if ( !eraName || !cal.eras ) return null; eraName = trim( eraName.toLowerCase() ); for ( var i = 0, l = cal.eras.length; i < l; i++ ) { if ( eraName === cal.eras[i].name.toLowerCase() ) { era = i; break; } } // could not find an era with that name if ( era === null ) return null; break; } } } var result = new Date(), defaultYear, convert = cal.convert; defaultYear = convert ? convert.fromGregorian( result )[ 0 ] : result.getFullYear(); if ( year === null ) { year = defaultYear; } else if ( cal.eras ) { // year must be shifted to normal gregorian year // but not if year was not specified, its already normal gregorian // per the main if clause above. year += cal.eras[( era || 0 )].offset; } // set default day and month to 1 and January, so if unspecified, these are the defaults // instead of the current day/month. if ( month === null ) { month = 0; } if ( date === null ) { date = 1; } // now have year, month, and date, but in the culture's calendar. // convert to gregorian if necessary if ( convert ) { result = convert.toGregorian( year, month, date ); // conversion failed, must be an invalid match if ( result === null ) return null; } else { // have to set year, month and date together to avoid overflow based on current date. result.setFullYear( year, month, date ); // check to see if date overflowed for specified month (only checked 1-31 above). if ( result.getDate() !== date ) return null; // invalid day of week. if ( weekDay !== null && result.getDay() !== weekDay ) { return null; } } // if pm designator token was found make sure the hours fit the 24-hour clock. if ( pmHour && hour < 12 ) { hour += 12; } result.setHours( hour, min, sec, msec ); if ( tzMinOffset !== null ) { // adjust timezone to utc before applying local offset. var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() ); // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours // to ensure both these fields will not exceed this range. adjustedMin will range // somewhere between -1440 and 1500, so we only need to split this into hours. result.setHours( result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60 ); } return result; }; }()); parseNegativePattern = function( value, nf, negativePattern ) { var neg = nf[ "-" ], pos = nf[ "+" ], ret; switch ( negativePattern ) { case "n -": neg = " " + neg; pos = " " + pos; // fall through case "n-": if ( endsWith(value, neg) ) { ret = [ "-", value.substr(0, value.length - neg.length) ]; } else if ( endsWith(value, pos) ) { ret = [ "+", value.substr(0, value.length - pos.length) ]; } break; case "- n": neg += " "; pos += " "; // fall through case "-n": if ( startsWith(value, neg) ) { ret = [ "-", value.substr(neg.length) ]; } else if ( startsWith(value, pos) ) { ret = [ "+", value.substr(pos.length) ]; } break; case "(n)": if ( startsWith(value, "(") && endsWith(value, ")") ) { ret = [ "-", value.substr(1, value.length - 2) ]; } break; } return ret || [ "", value ]; }; // // public instance functions // Globalize.prototype.findClosestCulture = function( cultureSelector ) { return Globalize.findClosestCulture.call( this, cultureSelector ); }; Globalize.prototype.format = function( value, format, cultureSelector ) { return Globalize.format.call( this, value, format, cultureSelector ); }; Globalize.prototype.localize = function( key, cultureSelector ) { return Globalize.localize.call( this, key, cultureSelector ); }; Globalize.prototype.parseInt = function( value, radix, cultureSelector ) { return Globalize.parseInt.call( this, value, radix, cultureSelector ); }; Globalize.prototype.parseFloat = function( value, radix, cultureSelector ) { return Globalize.parseFloat.call( this, value, radix, cultureSelector ); }; Globalize.prototype.culture = function( cultureSelector ) { return Globalize.culture.call( this, cultureSelector ); }; // // public singleton functions // Globalize.addCultureInfo = function( cultureName, baseCultureName, info ) { var base = {}, isNew = false; if ( typeof cultureName !== "string" ) { // cultureName argument is optional string. If not specified, assume info is first // and only argument. Specified info deep-extends current culture. info = cultureName; cultureName = this.culture().name; base = this.cultures[ cultureName ]; } else if ( typeof baseCultureName !== "string" ) { // baseCultureName argument is optional string. If not specified, assume info is second // argument. Specified info deep-extends specified culture. // If specified culture does not exist, create by deep-extending default info = baseCultureName; isNew = ( this.cultures[ cultureName ] == null ); base = this.cultures[ cultureName ] || this.cultures[ "default" ]; } else { // cultureName and baseCultureName specified. Assume a new culture is being created // by deep-extending an specified base culture isNew = true; base = this.cultures[ baseCultureName ]; } this.cultures[ cultureName ] = extend(true, {}, base, info ); // Make the standard calendar the current culture if it's a new culture if ( isNew ) { this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard; } }; Globalize.findClosestCulture = function( name ) { var match; if ( !name ) { return this.cultures[ this.cultureSelector ] || this.cultures[ "default" ]; } if ( typeof name === "string" ) { name = name.split( "," ); } if ( isArray(name) ) { var lang, cultures = this.cultures, list = name, i, l = list.length, prioritized = []; for ( i = 0; i < l; i++ ) { name = trim( list[i] ); var pri, parts = name.split( ";" ); lang = trim( parts[0] ); if ( parts.length === 1 ) { pri = 1; } else { name = trim( parts[1] ); if ( name.indexOf("q=") === 0 ) { name = name.substr( 2 ); pri = parseFloat( name ); pri = isNaN( pri ) ? 0 : pri; } else { pri = 1; } } prioritized.push({ lang: lang, pri: pri }); } prioritized.sort(function( a, b ) { return a.pri < b.pri ? 1 : -1; }); // exact match for ( i = 0; i < l; i++ ) { lang = prioritized[ i ].lang; match = cultures[ lang ]; if ( match ) { return match; } } // neutral language match for ( i = 0; i < l; i++ ) { lang = prioritized[ i ].lang; do { var index = lang.lastIndexOf( "-" ); if ( index === -1 ) { break; } // strip off the last part. e.g. en-US => en lang = lang.substr( 0, index ); match = cultures[ lang ]; if ( match ) { return match; } } while ( 1 ); } // last resort: match first culture using that language for ( i = 0; i < l; i++ ) { lang = prioritized[ i ].lang; for ( var cultureKey in cultures ) { var culture = cultures[ cultureKey ]; if ( culture.language == lang ) { return culture; } } } } else if ( typeof name === "object" ) { return name; } return match || null; }; Globalize.format = function( value, format, cultureSelector ) { culture = this.findClosestCulture( cultureSelector ); if ( value instanceof Date ) { value = formatDate( value, format, culture ); } else if ( typeof value === "number" ) { value = formatNumber( value, format, culture ); } return value; }; Globalize.localize = function( key, cultureSelector ) { return this.findClosestCulture( cultureSelector ).messages[ key ] || this.cultures[ "default" ].messages[ key ]; }; Globalize.parseDate = function( value, formats, culture ) { culture = this.findClosestCulture( culture ); var date, prop, patterns; if ( formats ) { if ( typeof formats === "string" ) { formats = [ formats ]; } if ( formats.length ) { for ( var i = 0, l = formats.length; i < l; i++ ) { var format = formats[ i ]; if ( format ) { date = parseExact( value, format, culture ); if ( date ) { break; } } } } } else { patterns = culture.calendar.patterns; for ( prop in patterns ) { date = parseExact( value, patterns[prop], culture ); if ( date ) { break; } } } return date || null; }; Globalize.parseInt = function( value, radix, cultureSelector ) { return truncate( Globalize.parseFloat(value, radix, cultureSelector) ); }; Globalize.parseFloat = function( value, radix, cultureSelector ) { // radix argument is optional if ( typeof radix !== "number" ) { cultureSelector = radix; radix = 10; } var culture = this.findClosestCulture( cultureSelector ); var ret = NaN, nf = culture.numberFormat; if ( value.indexOf(culture.numberFormat.currency.symbol) > -1 ) { // remove currency symbol value = value.replace( culture.numberFormat.currency.symbol, "" ); // replace decimal seperator value = value.replace( culture.numberFormat.currency["."], culture.numberFormat["."] ); } // trim leading and trailing whitespace value = trim( value ); // allow infinity or hexidecimal if ( regexInfinity.test(value) ) { ret = parseFloat( value ); } else if ( !radix && regexHex.test(value) ) { ret = parseInt( value, 16 ); } else { // determine sign and number var signInfo = parseNegativePattern( value, nf, nf.pattern[0] ), sign = signInfo[ 0 ], num = signInfo[ 1 ]; // #44 - try parsing as "(n)" if ( sign === "" && nf.pattern[0] !== "(n)" ) { signInfo = parseNegativePattern( value, nf, "(n)" ); sign = signInfo[ 0 ]; num = signInfo[ 1 ]; } // try parsing as "-n" if ( sign === "" && nf.pattern[0] !== "-n" ) { signInfo = parseNegativePattern( value, nf, "-n" ); sign = signInfo[ 0 ]; num = signInfo[ 1 ]; } sign = sign || "+"; // determine exponent and number var exponent, intAndFraction, exponentPos = num.indexOf( "e" ); if ( exponentPos < 0 ) exponentPos = num.indexOf( "E" ); if ( exponentPos < 0 ) { intAndFraction = num; exponent = null; } else { intAndFraction = num.substr( 0, exponentPos ); exponent = num.substr( exponentPos + 1 ); } // determine decimal position var integer, fraction, decSep = nf[ "." ], decimalPos = intAndFraction.indexOf( decSep ); if ( decimalPos < 0 ) { integer = intAndFraction; fraction = null; } else { integer = intAndFraction.substr( 0, decimalPos ); fraction = intAndFraction.substr( decimalPos + decSep.length ); } // handle groups (e.g. 1,000,000) var groupSep = nf[ "," ]; integer = integer.split( groupSep ).join( "" ); var altGroupSep = groupSep.replace( /\u00A0/g, " " ); if ( groupSep !== altGroupSep ) { integer = integer.split( altGroupSep ).join( "" ); } // build a natively parsable number string var p = sign + integer; if ( fraction !== null ) { p += "." + fraction; } if ( exponent !== null ) { // exponent itself may have a number patternd var expSignInfo = parseNegativePattern( exponent, nf, "-n" ); p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ]; } if ( regexParseFloat.test(p) ) { ret = parseFloat( p ); } } return ret; }; Globalize.culture = function( cultureSelector ) { // setter if ( typeof cultureSelector !== "undefined" ) { this.cultureSelector = cultureSelector; } // getter return this.findClosestCulture( cultureSelector ) || this.culture[ "default" ]; }; }( this )); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/jquery.mousewheel.js000066400000000000000000000064711231750357400256770ustar00rootroot00000000000000/*! Copyright (c) 2013 Brandon Aaron (http://brandonaaron.net) * Licensed under the MIT License (LICENSE.txt). * * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. * Thanks to: Seamus Leahy for adding deltaX and deltaY * * Version: 3.1.0 * * Requires: 1.2.2+ */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else { // Browser globals factory(jQuery); } }(function ($) { var toFix = ['wheel', 'mousewheel', 'DOMMouseScroll']; var toBind = 'onwheel' in document || document.documentMode >= 9 ? ['wheel'] : ['mousewheel', 'DomMouseScroll', 'MozMousePixelScroll']; var lowestDelta, lowestDeltaXY; if ($.event.fixHooks) { for ( var i=toFix.length; i; ) { $.event.fixHooks[ toFix[--i] ] = $.event.mouseHooks; } } $.event.special.mousewheel = { setup: function() { if ( this.addEventListener ) { for ( var i=toBind.length; i; ) { this.addEventListener( toBind[--i], handler, false ); } } else { this.onmousewheel = handler; } }, teardown: function() { if ( this.removeEventListener ) { for ( var i=toBind.length; i; ) { this.removeEventListener( toBind[--i], handler, false ); } } else { this.onmousewheel = null; } } }; $.fn.extend({ mousewheel: function(fn) { return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); }, unmousewheel: function(fn) { return this.unbind("mousewheel", fn); } }); function handler(event) { var orgEvent = event || window.event, args = [].slice.call( arguments, 1 ), delta = 0, deltaX = 0, deltaY = 0, absDelta = 0, absDeltaXY = 0; event = $.event.fix(orgEvent); event.type = "mousewheel"; // Old school scrollwheel delta if ( orgEvent.wheelDelta ) { delta = orgEvent.wheelDelta; } if ( orgEvent.detail ) { delta = orgEvent.detail * -1; } // New school wheel delta (wheel event) if ( orgEvent.deltaY ) { deltaY = orgEvent.deltaY * -1; delta = deltaY; } if ( orgEvent.deltaX ) { deltaX = orgEvent.deltaX; delta = deltaX * -1; } // Webkit if ( orgEvent.wheelDeltaY !== undefined ) { deltaY = orgEvent.wheelDeltaY; } if ( orgEvent.wheelDeltaX !== undefined ) { deltaX = orgEvent.wheelDeltaX * -1; } absDelta = Math.abs(delta); if ( !lowestDelta || absDelta < lowestDelta ) { lowestDelta = absDelta; } absDeltaXY = Math.max( Math.abs(deltaY), Math.abs(deltaX) ); if ( !lowestDeltaXY || absDeltaXY < lowestDeltaXY ) { lowestDeltaXY = absDeltaXY; } // Add event and delta to the front of the arguments args.unshift(event, Math.floor(delta/lowestDelta), Math.floor(deltaX/lowestDeltaXY), Math.floor(deltaY/lowestDeltaXY)); return ($.event.dispatch || $.event.handle).apply(this, args); } })); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/qunit.css000066400000000000000000000110741231750357400235130ustar00rootroot00000000000000/** * QUnit v1.11.0 - A JavaScript Unit Testing Framework * * http://qunitjs.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ /** Font Family and Sizes */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; } #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } #qunit-tests { font-size: smaller; } /** Resets */ #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { margin: 0; padding: 0; } /** Header */ #qunit-header { padding: 0.5em 0 0.5em 1em; color: #8699a4; background-color: #0d3349; font-size: 1.5em; line-height: 1em; font-weight: normal; border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0; -webkit-border-top-right-radius: 5px; -webkit-border-top-left-radius: 5px; } #qunit-header a { text-decoration: none; color: #c2ccd1; } #qunit-header a:hover, #qunit-header a:focus { color: #fff; } #qunit-testrunner-toolbar label { display: inline-block; padding: 0 .5em 0 .1em; } #qunit-banner { height: 5px; } #qunit-testrunner-toolbar { padding: 0.5em 0 0.5em 2em; color: #5E740B; background-color: #eee; overflow: hidden; } #qunit-userAgent { padding: 0.5em 0 0.5em 2.5em; background-color: #2b81af; color: #fff; text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; } #qunit-modulefilter-container { float: right; } /** Tests: Pass/Fail */ #qunit-tests { list-style-position: inside; } #qunit-tests li { padding: 0.4em 0.5em 0.4em 2.5em; border-bottom: 1px solid #fff; list-style-position: inside; } #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { display: none; } #qunit-tests li strong { cursor: pointer; } #qunit-tests li a { padding: 0.5em; color: #c2ccd1; text-decoration: none; } #qunit-tests li a:hover, #qunit-tests li a:focus { color: #000; } #qunit-tests li .runtime { float: right; font-size: smaller; } .qunit-assert-list { margin-top: 0.5em; padding: 0.5em; background-color: #fff; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; } .qunit-collapsed { display: none; } #qunit-tests table { border-collapse: collapse; margin-top: .2em; } #qunit-tests th { text-align: right; vertical-align: top; padding: 0 .5em 0 0; } #qunit-tests td { vertical-align: top; } #qunit-tests pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; } #qunit-tests del { background-color: #e0f2be; color: #374e0c; text-decoration: none; } #qunit-tests ins { background-color: #ffcaca; color: #500; text-decoration: none; } /*** Test Counts */ #qunit-tests b.counts { color: black; } #qunit-tests b.passed { color: #5E740B; } #qunit-tests b.failed { color: #710909; } #qunit-tests li li { padding: 5px; background-color: #fff; border-bottom: none; list-style-position: inside; } /*** Passing Styles */ #qunit-tests li li.pass { color: #3c510c; background-color: #fff; border-left: 10px solid #C6E746; } #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } #qunit-tests .pass .test-name { color: #366097; } #qunit-tests .pass .test-actual, #qunit-tests .pass .test-expected { color: #999999; } #qunit-banner.qunit-pass { background-color: #C6E746; } /*** Failing Styles */ #qunit-tests li li.fail { color: #710909; background-color: #fff; border-left: 10px solid #EE5757; white-space: pre; } #qunit-tests > li:last-child { border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-bottom-right-radius: 5px; -webkit-border-bottom-left-radius: 5px; } #qunit-tests .fail { color: #000000; background-color: #EE5757; } #qunit-tests .fail .test-name, #qunit-tests .fail .module-name { color: #000000; } #qunit-tests .fail .test-actual { color: #EE5757; } #qunit-tests .fail .test-expected { color: green; } #qunit-banner.qunit-fail { background-color: #EE5757; } /** Result */ #qunit-testresult { padding: 0.5em 0.5em 0.5em 2.5em; color: #2b81af; background-color: #D2E0E6; border-bottom: 1px solid white; } #qunit-testresult .module-name { font-weight: bold; } /** Fixture */ #qunit-fixture { position: absolute; top: -10000px; left: -10000px; width: 1000px; height: 1000px; } ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/external/qunit.js000066400000000000000000001571141231750357400233450ustar00rootroot00000000000000/** * QUnit v1.11.0 - A JavaScript Unit Testing Framework * * http://qunitjs.com * * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license */ (function( window ) { var QUnit, assert, config, onErrorFnPrev, testId = 0, fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, // Keep a local reference to Date (GH-283) Date = window.Date, defined = { setTimeout: typeof window.setTimeout !== "undefined", sessionStorage: (function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch( e ) { return false; } }()) }, /** * Provides a normalized error string, correcting an issue * with IE 7 (and prior) where Error.prototype.toString is * not properly implemented * * Based on http://es5.github.com/#x15.11.4.4 * * @param {String|Error} error * @return {String} error message */ errorString = function( error ) { var name, message, errorString = error.toString(); if ( errorString.substring( 0, 7 ) === "[object" ) { name = error.name ? error.name.toString() : "Error"; message = error.message ? error.message.toString() : ""; if ( name && message ) { return name + ": " + message; } else if ( name ) { return name; } else if ( message ) { return message; } else { return "Error"; } } else { return errorString; } }, /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ objectValues = function( obj ) { // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. /*jshint newcap: false */ var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { val = obj[key]; vals[key] = val === Object(val) ? objectValues(val) : val; } } return vals; }; function Test( settings ) { extend( this, settings ); this.assertions = []; this.testNumber = ++Test.count; } Test.count = 0; Test.prototype = { init: function() { var a, b, li, tests = id( "qunit-tests" ); if ( tests ) { b = document.createElement( "strong" ); b.innerHTML = this.nameHtml; // `a` initialized at top of scope a = document.createElement( "a" ); a.innerHTML = "Rerun"; a.href = QUnit.url({ testNumber: this.testNumber }); li = document.createElement( "li" ); li.appendChild( b ); li.appendChild( a ); li.className = "running"; li.id = this.id = "qunit-test-output" + testId++; tests.appendChild( li ); } }, setup: function() { if ( this.module !== config.previousModule ) { if ( config.previousModule ) { runLoggingCallbacks( "moduleDone", QUnit, { name: config.previousModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all }); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0 }; runLoggingCallbacks( "moduleStart", QUnit, { name: this.module }); } else if ( config.autorun ) { runLoggingCallbacks( "moduleStart", QUnit, { name: this.module }); } config.current = this; this.testEnvironment = extend({ setup: function() {}, teardown: function() {} }, this.moduleTestEnvironment ); this.started = +new Date(); runLoggingCallbacks( "testStart", QUnit, { name: this.testName, module: this.module }); // allow utility functions to access the current test environment // TODO why?? QUnit.current_testEnvironment = this.testEnvironment; if ( !config.pollution ) { saveGlobal(); } if ( config.notrycatch ) { this.testEnvironment.setup.call( this.testEnvironment ); return; } try { this.testEnvironment.setup.call( this.testEnvironment ); } catch( e ) { QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); } }, run: function() { config.current = this; var running = id( "qunit-testresult" ); if ( running ) { running.innerHTML = "Running:
" + this.nameHtml; } if ( this.async ) { QUnit.stop(); } this.callbackStarted = +new Date(); if ( config.notrycatch ) { this.callback.call( this.testEnvironment, QUnit.assert ); this.callbackRuntime = +new Date() - this.callbackStarted; return; } try { this.callback.call( this.testEnvironment, QUnit.assert ); this.callbackRuntime = +new Date() - this.callbackStarted; } catch( e ) { this.callbackRuntime = +new Date() - this.callbackStarted; QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { QUnit.start(); } } }, teardown: function() { config.current = this; if ( config.notrycatch ) { if ( typeof this.callbackRuntime === "undefined" ) { this.callbackRuntime = +new Date() - this.callbackStarted; } this.testEnvironment.teardown.call( this.testEnvironment ); return; } else { try { this.testEnvironment.teardown.call( this.testEnvironment ); } catch( e ) { QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); } } checkPollution(); }, finish: function() { config.current = this; if ( config.requireExpects && this.expected === null ) { QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); } else if ( this.expected !== null && this.expected !== this.assertions.length ) { QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); } else if ( this.expected === null && !this.assertions.length ) { QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); } var i, assertion, a, b, time, li, ol, test = this, good = 0, bad = 0, tests = id( "qunit-tests" ); this.runtime = +new Date() - this.started; config.stats.all += this.assertions.length; config.moduleStats.all += this.assertions.length; if ( tests ) { ol = document.createElement( "ol" ); ol.className = "qunit-assert-list"; for ( i = 0; i < this.assertions.length; i++ ) { assertion = this.assertions[i]; li = document.createElement( "li" ); li.className = assertion.result ? "pass" : "fail"; li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); ol.appendChild( li ); if ( assertion.result ) { good++; } else { bad++; config.stats.bad++; config.moduleStats.bad++; } } // store result when possible if ( QUnit.config.reorder && defined.sessionStorage ) { if ( bad ) { sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); } else { sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); } } if ( bad === 0 ) { addClass( ol, "qunit-collapsed" ); } // `b` initialized at top of scope b = document.createElement( "strong" ); b.innerHTML = this.nameHtml + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; addEvent(b, "click", function() { var next = b.parentNode.lastChild, collapsed = hasClass( next, "qunit-collapsed" ); ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); }); addEvent(b, "dblclick", function( e ) { var target = e && e.target ? e.target : window.event.srcElement; if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { target = target.parentNode; } if ( window.location && target.nodeName.toLowerCase() === "strong" ) { window.location = QUnit.url({ testNumber: test.testNumber }); } }); // `time` initialized at top of scope time = document.createElement( "span" ); time.className = "runtime"; time.innerHTML = this.runtime + " ms"; // `li` initialized at top of scope li = id( this.id ); li.className = bad ? "fail" : "pass"; li.removeChild( li.firstChild ); a = li.firstChild; li.appendChild( b ); li.appendChild( a ); li.appendChild( time ); li.appendChild( ol ); } else { for ( i = 0; i < this.assertions.length; i++ ) { if ( !this.assertions[i].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } } runLoggingCallbacks( "testDone", QUnit, { name: this.testName, module: this.module, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, duration: this.runtime }); QUnit.reset(); config.current = undefined; }, queue: function() { var bad, test = this; synchronize(function() { test.init(); }); function run() { // each of these can by async synchronize(function() { test.setup(); }); synchronize(function() { test.run(); }); synchronize(function() { test.teardown(); }); synchronize(function() { test.finish(); }); } // `bad` initialized at top of scope // defer when previous test run passed, if storage is available bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); if ( bad ) { run(); } else { synchronize( run, true ); } } }; // Root QUnit object. // `QUnit` initialized at top of scope QUnit = { // call on start of module test to prepend name to all tests module: function( name, testEnvironment ) { config.currentModule = name; config.currentModuleTestEnvironment = testEnvironment; config.modules[name] = true; }, asyncTest: function( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } QUnit.test( testName, expected, callback, true ); }, test: function( testName, expected, callback, async ) { var test, nameHtml = "" + escapeText( testName ) + ""; if ( arguments.length === 2 ) { callback = expected; expected = null; } if ( config.currentModule ) { nameHtml = "" + escapeText( config.currentModule ) + ": " + nameHtml; } test = new Test({ nameHtml: nameHtml, testName: testName, expected: expected, async: async, callback: callback, module: config.currentModule, moduleTestEnvironment: config.currentModuleTestEnvironment, stack: sourceFromStacktrace( 2 ) }); if ( !validTest( test ) ) { return; } test.queue(); }, // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. expect: function( asserts ) { if (arguments.length === 1) { config.current.expected = asserts; } else { return config.current.expected; } }, start: function( count ) { // QUnit hasn't been initialized yet. // Note: RequireJS (et al) may delay onLoad if ( config.semaphore === undefined ) { QUnit.begin(function() { // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first setTimeout(function() { QUnit.start( count ); }); }); return; } config.semaphore -= count || 1; // don't start until equal number of stop-calls if ( config.semaphore > 0 ) { return; } // ignore if start is called more often then stop if ( config.semaphore < 0 ) { config.semaphore = 0; QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); return; } // A slight delay, to avoid any current callbacks if ( defined.setTimeout ) { window.setTimeout(function() { if ( config.semaphore > 0 ) { return; } if ( config.timeout ) { clearTimeout( config.timeout ); } config.blocking = false; process( true ); }, 13); } else { config.blocking = false; process( true ); } }, stop: function( count ) { config.semaphore += count || 1; config.blocking = true; if ( config.testTimeout && defined.setTimeout ) { clearTimeout( config.timeout ); config.timeout = window.setTimeout(function() { QUnit.ok( false, "Test timed out" ); config.semaphore = 1; QUnit.start(); }, config.testTimeout ); } } }; // `assert` initialized at top of scope // Asssert helpers // All of these must either call QUnit.push() or manually do: // - runLoggingCallbacks( "log", .. ); // - config.current.assertions.push({ .. }); // We attach it to the QUnit object *after* we expose the public API, // otherwise `assert` will become a global variable in browsers (#341). assert = { /** * Asserts rough true-ish result. * @name ok * @function * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); */ ok: function( result, msg ) { if ( !config.current ) { throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); } result = !!result; var source, details = { module: config.current.module, name: config.current.testName, result: result, message: msg }; msg = escapeText( msg || (result ? "okay" : "failed" ) ); msg = "" + msg + ""; if ( !result ) { source = sourceFromStacktrace( 2 ); if ( source ) { details.source = source; msg += "
Source:
" + escapeText( source ) + "
"; } } runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: result, message: msg }); }, /** * Assert that the first two arguments are equal, with an optional message. * Prints out both actual and expected values. * @name equal * @function * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); */ equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ QUnit.push( expected == actual, actual, expected, message ); }, /** * @name notEqual * @function */ notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ QUnit.push( expected != actual, actual, expected, message ); }, /** * @name propEqual * @function */ propEqual: function( actual, expected, message ) { actual = objectValues(actual); expected = objectValues(expected); QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name notPropEqual * @function */ notPropEqual: function( actual, expected, message ) { actual = objectValues(actual); expected = objectValues(expected); QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name deepEqual * @function */ deepEqual: function( actual, expected, message ) { QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name notDeepEqual * @function */ notDeepEqual: function( actual, expected, message ) { QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); }, /** * @name strictEqual * @function */ strictEqual: function( actual, expected, message ) { QUnit.push( expected === actual, actual, expected, message ); }, /** * @name notStrictEqual * @function */ notStrictEqual: function( actual, expected, message ) { QUnit.push( expected !== actual, actual, expected, message ); }, "throws": function( block, expected, message ) { var actual, expectedOutput = expected, ok = false; // 'expected' is optional if ( typeof expected === "string" ) { message = expected; expected = null; } config.current.ignoreGlobalErrors = true; try { block.call( config.current.testEnvironment ); } catch (e) { actual = e; } config.current.ignoreGlobalErrors = false; if ( actual ) { // we don't want to validate thrown error if ( !expected ) { ok = true; expectedOutput = null; // expected is a regexp } else if ( QUnit.objectType( expected ) === "regexp" ) { ok = expected.test( errorString( actual ) ); // expected is a constructor } else if ( actual instanceof expected ) { ok = true; // expected is a validation function which returns true is validation passed } else if ( expected.call( {}, actual ) === true ) { expectedOutput = null; ok = true; } QUnit.push( ok, actual, expectedOutput, message ); } else { QUnit.pushFailure( message, null, 'No exception was thrown.' ); } } }; /** * @deprecate since 1.8.0 * Kept assertion helpers in root for backwards compatibility. */ extend( QUnit, assert ); /** * @deprecated since 1.9.0 * Kept root "raises()" for backwards compatibility. * (Note that we don't introduce assert.raises). */ QUnit.raises = assert[ "throws" ]; /** * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 * Kept to avoid TypeErrors for undefined methods. */ QUnit.equals = function() { QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); }; QUnit.same = function() { QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); }; // We want access to the constructor's prototype (function() { function F() {} F.prototype = QUnit; QUnit = new F(); // Make F QUnit's constructor so that we can add to the prototype later QUnit.constructor = F; }()); /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ config = { // The queue of tests to run queue: [], // block until document ready blocking: true, // when enabled, show only failing tests // gets persisted through sessionStorage and can be changed in UI via checkbox hidepassed: false, // by default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // by default, modify document.title when suite is done altertitle: true, // when enabled, all tests must call expect() requireExpects: false, // add checkboxes that are persisted in the query-string // when enabled, the id is set to `true` as a `QUnit.config` property urlConfig: [ { id: "noglobals", label: "Check for Globals", tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." }, { id: "notrycatch", label: "No try-catch", tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." } ], // Set of all modules. modules: {}, // logging callback queues begin: [], done: [], log: [], testStart: [], testDone: [], moduleStart: [], moduleDone: [] }; // Export global variables, unless an 'exports' object exists, // in that case we assume we're in CommonJS (dealt with on the bottom of the script) if ( typeof exports === "undefined" ) { extend( window, QUnit ); // Expose QUnit object window.QUnit = QUnit; } // Initialize more QUnit.config and QUnit.urlParams (function() { var i, location = window.location || { search: "", protocol: "file:" }, params = location.search.slice( 1 ).split( "&" ), length = params.length, urlParams = {}, current; if ( params[ 0 ] ) { for ( i = 0; i < length; i++ ) { current = params[ i ].split( "=" ); current[ 0 ] = decodeURIComponent( current[ 0 ] ); // allow just a key to turn on a flag, e.g., test.html?noglobals current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; urlParams[ current[ 0 ] ] = current[ 1 ]; } } QUnit.urlParams = urlParams; // String search anywhere in moduleName+testName config.filter = urlParams.filter; // Exact match of the module name config.module = urlParams.module; config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; // Figure out if we're running the tests from a server or not QUnit.isLocal = location.protocol === "file:"; }()); // Extend QUnit object, // these after set here because they should not be exposed as global functions extend( QUnit, { assert: assert, config: config, // Initialize the configuration options init: function() { extend( config, { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: +new Date(), updateRate: 1000, blocking: false, autostart: true, autorun: false, filter: "", queue: [], semaphore: 1 }); var tests, banner, result, qunit = id( "qunit" ); if ( qunit ) { qunit.innerHTML = "

" + escapeText( document.title ) + "

" + "

" + "
" + "

" + "
    "; } tests = id( "qunit-tests" ); banner = id( "qunit-banner" ); result = id( "qunit-testresult" ); if ( tests ) { tests.innerHTML = ""; } if ( banner ) { banner.className = ""; } if ( result ) { result.parentNode.removeChild( result ); } if ( tests ) { result = document.createElement( "p" ); result.id = "qunit-testresult"; result.className = "result"; tests.parentNode.insertBefore( result, tests ); result.innerHTML = "Running...
     "; } }, // Resets the test setup. Useful for tests that modify the DOM. reset: function() { var fixture = id( "qunit-fixture" ); if ( fixture ) { fixture.innerHTML = config.fixture; } }, // Trigger an event on an element. // @example triggerEvent( document.body, "click" ); triggerEvent: function( elem, type, event ) { if ( document.createEvent ) { event = document.createEvent( "MouseEvents" ); event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 0, 0, 0, 0, 0, false, false, false, false, 0, null); elem.dispatchEvent( event ); } else if ( elem.fireEvent ) { elem.fireEvent( "on" + type ); } }, // Safe object type checking is: function( type, obj ) { return QUnit.objectType( obj ) === type; }, objectType: function( obj ) { if ( typeof obj === "undefined" ) { return "undefined"; // consider: typeof null === object } if ( obj === null ) { return "null"; } var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), type = match && match[1] || ""; switch ( type ) { case "Number": if ( isNaN(obj) ) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Date": case "RegExp": case "Function": return type.toLowerCase(); } if ( typeof obj === "object" ) { return "object"; } return undefined; }, push: function( result, actual, expected, message ) { if ( !config.current ) { throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); } var output, source, details = { module: config.current.module, name: config.current.testName, result: result, message: message, actual: actual, expected: expected }; message = escapeText( message ) || ( result ? "okay" : "failed" ); message = "" + message + ""; output = message; if ( !result ) { expected = escapeText( QUnit.jsDump.parse(expected) ); actual = escapeText( QUnit.jsDump.parse(actual) ); output += ""; if ( actual !== expected ) { output += ""; output += ""; } source = sourceFromStacktrace(); if ( source ) { details.source = source; output += ""; } output += "
    Expected:
    " + expected + "
    Result:
    " + actual + "
    Diff:
    " + QUnit.diff( expected, actual ) + "
    Source:
    " + escapeText( source ) + "
    "; } runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: !!result, message: output }); }, pushFailure: function( message, source, actual ) { if ( !config.current ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); } var output, details = { module: config.current.module, name: config.current.testName, result: false, message: message }; message = escapeText( message ) || "error"; message = "" + message + ""; output = message; output += ""; if ( actual ) { output += ""; } if ( source ) { details.source = source; output += ""; } output += "
    Result:
    " + escapeText( actual ) + "
    Source:
    " + escapeText( source ) + "
    "; runLoggingCallbacks( "log", QUnit, details ); config.current.assertions.push({ result: false, message: output }); }, url: function( params ) { params = extend( extend( {}, QUnit.urlParams ), params ); var key, querystring = "?"; for ( key in params ) { if ( !hasOwn.call( params, key ) ) { continue; } querystring += encodeURIComponent( key ) + "=" + encodeURIComponent( params[ key ] ) + "&"; } return window.location.protocol + "//" + window.location.host + window.location.pathname + querystring.slice( 0, -1 ); }, extend: extend, id: id, addEvent: addEvent // load, equiv, jsDump, diff: Attached later }); /** * @deprecated: Created for backwards compatibility with test runner that set the hook function * into QUnit.{hook}, instead of invoking it and passing the hook function. * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. * Doing this allows us to tell if the following methods have been overwritten on the actual * QUnit object. */ extend( QUnit.constructor.prototype, { // Logging callbacks; all receive a single argument with the listed properties // run test/logs.html for any related changes begin: registerLoggingCallback( "begin" ), // done: { failed, passed, total, runtime } done: registerLoggingCallback( "done" ), // log: { result, actual, expected, message } log: registerLoggingCallback( "log" ), // testStart: { name } testStart: registerLoggingCallback( "testStart" ), // testDone: { name, failed, passed, total, duration } testDone: registerLoggingCallback( "testDone" ), // moduleStart: { name } moduleStart: registerLoggingCallback( "moduleStart" ), // moduleDone: { name, failed, passed, total } moduleDone: registerLoggingCallback( "moduleDone" ) }); if ( typeof document === "undefined" || document.readyState === "complete" ) { config.autorun = true; } QUnit.load = function() { runLoggingCallbacks( "begin", QUnit, {} ); // Initialize the config, saving the execution queue var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, numModules = 0, moduleFilterHtml = "", urlConfigHtml = "", oldconfig = extend( {}, config ); QUnit.init(); extend(config, oldconfig); config.blocking = false; len = config.urlConfig.length; for ( i = 0; i < len; i++ ) { val = config.urlConfig[i]; if ( typeof val === "string" ) { val = { id: val, label: val, tooltip: "[no tooltip available]" }; } config[ val.id ] = QUnit.urlParams[ val.id ]; urlConfigHtml += ""; } moduleFilterHtml += ""; // `userAgent` initialized at top of scope userAgent = id( "qunit-userAgent" ); if ( userAgent ) { userAgent.innerHTML = navigator.userAgent; } // `banner` initialized at top of scope banner = id( "qunit-header" ); if ( banner ) { banner.innerHTML = "" + banner.innerHTML + " "; } // `toolbar` initialized at top of scope toolbar = id( "qunit-testrunner-toolbar" ); if ( toolbar ) { // `filter` initialized at top of scope filter = document.createElement( "input" ); filter.type = "checkbox"; filter.id = "qunit-filter-pass"; addEvent( filter, "click", function() { var tmp, ol = document.getElementById( "qunit-tests" ); if ( filter.checked ) { ol.className = ol.className + " hidepass"; } else { tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; ol.className = tmp.replace( / hidepass /, " " ); } if ( defined.sessionStorage ) { if (filter.checked) { sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); } else { sessionStorage.removeItem( "qunit-filter-passed-tests" ); } } }); if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { filter.checked = true; // `ol` initialized at top of scope ol = document.getElementById( "qunit-tests" ); ol.className = ol.className + " hidepass"; } toolbar.appendChild( filter ); // `label` initialized at top of scope label = document.createElement( "label" ); label.setAttribute( "for", "qunit-filter-pass" ); label.setAttribute( "title", "Only show tests and assertons that fail. Stored in sessionStorage." ); label.innerHTML = "Hide passed tests"; toolbar.appendChild( label ); urlConfigCheckboxesContainer = document.createElement("span"); urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); // For oldIE support: // * Add handlers to the individual elements instead of the container // * Use "click" instead of "change" // * Fallback from event.target to event.srcElement addEvents( urlConfigCheckboxes, "click", function( event ) { var params = {}, target = event.target || event.srcElement; params[ target.name ] = target.checked ? true : undefined; window.location = QUnit.url( params ); }); toolbar.appendChild( urlConfigCheckboxesContainer ); if (numModules > 1) { moduleFilter = document.createElement( 'span' ); moduleFilter.setAttribute( 'id', 'qunit-modulefilter-container' ); moduleFilter.innerHTML = moduleFilterHtml; addEvent( moduleFilter.lastChild, "change", function() { var selectBox = moduleFilter.getElementsByTagName("select")[0], selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); window.location = QUnit.url( { module: ( selectedModule === "" ) ? undefined : selectedModule } ); }); toolbar.appendChild(moduleFilter); } } // `main` initialized at top of scope main = id( "qunit-fixture" ); if ( main ) { config.fixture = main.innerHTML; } if ( config.autostart ) { QUnit.start(); } }; addEvent( window, "load", QUnit.load ); // `onErrorFnPrev` initialized at top of scope // Preserve other handlers onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will surpress the default browser handler, // returning false will let it run. window.onerror = function ( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); } // Treat return value as window.onerror itself does, // Only do our handling if not surpressed. if ( ret !== true ) { if ( QUnit.config.current ) { if ( QUnit.config.current.ignoreGlobalErrors ) { return true; } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { QUnit.test( "global failure", extend( function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); }, { validTest: validTest } ) ); } return false; } return ret; }; function done() { config.autorun = true; // Log the last module results if ( config.currentModule ) { runLoggingCallbacks( "moduleDone", QUnit, { name: config.currentModule, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all }); } var i, key, banner = id( "qunit-banner" ), tests = id( "qunit-tests" ), runtime = +new Date() - config.started, passed = config.stats.all - config.stats.bad, html = [ "Tests completed in ", runtime, " milliseconds.
    ", "", passed, " assertions of ", config.stats.all, " passed, ", config.stats.bad, " failed." ].join( "" ); if ( banner ) { banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); } if ( tests ) { id( "qunit-testresult" ).innerHTML = html; } if ( config.altertitle && typeof document !== "undefined" && document.title ) { // show ✖ for good, ✔ for bad suite result in title // use escape sequences in case file gets loaded with non-utf-8-charset document.title = [ ( config.stats.bad ? "\u2716" : "\u2714" ), document.title.replace( /^[\u2714\u2716] /i, "" ) ].join( " " ); } // clear own sessionStorage items if all tests passed if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { // `key` & `i` initialized at top of scope for ( i = 0; i < sessionStorage.length; i++ ) { key = sessionStorage.key( i++ ); if ( key.indexOf( "qunit-test-" ) === 0 ) { sessionStorage.removeItem( key ); } } } // scroll back to top to show results if ( window.scrollTo ) { window.scrollTo(0, 0); } runLoggingCallbacks( "done", QUnit, { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime }); } /** @return Boolean: true if this test should be ran */ function validTest( test ) { var include, filter = config.filter && config.filter.toLowerCase(), module = config.module && config.module.toLowerCase(), fullName = (test.module + ": " + test.testName).toLowerCase(); // Internally-generated tests are always valid if ( test.callback && test.callback.validTest === validTest ) { delete test.callback.validTest; return true; } if ( config.testNumber ) { return test.testNumber === config.testNumber; } if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { return false; } if ( !filter ) { return true; } include = filter.charAt( 0 ) !== "!"; if ( !include ) { filter = filter.slice( 1 ); } // If the filter matches, we need to honour include if ( fullName.indexOf( filter ) !== -1 ) { return include; } // Otherwise, do the opposite return !include; } // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) // Later Safari and IE10 are supposed to support error.stack as well // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 3 : offset; var stack, include, i; if ( e.stacktrace ) { // Opera return e.stacktrace.split( "\n" )[ offset + 3 ]; } else if ( e.stack ) { // Firefox, Chrome stack = e.stack.split( "\n" ); if (/^error$/i.test( stack[0] ) ) { stack.shift(); } if ( fileName ) { include = []; for ( i = offset; i < stack.length; i++ ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) { break; } include.push( stack[ i ] ); } if ( include.length ) { return include.join( "\n" ); } } return stack[ offset ]; } else if ( e.sourceURL ) { // Safari, PhantomJS // hopefully one day Safari provides actual stacktraces // exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } // for actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } function sourceFromStacktrace( offset ) { try { throw new Error(); } catch ( e ) { return extractStacktrace( e, offset ); } } /** * Escape text for attribute or text content. */ function escapeText( s ) { if ( !s ) { return ""; } s = s + ""; // Both single quotes and double quotes (for attributes) return s.replace( /['"<>&]/g, function( s ) { switch( s ) { case '\'': return '''; case '"': return '"'; case '<': return '<'; case '>': return '>'; case '&': return '&'; } }); } function synchronize( callback, last ) { config.queue.push( callback ); if ( config.autorun && !config.blocking ) { process( last ); } } function process( last ) { function next() { process( last ); } var start = new Date().getTime(); config.depth = config.depth ? config.depth + 1 : 1; while ( config.queue.length && !config.blocking ) { if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { config.queue.shift()(); } else { window.setTimeout( next, 13 ); break; } } config.depth--; if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in window ) { // in Opera sometimes DOM element ids show up here, ignore them if ( !hasOwn.call( window, key ) || /^qunit-test-output/.test( key ) ) { continue; } config.pollution.push( key ); } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); } } // returns a new Array with the elements that are in a but not in b function diff( a, b ) { var i, j, result = a.slice(); for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { if ( result[i] === b[j] ) { result.splice( i, 1 ); i--; break; } } } return result; } function extend( a, b ) { for ( var prop in b ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; // Avoid "Member not found" error in IE8 caused by setting window.constructor } else if ( prop !== "constructor" || a !== window ) { a[ prop ] = b[ prop ]; } } return a; } /** * @param {HTMLElement} elem * @param {string} type * @param {Function} fn */ function addEvent( elem, type, fn ) { // Standards-based browsers if ( elem.addEventListener ) { elem.addEventListener( type, fn, false ); // IE } else { elem.attachEvent( "on" + type, fn ); } } /** * @param {Array|NodeList} elems * @param {string} type * @param {Function} fn */ function addEvents( elems, type, fn ) { var i = elems.length; while ( i-- ) { addEvent( elems[i], type, fn ); } } function hasClass( elem, name ) { return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; } function addClass( elem, name ) { if ( !hasClass( elem, name ) ) { elem.className += (elem.className ? " " : "") + name; } } function removeClass( elem, name ) { var set = " " + elem.className + " "; // Class name may appear multiple times while ( set.indexOf(" " + name + " ") > -1 ) { set = set.replace(" " + name + " " , " "); } // If possible, trim it for prettiness, but not neccecarily elem.className = window.jQuery ? jQuery.trim( set ) : ( set.trim ? set.trim() : set ); } function id( name ) { return !!( typeof document !== "undefined" && document && document.getElementById ) && document.getElementById( name ); } function registerLoggingCallback( key ) { return function( callback ) { config[key].push( callback ); }; } // Supports deprecated method of completely overwriting logging callbacks function runLoggingCallbacks( key, scope, args ) { var i, callbacks; if ( QUnit.hasOwnProperty( key ) ) { QUnit[ key ].call(scope, args ); } else { callbacks = config[ key ]; for ( i = 0; i < callbacks.length; i++ ) { callbacks[ i ].call( scope, args ); } } } // Test for equality any JavaScript type. // Author: Philippe Rathé QUnit.equiv = (function() { // Call the o related callback with the given arguments. function bindCallbacks( o, callbacks, args ) { var prop = QUnit.objectType( o ); if ( prop ) { if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { return callbacks[ prop ].apply( callbacks, args ); } else { return callbacks[ prop ]; // or undefined } } } // the real equiv function var innerEquiv, // stack to decide between skip/abort functions callers = [], // stack to avoiding loops from circular referencing parents = [], getProto = Object.getPrototypeOf || function ( obj ) { return obj.__proto__; }, callbacks = (function () { // for string, boolean, number and null function useStrictEquality( b, a ) { /*jshint eqeqeq:false */ if ( b instanceof a.constructor || a instanceof b.constructor ) { // to catch short annotaion VS 'new' annotation of a // declaration // e.g. var i = 1; // var j = new Number(1); return a == b; } else { return a === b; } } return { "string": useStrictEquality, "boolean": useStrictEquality, "number": useStrictEquality, "null": useStrictEquality, "undefined": useStrictEquality, "nan": function( b ) { return isNaN( b ); }, "date": function( b, a ) { return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); }, "regexp": function( b, a ) { return QUnit.objectType( b ) === "regexp" && // the regex itself a.source === b.source && // and its modifers a.global === b.global && // (gmi) ... a.ignoreCase === b.ignoreCase && a.multiline === b.multiline && a.sticky === b.sticky; }, // - skip when the property is a method of an instance (OOP) // - abort otherwise, // initial === would have catch identical references anyway "function": function() { var caller = callers[callers.length - 1]; return caller !== Object && typeof caller !== "undefined"; }, "array": function( b, a ) { var i, j, len, loop; // b could be an object literal here if ( QUnit.objectType( b ) !== "array" ) { return false; } len = a.length; if ( len !== b.length ) { // safe and faster return false; } // track reference to avoid circular references parents.push( a ); for ( i = 0; i < len; i++ ) { loop = false; for ( j = 0; j < parents.length; j++ ) { if ( parents[j] === a[i] ) { loop = true;// dont rewalk array } } if ( !loop && !innerEquiv(a[i], b[i]) ) { parents.pop(); return false; } } parents.pop(); return true; }, "object": function( b, a ) { var i, j, loop, // Default to true eq = true, aProperties = [], bProperties = []; // comparing constructors is more strict than using // instanceof if ( a.constructor !== b.constructor ) { // Allow objects with no prototype to be equivalent to // objects with Object as their constructor. if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { return false; } } // stack constructor before traversing properties callers.push( a.constructor ); // track reference to avoid circular references parents.push( a ); for ( i in a ) { // be strict: don't ensures hasOwnProperty // and go deep loop = false; for ( j = 0; j < parents.length; j++ ) { if ( parents[j] === a[i] ) { // don't go down the same path twice loop = true; } } aProperties.push(i); // collect a's properties if (!loop && !innerEquiv( a[i], b[i] ) ) { eq = false; break; } } callers.pop(); // unstack, we are done parents.pop(); for ( i in b ) { bProperties.push( i ); // collect b's properties } // Ensures identical properties name return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); } }; }()); innerEquiv = function() { // can take multiple arguments var args = [].slice.apply( arguments ); if ( args.length < 2 ) { return true; // end transition } return (function( a, b ) { if ( a === b ) { return true; // catch the most you can } else if ( a === null || b === null || typeof a === "undefined" || typeof b === "undefined" || QUnit.objectType(a) !== QUnit.objectType(b) ) { return false; // don't lose time with error prone cases } else { return bindCallbacks(a, callbacks, [ b, a ]); } // apply transition with (1..n) arguments }( args[0], args[1] ) && arguments.callee.apply( this, args.splice(1, args.length - 1 )) ); }; return innerEquiv; }()); /** * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | * http://flesler.blogspot.com Licensed under BSD * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 * * @projectDescription Advanced and extensible data dumping for Javascript. * @version 1.0.0 * @author Ariel Flesler * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} */ QUnit.jsDump = (function() { function quote( str ) { return '"' + str.toString().replace( /"/g, '\\"' ) + '"'; } function literal( o ) { return o + ""; } function join( pre, arr, post ) { var s = jsDump.separator(), base = jsDump.indent(), inner = jsDump.indent(1); if ( arr.join ) { arr = arr.join( "," + s + inner ); } if ( !arr ) { return pre + post; } return [ pre, inner + arr, base + post ].join(s); } function array( arr, stack ) { var i = arr.length, ret = new Array(i); this.up(); while ( i-- ) { ret[i] = this.parse( arr[i] , undefined , stack); } this.down(); return join( "[", ret, "]" ); } var reName = /^function (\w+)/, jsDump = { // type is used mostly internally, you can fix a (custom)type in advance parse: function( obj, type, stack ) { stack = stack || [ ]; var inStack, res, parser = this.parsers[ type || this.typeOf(obj) ]; type = typeof parser; inStack = inArray( obj, stack ); if ( inStack !== -1 ) { return "recursion(" + (inStack - stack.length) + ")"; } if ( type === "function" ) { stack.push( obj ); res = parser.call( this, obj, stack ); stack.pop(); return res; } return ( type === "string" ) ? parser : this.parsers.error; }, typeOf: function( obj ) { var type; if ( obj === null ) { type = "null"; } else if ( typeof obj === "undefined" ) { type = "undefined"; } else if ( QUnit.is( "regexp", obj) ) { type = "regexp"; } else if ( QUnit.is( "date", obj) ) { type = "date"; } else if ( QUnit.is( "function", obj) ) { type = "function"; } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { type = "window"; } else if ( obj.nodeType === 9 ) { type = "document"; } else if ( obj.nodeType ) { type = "node"; } else if ( // native arrays toString.call( obj ) === "[object Array]" || // NodeList objects ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) ) { type = "array"; } else if ( obj.constructor === Error.prototype.constructor ) { type = "error"; } else { type = typeof obj; } return type; }, separator: function() { return this.multiline ? this.HTML ? "
    " : "\n" : this.HTML ? " " : " "; }, // extra can be a number, shortcut for increasing-calling-decreasing indent: function( extra ) { if ( !this.multiline ) { return ""; } var chr = this.indentChar; if ( this.HTML ) { chr = chr.replace( /\t/g, " " ).replace( / /g, " " ); } return new Array( this._depth_ + (extra||0) ).join(chr); }, up: function( a ) { this._depth_ += a || 1; }, down: function( a ) { this._depth_ -= a || 1; }, setParser: function( name, parser ) { this.parsers[name] = parser; }, // The next 3 are exposed so you can use them quote: quote, literal: literal, join: join, // _depth_: 1, // This is the list of parsers, to modify them, use jsDump.setParser parsers: { window: "[Window]", document: "[Document]", error: function(error) { return "Error(\"" + error.message + "\")"; }, unknown: "[Unknown]", "null": "null", "undefined": "undefined", "function": function( fn ) { var ret = "function", // functions never have name in IE name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; if ( name ) { ret += " " + name; } ret += "( "; ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); }, array: array, nodelist: array, "arguments": array, object: function( map, stack ) { var ret = [ ], keys, key, val, i; QUnit.jsDump.up(); keys = []; for ( key in map ) { keys.push( key ); } keys.sort(); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; val = map[ key ]; ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); } QUnit.jsDump.down(); return join( "{", ret, "}" ); }, node: function( node ) { var len, i, val, open = QUnit.jsDump.HTML ? "<" : "<", close = QUnit.jsDump.HTML ? ">" : ">", tag = node.nodeName.toLowerCase(), ret = open + tag, attrs = node.attributes; if ( attrs ) { for ( i = 0, len = attrs.length; i < len; i++ ) { val = attrs[i].nodeValue; // IE6 includes all attributes in .attributes, even ones not explicitly set. // Those have values like undefined, null, 0, false, "" or "inherit". if ( val && val !== "inherit" ) { ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); } } } ret += close; // Show content of TextNode or CDATASection if ( node.nodeType === 3 || node.nodeType === 4 ) { ret += node.nodeValue; } return ret + open + "/" + tag + close; }, // function calls it internally, it's the arguments part of the function functionArgs: function( fn ) { var args, l = fn.length; if ( !l ) { return ""; } args = new Array(l); while ( l-- ) { // 97 is 'a' args[l] = String.fromCharCode(97+l); } return " " + args.join( ", " ) + " "; }, // object calls it internally, the key part of an item in a map key: quote, // function calls it internally, it's the content of the function functionCode: "[code]", // node calls it internally, it's an html attribute value attribute: quote, string: quote, date: quote, regexp: literal, number: literal, "boolean": literal }, // if true, entities are escaped ( <, >, \t, space and \n ) HTML: false, // indentation unit indentChar: " ", // if true, items in a collection, are separated by a \n, else just a space. multiline: true }; return jsDump; }()); // from jquery.js function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } /* * Javascript Diff Algorithm * By John Resig (http://ejohn.org/) * Modified by Chu Alan "sprite" * * Released under the MIT license. * * More Info: * http://ejohn.org/projects/javascript-diff-algorithm/ * * Usage: QUnit.diff(expected, actual) * * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over" */ QUnit.diff = (function() { /*jshint eqeqeq:false, eqnull:true */ function diff( o, n ) { var i, ns = {}, os = {}; for ( i = 0; i < n.length; i++ ) { if ( !hasOwn.call( ns, n[i] ) ) { ns[ n[i] ] = { rows: [], o: null }; } ns[ n[i] ].rows.push( i ); } for ( i = 0; i < o.length; i++ ) { if ( !hasOwn.call( os, o[i] ) ) { os[ o[i] ] = { rows: [], n: null }; } os[ o[i] ].rows.push( i ); } for ( i in ns ) { if ( !hasOwn.call( ns, i ) ) { continue; } if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) { n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; } } for ( i = 0; i < n.length - 1; i++ ) { if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && n[ i + 1 ] == o[ n[i].row + 1 ] ) { n[ i + 1 ] = { text: n[ i + 1 ], row: n[i].row + 1 }; o[ n[i].row + 1 ] = { text: o[ n[i].row + 1 ], row: i + 1 }; } } for ( i = n.length - 1; i > 0; i-- ) { if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && n[ i - 1 ] == o[ n[i].row - 1 ]) { n[ i - 1 ] = { text: n[ i - 1 ], row: n[i].row - 1 }; o[ n[i].row - 1 ] = { text: o[ n[i].row - 1 ], row: i - 1 }; } } return { o: o, n: n }; } return function( o, n ) { o = o.replace( /\s+$/, "" ); n = n.replace( /\s+$/, "" ); var i, pre, str = "", out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), oSpace = o.match(/\s+/g), nSpace = n.match(/\s+/g); if ( oSpace == null ) { oSpace = [ " " ]; } else { oSpace.push( " " ); } if ( nSpace == null ) { nSpace = [ " " ]; } else { nSpace.push( " " ); } if ( out.n.length === 0 ) { for ( i = 0; i < out.o.length; i++ ) { str += "" + out.o[i] + oSpace[i] + ""; } } else { if ( out.n[0].text == null ) { for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { str += "" + out.o[n] + oSpace[n] + ""; } } for ( i = 0; i < out.n.length; i++ ) { if (out.n[i].text == null) { str += "" + out.n[i] + nSpace[i] + ""; } else { // `pre` initialized at top of scope pre = ""; for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { pre += "" + out.o[n] + oSpace[n] + ""; } str += " " + out.n[i].text + nSpace[i] + pre; } } } return str; }; }()); // for CommonJS enviroments, export everything if ( typeof exports !== "undefined" ) { extend( exports, QUnit ); } // get at whatever the global object is, like window in browsers }( (function() {return this;}.call()) )); ganglia-web-3.6.1/contrib/jquery-ui-1.10.2/jquery-1.9.1.js000066400000000000000000010141351231750357400223420ustar00rootroot00000000000000/*! * jQuery JavaScript Library v1.9.1 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * * Date: 2013-2-4 */ (function( window, undefined ) { // Can't do this because several apps including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) // Support: Firefox 18+ //"use strict"; var // The deferred used on DOM ready readyList, // A central reference to the root jQuery(document) rootjQuery, // Support: IE<9 // For `typeof node.method` instead of `node.method !== undefined` core_strundefined = typeof undefined, // Use the correct document accordingly with window argument (sandbox) document = window.document, location = window.location, // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, // [[Class]] -> type pairs class2type = {}, // List of deleted data cache ids, so we can reuse them core_deletedIds = [], core_version = "1.9.1", // Save a reference to some core methods core_concat = core_deletedIds.concat, core_push = core_deletedIds.push, core_slice = core_deletedIds.slice, core_indexOf = core_deletedIds.indexOf, core_toString = class2type.toString, core_hasOwn = class2type.hasOwnProperty, core_trim = core_version.trim, // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, // Used for matching numbers core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, // Used for splitting on whitespace core_rnotwhite = /\S+/g, // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // A simple way to check for HTML strings // Prioritize #id over to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, // JSON RegExp rvalidchars = /^[\],:{}\s]*$/, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); }, // The ready event handler completed = function( event ) { // readyState === "complete" is good enough for us to call the dom ready in oldIE if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { detach(); jQuery.ready(); } }, // Clean-up method for dom ready events detach = function() { if ( document.addEventListener ) { document.removeEventListener( "DOMContentLoaded", completed, false ); window.removeEventListener( "load", completed, false ); } else { document.detachEvent( "onreadystatechange", completed ); window.detachEvent( "onload", completed ); } }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used jquery: core_version, constructor: jQuery, init: function( selector, context, rootjQuery ) { var match, elem; // HANDLE: $(""), $(null), $(undefined), $(false) if ( !selector ) { return this; } // Handle HTML strings if ( typeof selector === "string" ) { if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; } else { match = rquickExpr.exec( selector ); } // Match html or make sure no context is specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) { context = context instanceof jQuery ? context[0] : context; // scripts is true for back-compat jQuery.merge( this, jQuery.parseHTML( match[1], context && context.nodeType ? context.ownerDocument || context : document, true ) ); // HANDLE: $(html, props) if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { for ( match in context ) { // Properties of context are called as methods if possible if ( jQuery.isFunction( this[ match ] ) ) { this[ match ]( context[ match ] ); // ...and otherwise set as attributes } else { this.attr( match, context[ match ] ); } } } return this; // HANDLE: $(#id) } else { elem = document.getElementById( match[2] ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE and Opera return items // by name instead of ID if ( elem.id !== match[2] ) { return rootjQuery.find( selector ); } // Otherwise, we inject the element directly into the jQuery object this.length = 1; this[0] = elem; } this.context = document; this.selector = selector; return this; } // HANDLE: $(expr, $(...)) } else if ( !context || context.jquery ) { return ( context || rootjQuery ).find( selector ); // HANDLE: $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return this.constructor( context ).find( selector ); } // HANDLE: $(DOMElement) } else if ( selector.nodeType ) { this.context = this[0] = selector; this.length = 1; return this; // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } if ( selector.selector !== undefined ) { this.selector = selector.selector; this.context = selector.context; } return jQuery.makeArray( selector, this ); }, // Start with an empty selector selector: "", // The default length of a jQuery object is 0 length: 0, // The number of elements contained in the matched element set size: function() { return this.length; }, toArray: function() { return core_slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num == null ? // Return a 'clean' array this.toArray() : // Return just the object ( num < 0 ? this[ this.length + num ] : this[ num ] ); }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems ) { // Build a new jQuery matched element set var ret = jQuery.merge( this.constructor(), elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; // Return the newly-formed element set return ret; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, ready: function( fn ) { // Add the callback jQuery.ready.promise().done( fn ); return this; }, slice: function() { return this.pushStack( core_slice.apply( this, arguments ) ); }, first: function() { return this.eq( 0 ); }, last: function() { return this.eq( -1 ); }, eq: function( i ) { var len = this.length, j = +i + ( i < 0 ? len : 0 ); return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function( elem, i ) { return callback.call( elem, i, elem ); })); }, end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: core_push, sort: [].sort, splice: [].splice }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; jQuery.extend = jQuery.fn.extend = function() { var src, copyIsArray, copy, name, options, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // extend jQuery itself if only one argument is passed if ( length === i ) { target = this; --i; } for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // Extend the base object for ( name in options ) { src = target[ name ]; copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) { continue; } // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { clone = src && jQuery.isPlainObject(src) ? src : {}; } // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // Don't bring in undefined values } else if ( copy !== undefined ) { target[ name ] = copy; } } } } // Return the modified object return target; }; jQuery.extend({ noConflict: function( deep ) { if ( window.$ === jQuery ) { window.$ = _$; } if ( deep && window.jQuery === jQuery ) { window.jQuery = _jQuery; } return jQuery; }, // Is the DOM ready to be used? Set to true once it occurs. isReady: false, // A counter to track how many items to wait for before // the ready event fires. See #6781 readyWait: 1, // Hold (or release) the ready event holdReady: function( hold ) { if ( hold ) { jQuery.readyWait++; } else { jQuery.ready( true ); } }, // Handle when the DOM is ready ready: function( wait ) { // Abort if there are pending holds or we're already ready if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if ( !document.body ) { return setTimeout( jQuery.ready ); } // Remember that the DOM is ready jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be if ( wait !== true && --jQuery.readyWait > 0 ) { return; } // If there are functions bound, to execute readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events if ( jQuery.fn.trigger ) { jQuery( document ).trigger("ready").off("ready"); } }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return jQuery.type(obj) === "function"; }, isArray: Array.isArray || function( obj ) { return jQuery.type(obj) === "array"; }, isWindow: function( obj ) { return obj != null && obj == obj.window; }, isNumeric: function( obj ) { return !isNaN( parseFloat(obj) ) && isFinite( obj ); }, type: function( obj ) { if ( obj == null ) { return String( obj ); } return typeof obj === "object" || typeof obj === "function" ? class2type[ core_toString.call(obj) ] || "object" : typeof obj; }, isPlainObject: function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { return false; } try { // Not own constructor property must be Object if ( obj.constructor && !core_hasOwn.call(obj, "constructor") && !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } } catch ( e ) { // IE8,9 Will throw exceptions on certain host objects #9897 return false; } // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. var key; for ( key in obj ) {} return key === undefined || core_hasOwn.call( obj, key ); }, isEmptyObject: function( obj ) { var name; for ( name in obj ) { return false; } return true; }, error: function( msg ) { throw new Error( msg ); }, // data: string of html // context (optional): If specified, the fragment will be created in this context, defaults to document // keepScripts (optional): If true, will include scripts passed in the html string parseHTML: function( data, context, keepScripts ) { if ( !data || typeof data !== "string" ) { return null; } if ( typeof context === "boolean" ) { keepScripts = context; context = false; } context = context || document; var parsed = rsingleTag.exec( data ), scripts = !keepScripts && []; // Single tag if ( parsed ) { return [ context.createElement( parsed[1] ) ]; } parsed = jQuery.buildFragment( [ data ], context, scripts ); if ( scripts ) { jQuery( scripts ).remove(); } return jQuery.merge( [], parsed.childNodes ); }, parseJSON: function( data ) { // Attempt to parse using the native JSON parser first if ( window.JSON && window.JSON.parse ) { return window.JSON.parse( data ); } if ( data === null ) { return data; } if ( typeof data === "string" ) { // Make sure leading/trailing whitespace is removed (IE can't handle it) data = jQuery.trim( data ); if ( data ) { // Make sure the incoming data is actual JSON // Logic borrowed from http://json.org/json2.js if ( rvalidchars.test( data.replace( rvalidescape, "@" ) .replace( rvalidtokens, "]" ) .replace( rvalidbraces, "")) ) { return ( new Function( "return " + data ) )(); } } } jQuery.error( "Invalid JSON: " + data ); }, // Cross-browser xml parsing parseXML: function( data ) { var xml, tmp; if ( !data || typeof data !== "string" ) { return null; } try { if ( window.DOMParser ) { // Standard tmp = new DOMParser(); xml = tmp.parseFromString( data , "text/xml" ); } else { // IE xml = new ActiveXObject( "Microsoft.XMLDOM" ); xml.async = "false"; xml.loadXML( data ); } } catch( e ) { xml = undefined; } if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { jQuery.error( "Invalid XML: " + data ); } return xml; }, noop: function() {}, // Evaluates a script in a global context // Workarounds based on findings by Jim Driscoll // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context globalEval: function( data ) { if ( data && jQuery.trim( data ) ) { // We use execScript on Internet Explorer // We use an anonymous function so that context is window // rather than jQuery in Firefox ( window.execScript || function( data ) { window[ "eval" ].call( window, data ); } )( data ); } }, // Convert dashed to camelCase; used by the css and data modules // Microsoft forgot to hump their vendor prefix (#9572) camelCase: function( string ) { return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); }, // args is for internal usage only each: function( obj, callback, args ) { var value, i = 0, length = obj.length, isArray = isArraylike( obj ); if ( args ) { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.apply( obj[ i ], args ); if ( value === false ) { break; } } } // A special, fast, case for the most common use of each } else { if ( isArray ) { for ( ; i < length; i++ ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } else { for ( i in obj ) { value = callback.call( obj[ i ], i, obj[ i ] ); if ( value === false ) { break; } } } } return obj; }, // Use native String.trim function wherever possible trim: core_trim && !core_trim.call("\uFEFF\xA0") ? function( text ) { return text == null ? "" : core_trim.call( text ); } : // Otherwise use our own trimming functionality function( text ) { return text == null ? "" : ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; if ( arr != null ) { if ( isArraylike( Object(arr) ) ) { jQuery.merge( ret, typeof arr === "string" ? [ arr ] : arr ); } else { core_push.call( ret, arr ); } } return ret; }, inArray: function( elem, arr, i ) { var len; if ( arr ) { if ( core_indexOf ) { return core_indexOf.call( arr, elem, i ); } len = arr.length; i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; for ( ; i < len; i++ ) { // Skip accessing in sparse arrays if ( i in arr && arr[ i ] === elem ) { return i; } } } return -1; }, merge: function( first, second ) { var l = second.length, i = first.length, j = 0; if ( typeof l === "number" ) { for ( ; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; } } first.length = i; return first; }, grep: function( elems, callback, inv ) { var retVal, ret = [], i = 0, length = elems.length; inv = !!inv; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { retVal = !!callback( elems[ i ], i ); if ( inv !== retVal ) { ret.push( elems[ i ] ); } } return ret; }, // arg is for internal usage only map: function( elems, callback, arg ) { var value, i = 0, length = elems.length, isArray = isArraylike( elems ), ret = []; // Go through the array, translating each of the items to their if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } // Go through every key on the object, } else { for ( i in elems ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { ret[ ret.length ] = value; } } } // Flatten any nested arrays return core_concat.apply( [], ret ); }, // A global GUID counter for objects guid: 1, // Bind a function to a context, optionally partially applying any // arguments. proxy: function( fn, context ) { var args, proxy, tmp; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = core_slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++; return proxy; }, // Multifunctional method to get and set values of a collection // The value/s can optionally be executed if it's a function access: function( elems, fn, key, value, chainable, emptyGet, raw ) { var i = 0, length = elems.length, bulk = key == null; // Sets many values if ( jQuery.type( key ) === "object" ) { chainable = true; for ( i in key ) { jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); } // Sets one value } else if ( value !== undefined ) { chainable = true; if ( !jQuery.isFunction( value ) ) { raw = true; } if ( bulk ) { // Bulk operations run against the entire set if ( raw ) { fn.call( elems, value ); fn = null; // ...except when executing function values } else { bulk = fn; fn = function( elem, key, value ) { return bulk.call( jQuery( elem ), value ); }; } } if ( fn ) { for ( ; i < length; i++ ) { fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); } } } return chainable ? elems : // Gets bulk ? fn.call( elems ) : length ? fn( elems[0], key ) : emptyGet; }, now: function() { return ( new Date() ).getTime(); } }); jQuery.ready.promise = function( obj ) { if ( !readyList ) { readyList = jQuery.Deferred(); // Catch cases where $(document).ready() is called after the browser event has already occurred. // we once tried to use readyState "interactive" here, but it caused issues like the one // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 if ( document.readyState === "complete" ) { // Handle it asynchronously to allow scripts the opportunity to delay ready setTimeout( jQuery.ready ); // Standards-based browsers support DOMContentLoaded } else if ( document.addEventListener ) { // Use the handy event callback document.addEventListener( "DOMContentLoaded", completed, false ); // A fallback to window.onload, that will always work window.addEventListener( "load", completed, false ); // If IE event model is used } else { // Ensure firing before onload, maybe late but safe also for iframes document.attachEvent( "onreadystatechange", completed ); // A fallback to window.onload, that will always work window.attachEvent( "onload", completed ); // If IE and not a frame // continually check to see if the document is ready var top = false; try { top = window.frameElement == null && document.documentElement; } catch(e) {} if ( top && top.doScroll ) { (function doScrollCheck() { if ( !jQuery.isReady ) { try { // Use the trick by Diego Perini // http://javascript.nwbox.com/IEContentLoaded/ top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); // and execute any waiting functions jQuery.ready(); } })(); } } } return readyList.promise( obj ); }; // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); }); function isArraylike( obj ) { var length = obj.length, type = jQuery.type( obj ); if ( jQuery.isWindow( obj ) ) { return false; } if ( obj.nodeType === 1 && length ) { return true; } return type === "array" || type !== "function" && ( length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj ); } // All jQuery objects should point back to these rootjQuery = jQuery(document); // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; } /* * Create a callback list using the following parameters: * * options: an optional list of space-separated options that will change how * the callback list behaves or a more traditional option object * * By default a callback list will act like an event callback list and can be * "fired" multiple times. * * Possible options: * * once: will ensure the callback list can only be fired once (like a Deferred) * * memory: will keep track of previous values and will call any callback added * after the list has been fired right away with the latest "memorized" * values (like a Deferred) * * unique: will ensure a callback can only be added once (no duplicate in the list) * * stopOnFalse: interrupt callings when a callback returns false * */ jQuery.Callbacks = function( options ) { // Convert options from String-formatted to Object-formatted if needed // (we check in cache first) options = typeof options === "string" ? ( optionsCache[ options ] || createOptions( options ) ) : jQuery.extend( {}, options ); var // Flag to know if list is currently firing firing, // Last fire value (for non-forgettable lists) memory, // Flag to know if list was already fired fired, // End of the loop when firing firingLength, // Index of currently firing callback (modified by remove if needed) firingIndex, // First callback to fire (used internally by add and fireWith) firingStart, // Actual callback list list = [], // Stack of fire calls for repeatable lists stack = !options.once && [], // Fire callbacks fire = function( data ) { memory = options.memory && data; fired = true; firingIndex = firingStart || 0; firingStart = 0; firingLength = list.length; firing = true; for ( ; list && firingIndex < firingLength; firingIndex++ ) { if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { memory = false; // To prevent further calls using add break; } } firing = false; if ( list ) { if ( stack ) { if ( stack.length ) { fire( stack.shift() ); } } else if ( memory ) { list = []; } else { self.disable(); } } }, // Actual Callbacks object self = { // Add a callback or a collection of callbacks to the list add: function() { if ( list ) { // First, we save the current length var start = list.length; (function add( args ) { jQuery.each( args, function( _, arg ) { var type = jQuery.type( arg ); if ( type === "function" ) { if ( !options.unique || !self.has( arg ) ) { list.push( arg ); } } else if ( arg && arg.length && type !== "string" ) { // Inspect recursively add( arg ); } }); })( arguments ); // Do we need to add the callbacks to the // current firing batch? if ( firing ) { firingLength = list.length; // With memory, if we're not firing then // we should call right away } else if ( memory ) { firingStart = start; fire( memory ); } } return this; }, // Remove a callback from the list remove: function() { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { if ( index <= firingLength ) { firingLength--; } if ( index <= firingIndex ) { firingIndex--; } } } }); } return this; }, // Check if a given callback is in the list. // If no argument is given, return whether or not list has callbacks attached. has: function( fn ) { return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); }, // Remove all callbacks from the list empty: function() { list = []; return this; }, // Have the list do nothing anymore disable: function() { list = stack = memory = undefined; return this; }, // Is it disabled? disabled: function() { return !list; }, // Lock the list in its current state lock: function() { stack = undefined; if ( !memory ) { self.disable(); } return this; }, // Is it locked? locked: function() { return !stack; }, // Call all callbacks with the given context and arguments fireWith: function( context, args ) { args = args || []; args = [ context, args.slice ? args.slice() : args ]; if ( list && ( !fired || stack ) ) { if ( firing ) { stack.push( args ); } else { fire( args ); } } return this; }, // Call all the callbacks with the given arguments fire: function() { self.fireWith( this, arguments ); return this; }, // To know if the callbacks have already been called at least once fired: function() { return !!fired; } }; return self; }; jQuery.extend({ Deferred: function( func ) { var tuples = [ // action, add listener, listener list, final state [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], [ "notify", "progress", jQuery.Callbacks("memory") ] ], state = "pending", promise = { state: function() { return state; }, always: function() { deferred.done( arguments ).fail( arguments ); return this; }, then: function( /* fnDone, fnFail, fnProgress */ ) { var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { var action = tuple[ 0 ], fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise() .done( newDefer.resolve ) .fail( newDefer.reject ) .progress( newDefer.notify ); } else { newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); fns = null; }).promise(); }, // Get a promise for this deferred // If obj is provided, the promise aspect is added to the object promise: function( obj ) { return obj != null ? jQuery.extend( obj, promise ) : promise; } }, deferred = {}; // Keep pipe for back-compat promise.pipe = promise.then; // Add list-specific methods jQuery.each( tuples, function( i, tuple ) { var list = tuple[ 2 ], stateString = tuple[ 3 ]; // promise[ done | fail | progress ] = list.add promise[ tuple[1] ] = list.add; // Handle state if ( stateString ) { list.add(function() { // state = [ resolved | rejected ] state = stateString; // [ reject_list | resolve_list ].disable; progress_list.lock }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); } // deferred[ resolve | reject | notify ] deferred[ tuple[0] ] = function() { deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); return this; }; deferred[ tuple[0] + "With" ] = list.fireWith; }); // Make the deferred a promise promise.promise( deferred ); // Call given func if any if ( func ) { func.call( deferred, deferred ); } // All done! return deferred; }, // Deferred helper when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, resolveValues = core_slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, // the master Deferred. If resolveValues consist of only a single Deferred, just use that. deferred = remaining === 1 ? subordinate : jQuery.Deferred(), // Update function for both resolve and progress values updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; if( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); } }; }, progressValues, progressContexts, resolveContexts; // add listeners to Deferred subordinates; treat others as resolved if ( length > 1 ) { progressValues = new Array( length ); progressContexts = new Array( length ); resolveContexts = new Array( length ); for ( ; i < length; i++ ) { if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { resolveValues[ i ].promise() .done( updateFunc( i, resolveContexts, resolveValues ) ) .fail( deferred.reject ) .progress( updateFunc( i, progressContexts, progressValues ) ); } else { --remaining; } } } // if we're not waiting on anything, resolve the master if ( !remaining ) { deferred.resolveWith( resolveContexts, resolveValues ); } return deferred.promise(); } }); jQuery.support = (function() { var support, all, a, input, select, fragment, opt, eventName, isSupported, i, div = document.createElement("div"); // Setup div.setAttribute( "className", "t" ); div.innerHTML = "
    a"; // Support tests won't run in some limited or non-browser environments all = div.getElementsByTagName("*"); a = div.getElementsByTagName("a")[ 0 ]; if ( !all || !a || !all.length ) { return {}; } // First batch of tests select = document.createElement("select"); opt = select.appendChild( document.createElement("option") ); input = div.getElementsByTagName("input")[ 0 ]; a.style.cssText = "top:1px;float:left;opacity:.5"; support = { // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) getSetAttribute: div.className !== "t", // IE strips leading whitespace when .innerHTML is used leadingWhitespace: div.firstChild.nodeType === 3, // Make sure that tbody elements aren't automatically inserted // IE will insert them into empty tables tbody: !div.getElementsByTagName("tbody").length, // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE htmlSerialize: !!div.getElementsByTagName("link").length, // Get the style information from getAttribute // (IE uses .cssText instead) style: /top/.test( a.getAttribute("style") ), // Make sure that URLs aren't manipulated // (IE normalizes it by default) hrefNormalized: a.getAttribute("href") === "/a", // Make sure that element opacity exists // (IE uses filter instead) // Use a regex to work around a WebKit issue. See #5145 opacity: /^0.5/.test( a.style.opacity ), // Verify style float existence // (IE uses styleFloat instead of cssFloat) cssFloat: !!a.style.cssFloat, // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) checkOn: !!input.value, // Make sure that a selected-by-default option has a working selected property. // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) optSelected: opt.selected, // Tests for enctype support on a form (#6743) enctype: !!document.createElement("form").enctype, // Makes sure cloning an html5 element does not cause problems // Where outerHTML is undefined, this still works html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>", // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode boxModel: document.compatMode === "CSS1Compat", // Will be defined later deleteExpando: true, noCloneEvent: true, inlineBlockNeedsLayout: false, shrinkWrapBlocks: false, reliableMarginRight: true, boxSizingReliable: true, pixelPosition: false }; // Make sure checked status is properly cloned input.checked = true; support.noCloneChecked = input.cloneNode( true ).checked; // Make sure that the options inside disabled selects aren't marked as disabled // (WebKit marks them as disabled) select.disabled = true; support.optDisabled = !opt.disabled; // Support: IE<9 try { delete div.test; } catch( e ) { support.deleteExpando = false; } // Check if we can trust getAttribute("value") input = document.createElement("input"); input.setAttribute( "value", "" ); support.input = input.getAttribute( "value" ) === ""; // Check if an input maintains its value after becoming a radio input.value = "t"; input.setAttribute( "type", "radio" ); support.radioValue = input.value === "t"; // #11217 - WebKit loses check when the name is after the checked attribute input.setAttribute( "checked", "t" ); input.setAttribute( "name", "t" ); fragment = document.createDocumentFragment(); fragment.appendChild( input ); // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) support.appendChecked = input.checked; // WebKit doesn't clone checked state correctly in fragments support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; // Support: IE<9 // Opera does not clone events (and typeof div.attachEvent === undefined). // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() if ( div.attachEvent ) { div.attachEvent( "onclick", function() { support.noCloneEvent = false; }); div.cloneNode( true ).click(); } // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php for ( i in { submit: true, change: true, focusin: true }) { div.setAttribute( eventName = "on" + i, "t" ); support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; } div.style.backgroundClip = "content-box"; div.cloneNode( true ).style.backgroundClip = ""; support.clearCloneStyle = div.style.backgroundClip === "content-box"; // Run tests that need a body at doc ready jQuery(function() { var container, marginDiv, tds, divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", body = document.getElementsByTagName("body")[0]; if ( !body ) { // Return for frameset docs that don't have a body return; } container = document.createElement("div"); container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; body.appendChild( container ).appendChild( div ); // Support: IE8 // Check if table cells still have offsetWidth/Height when they are set // to display:none and there are still other visible table cells in a // table row; if so, offsetWidth/Height are not reliable for use when // determining if an element has been hidden directly using // display:none (it is still safe to use offsets if a parent element is // hidden; don safety goggles and see bug #4512 for more information). div.innerHTML = "
    t
    "; tds = div.getElementsByTagName("td"); tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; isSupported = ( tds[ 0 ].offsetHeight === 0 ); tds[ 0 ].style.display = ""; tds[ 1 ].style.display = "none"; // Support: IE8 // Check if empty table cells still have offsetWidth/Height support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); // Check box-sizing and margin behavior div.innerHTML = ""; div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; support.boxSizing = ( div.offsetWidth === 4 ); support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 ); // Use window.getComputedStyle because jsdom on node.js will break without it. if ( window.getComputedStyle ) { support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; // Check if div with explicit width and no margin-right incorrectly // gets computed margin-right based on width of container. (#3333) // Fails in WebKit before Feb 2011 nightlies // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right marginDiv = div.appendChild( document.createElement("div") ); marginDiv.style.cssText = div.style.cssText = divReset; marginDiv.style.marginRight = marginDiv.style.width = "0"; div.style.width = "1px"; support.reliableMarginRight = !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); } if ( typeof div.style.zoom !== core_strundefined ) { // Support: IE<8 // Check if natively block-level elements act like inline-block // elements when setting their display to 'inline' and giving // them layout div.innerHTML = ""; div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); // Support: IE6 // Check if elements with layout shrink-wrap their children div.style.display = "block"; div.innerHTML = "
    "; div.firstChild.style.width = "5px"; support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); if ( support.inlineBlockNeedsLayout ) { // Prevent IE 6 from affecting layout for positioned elements #11048 // Prevent IE from shrinking the body in IE 7 mode #12869 // Support: IE<8 body.style.zoom = 1; } } body.removeChild( container ); // Null elements to avoid leaks in IE container = div = tds = marginDiv = null; }); // Null elements to avoid leaks in IE all = select = fragment = opt = a = input = null; return support; })(); var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, name, data, pvt /* Internal Use Only */ ){ if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) { return; } if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } } if ( !cache[ id ] ) { cache[ id ] = {}; // Avoids exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; } } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret; } function internalRemoveData( elem, name, pvt ) { if ( !jQuery.acceptData( elem ) ) { return; } var i, l, thisCache, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, id = isNode ? elem[ jQuery.expando ] : jQuery.expando; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split(" "); } } } else { // If "name" is an array of keys... // When data is initially created, via ("key", "val") signature, // keys will be converted to camelCase. // Since there is no way to tell _how_ a key was added, remove // both plain key and camelCase key. #12786 // This will only penalize the array argument path. name = name.concat( jQuery.map( name, jQuery.camelCase ) ); } for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject( cache[ id ] ) ) { return; } } // Destroy the cache if ( isNode ) { jQuery.cleanData( [ elem ], true ); // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) } else if ( jQuery.support.deleteExpando || cache != cache.window ) { delete cache[ id ]; // When all else fails, null } else { cache[ id ] = null; } } jQuery.extend({ cache: {}, // Unique for each copy of jQuery on the page // Non-digits removed to match rinlinejQuery expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), // The following elements throw uncatchable exceptions if you // attempt to add expando properties to them. noData: { "embed": true, // Ban all objects except for Flash (which handle expandos) "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", "applet": true }, hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); }, data: function( elem, name, data ) { return internalData( elem, name, data ); }, removeData: function( elem, name ) { return internalRemoveData( elem, name ); }, // For internal use only. _data: function( elem, name, data ) { return internalData( elem, name, data, true ); }, _removeData: function( elem, name ) { return internalRemoveData( elem, name, true ); }, // A method for determining if a DOM node can handle the data expando acceptData: function( elem ) { // Do not set data on non-element because it will not be cleared (#8335). if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { return false; } var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; // nodes accept data unless otherwise specified; rejection can be conditional return !noData || noData !== true && elem.getAttribute("classid") === noData; } }); jQuery.fn.extend({ data: function( key, value ) { var attrs, name, elem = this[0], i = 0, data = null; // Gets all values if ( key === undefined ) { if ( this.length ) { data = jQuery.data( elem ); if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { attrs = elem.attributes; for ( ; i < attrs.length; i++ ) { name = attrs[i].name; if ( !name.indexOf( "data-" ) ) { name = jQuery.camelCase( name.slice(5) ); dataAttr( elem, name, data[ name ] ); } } jQuery._data( elem, "parsedAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } return jQuery.access( this, function( value ) { if ( value === undefined ) { // Try to fetch any internally stored data first return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; } this.each(function() { jQuery.data( this, key, value ); }); }, null, value, arguments.length > 1, null, true ); }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : // Only convert to a number if it doesn't change the string +data + "" === data ? +data : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; } // checks a cache object for emptiness function isEmptyDataObject( obj ) { var name; for ( name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; } jQuery.extend({ queue: function( elem, type, data ) { var queue; if ( elem ) { type = ( type || "fx" ) + "queue"; queue = jQuery._data( elem, type ); // Speed up dequeue by getting out quickly if this is just a lookup if ( data ) { if ( !queue || jQuery.isArray(data) ) { queue = jQuery._data( elem, type, jQuery.makeArray(data) ); } else { queue.push( data ); } } return queue || []; } }, dequeue: function( elem, type ) { type = type || "fx"; var queue = jQuery.queue( elem, type ), startLength = queue.length, fn = queue.shift(), hooks = jQuery._queueHooks( elem, type ), next = function() { jQuery.dequeue( elem, type ); }; // If the fx queue is dequeued, always remove the progress sentinel if ( fn === "inprogress" ) { fn = queue.shift(); startLength--; } hooks.cur = fn; if ( fn ) { // Add a progress sentinel to prevent the fx queue from being // automatically dequeued if ( type === "fx" ) { queue.unshift( "inprogress" ); } // clear up the last queue stop function delete hooks.stop; fn.call( elem, next, hooks ); } if ( !startLength && hooks ) { hooks.empty.fire(); } }, // not intended for public consumption - generates a queueHooks object, or returns the current one _queueHooks: function( elem, type ) { var key = type + "queueHooks"; return jQuery._data( elem, key ) || jQuery._data( elem, key, { empty: jQuery.Callbacks("once memory").add(function() { jQuery._removeData( elem, type + "queue" ); jQuery._removeData( elem, key ); }) }); } }); jQuery.fn.extend({ queue: function( type, data ) { var setter = 2; if ( typeof type !== "string" ) { data = type; type = "fx"; setter--; } if ( arguments.length < setter ) { return jQuery.queue( this[0], type ); } return data === undefined ? this : this.each(function() { var queue = jQuery.queue( this, type, data ); // ensure a hooks for this queue jQuery._queueHooks( this, type ); if ( type === "fx" && queue[0] !== "inprogress" ) { jQuery.dequeue( this, type ); } }); }, dequeue: function( type ) { return this.each(function() { jQuery.dequeue( this, type ); }); }, // Based off of the plugin by Clint Helfers, with permission. // http://blindsignals.com/index.php/2009/07/jquery-delay/ delay: function( time, type ) { time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; type = type || "fx"; return this.queue( type, function( next, hooks ) { var timeout = setTimeout( next, time ); hooks.stop = function() { clearTimeout( timeout ); }; }); }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, // Get a promise resolved when queues of a certain type // are emptied (fx is the type by default) promise: function( type, obj ) { var tmp, count = 1, defer = jQuery.Deferred(), elements = this, i = this.length, resolve = function() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); } }; if ( typeof type !== "string" ) { obj = type; type = undefined; } type = type || "fx"; while( i-- ) { tmp = jQuery._data( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; tmp.empty.add( resolve ); } } resolve(); return defer.promise( obj ); } }); var nodeHook, boolHook, rclass = /[\t\r\n]/g, rreturn = /\r/g, rfocusable = /^(?:input|select|textarea|button|object)$/i, rclickable = /^(?:a|area)$/i, rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i, ruseDefault = /^(?:checked|selected)$/i, getSetAttribute = jQuery.support.getSetAttribute, getSetInput = jQuery.support.input; jQuery.fn.extend({ attr: function( name, value ) { return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); }, removeAttr: function( name ) { return this.each(function() { jQuery.removeAttr( this, name ); }); }, prop: function( name, value ) { return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); }, removeProp: function( name ) { name = jQuery.propFix[ name ] || name; return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { this[ name ] = undefined; delete this[ name ]; } catch( e ) {} }); }, addClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).addClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { // The disjunction here is for better compressibility (see removeClass) classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : " " ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { if ( cur.indexOf( " " + clazz + " " ) < 0 ) { cur += clazz + " "; } } elem.className = jQuery.trim( cur ); } } } return this; }, removeClass: function( value ) { var classes, elem, cur, clazz, j, i = 0, len = this.length, proceed = arguments.length === 0 || typeof value === "string" && value; if ( jQuery.isFunction( value ) ) { return this.each(function( j ) { jQuery( this ).removeClass( value.call( this, j, this.className ) ); }); } if ( proceed ) { classes = ( value || "" ).match( core_rnotwhite ) || []; for ( ; i < len; i++ ) { elem = this[ i ]; // This expression is here for better compressibility (see addClass) cur = elem.nodeType === 1 && ( elem.className ? ( " " + elem.className + " " ).replace( rclass, " " ) : "" ); if ( cur ) { j = 0; while ( (clazz = classes[j++]) ) { // Remove *all* instances while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { cur = cur.replace( " " + clazz + " ", " " ); } } elem.className = value ? jQuery.trim( cur ) : ""; } } } return this; }, toggleClass: function( value, stateVal ) { var type = typeof value, isBool = typeof stateVal === "boolean"; if ( jQuery.isFunction( value ) ) { return this.each(function( i ) { jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); }); } return this.each(function() { if ( type === "string" ) { // toggle individual class names var className, i = 0, self = jQuery( this ), state = stateVal, classNames = value.match( core_rnotwhite ) || []; while ( (className = classNames[ i++ ]) ) { // check each className given, space separated list state = isBool ? state : !self.hasClass( className ); self[ state ? "addClass" : "removeClass" ]( className ); } // Toggle whole class name } else if ( type === core_strundefined || type === "boolean" ) { if ( this.className ) { // store className if set jQuery._data( this, "__className__", this.className ); } // If the element has a class name or if we're passed "false", // then remove the whole classname (if there was one, the above saved it). // Otherwise bring back whatever was previously saved (if anything), // falling back to the empty string if nothing was stored. this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; } }); }, hasClass: function( selector ) { var className = " " + selector + " ", i = 0, l = this.length; for ( ; i < l; i++ ) { if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { return true; } } return false; }, val: function( value ) { var ret, hooks, isFunction, elem = this[0]; if ( !arguments.length ) { if ( elem ) { hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { return ret; } ret = elem.value; return typeof ret === "string" ? // handle most common string cases ret.replace(rreturn, "") : // handle cases where value is null/undef or number ret == null ? "" : ret; } return; } isFunction = jQuery.isFunction( value ); return this.each(function( i ) { var val, self = jQuery(this); if ( this.nodeType !== 1 ) { return; } if ( isFunction ) { val = value.call( this, i, self.val() ); } else { val = value; } // Treat null/undefined as ""; convert numbers to string if ( val == null ) { val = ""; } else if ( typeof val === "number" ) { val += ""; } else if ( jQuery.isArray( val ) ) { val = jQuery.map(val, function ( value ) { return value == null ? "" : value + ""; }); } hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; // If set returns undefined, fall back to normal setting if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); } }); jQuery.extend({ valHooks: { option: { get: function( elem ) { // attributes.value is undefined in Blackberry 4.7 but // uses .value. See #6932 var val = elem.attributes.value; return !val || val.specified ? elem.value : elem.text; } }, select: { get: function( elem ) { var value, option, options = elem.options, index = elem.selectedIndex, one = elem.type === "select-one" || index < 0, values = one ? null : [], max = one ? index + 1 : options.length, i = index < 0 ? max : one ? index : 0; // Loop through all the selected options for ( ; i < max; i++ ) { option = options[ i ]; // oldIE doesn't update selected after form reset (#2551) if ( ( option.selected || i === index ) && // Don't return options that are disabled or in a disabled optgroup ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { // Get the specific value for the option value = jQuery( option ).val(); // We don't need an array for one selects if ( one ) { return value; } // Multi-Selects return an array values.push( value ); } } return values; }, set: function( elem, value ) { var values = jQuery.makeArray( value ); jQuery(elem).find("option").each(function() { this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; }); if ( !values.length ) { elem.selectedIndex = -1; } return values; } } }, attr: function( elem, name, value ) { var hooks, notxml, ret, nType = elem.nodeType; // don't get/set attributes on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } // Fallback to prop when attributes are not supported if ( typeof elem.getAttribute === core_strundefined ) { return jQuery.prop( elem, name, value ); } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); // All attributes are lowercase // Grab necessary hook if one is defined if ( notxml ) { name = name.toLowerCase(); hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); } if ( value !== undefined ) { if ( value === null ) { jQuery.removeAttr( elem, name ); } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { elem.setAttribute( name, value + "" ); return value; } } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { // In IE9+, Flash objects don't have .getAttribute (#12945) // Support: IE9+ if ( typeof elem.getAttribute !== core_strundefined ) { ret = elem.getAttribute( name ); } // Non-existent attributes return null, we normalize to undefined return ret == null ? undefined : ret; } }, removeAttr: function( elem, value ) { var name, propName, i = 0, attrNames = value && value.match( core_rnotwhite ); if ( attrNames && elem.nodeType === 1 ) { while ( (name = attrNames[i++]) ) { propName = jQuery.propFix[ name ] || name; // Boolean attributes get special treatment (#10870) if ( rboolean.test( name ) ) { // Set corresponding property to false for boolean attributes // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8 if ( !getSetAttribute && ruseDefault.test( name ) ) { elem[ jQuery.camelCase( "default-" + name ) ] = elem[ propName ] = false; } else { elem[ propName ] = false; } // See #9699 for explanation of this approach (setting first, then removal) } else { jQuery.attr( elem, name, "" ); } elem.removeAttribute( getSetAttribute ? name : propName ); } } }, attrHooks: { type: { set: function( elem, value ) { if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to default in case type is set after value during creation var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; } return value; } } } }, propFix: { tabindex: "tabIndex", readonly: "readOnly", "for": "htmlFor", "class": "className", maxlength: "maxLength", cellspacing: "cellSpacing", cellpadding: "cellPadding", rowspan: "rowSpan", colspan: "colSpan", usemap: "useMap", frameborder: "frameBorder", contenteditable: "contentEditable" }, prop: function( elem, name, value ) { var ret, hooks, notxml, nType = elem.nodeType; // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return; } notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); if ( notxml ) { // Fix name and attach hooks name = jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; } if ( value !== undefined ) { if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; } else { return ( elem[ name ] = value ); } } else { if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { return ret; } else { return elem[ name ]; } } }, propHooks: { tabIndex: { get: function( elem ) { // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ var attributeNode = elem.getAttributeNode("tabindex"); return attributeNode && attributeNode.specified ? parseInt( attributeNode.value, 10 ) : rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? 0 : undefined; } } } }); // Hook for boolean attributes boolHook = { get: function( elem, name ) { var // Use .prop to determine if this attribute is understood as boolean prop = jQuery.prop( elem, name ), // Fetch it accordingly attr = typeof prop === "boolean" && elem.getAttribute( name ), detail = typeof prop === "boolean" ? getSetInput && getSetAttribute ? attr != null : // oldIE fabricates an empty string for missing boolean attributes // and conflates checked/selected into attroperties ruseDefault.test( name ) ? elem[ jQuery.camelCase( "default-" + name ) ] : !!attr : // fetch an attribute node for properties not recognized as boolean elem.getAttributeNode( name ); return detail && detail.value !== false ? name.toLowerCase() : undefined; }, set: function( elem, value, name ) { if ( value === false ) { // Remove boolean attributes when set to false jQuery.removeAttr( elem, name ); } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { // IE<8 needs the *property* name elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); // Use defaultChecked and defaultSelected for oldIE } else { elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; } return name; } }; // fix oldIE value attroperty if ( !getSetInput || !getSetAttribute ) { jQuery.attrHooks.value = { get: function( elem, name ) { var ret = elem.getAttributeNode( name ); return jQuery.nodeName( elem, "input" ) ? // Ignore the value *property* by using defaultValue elem.defaultValue : ret && ret.specified ? ret.value : undefined; }, set: function( elem, value, name ) { if ( jQuery.nodeName( elem, "input" ) ) { // Does not return so that setAttribute is also used elem.defaultValue = value; } else { // Use nodeHook if defined (#1954); otherwise setAttribute is fine return nodeHook && nodeHook.set( elem, value, name ); } } }; } // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !getSetAttribute ) { // Use this for any attribute in IE6/7 // This fixes almost every IE6/7 issue nodeHook = jQuery.valHooks.button = { get: function( elem, name ) { var ret = elem.getAttributeNode( name ); return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ? ret.value : undefined; }, set: function( elem, value, name ) { // Set the existing or create a new attribute node var ret = elem.getAttributeNode( name ); if ( !ret ) { elem.setAttributeNode( (ret = elem.ownerDocument.createAttribute( name )) ); } ret.value = value += ""; // Break association with cloned elements by also using setAttribute (#9646) return name === "value" || value === elem.getAttribute( name ) ? value : undefined; } }; // Set contenteditable to false on removals(#10429) // Setting to empty string throws an error as an invalid value jQuery.attrHooks.contenteditable = { get: nodeHook.get, set: function( elem, value, name ) { nodeHook.set( elem, value === "" ? false : value, name ); } }; // Set width and height to auto instead of 0 on empty string( Bug #8150 ) // This is for removals jQuery.each([ "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { set: function( elem, value ) { if ( value === "" ) { elem.setAttribute( name, "auto" ); return value; } } }); }); } // Some attributes require a special call on IE // http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx if ( !jQuery.support.hrefNormalized ) { jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { get: function( elem ) { var ret = elem.getAttribute( name, 2 ); return ret == null ? undefined : ret; } }); }); // href/src property should get the full normalized URL (#10299/#12915) jQuery.each([ "href", "src" ], function( i, name ) { jQuery.propHooks[ name ] = { get: function( elem ) { return elem.getAttribute( name, 4 ); } }; }); } if ( !jQuery.support.style ) { jQuery.attrHooks.style = { get: function( elem ) { // Return undefined in the case of empty string // Note: IE uppercases css property names, but if we were to .toLowerCase() // .cssText, that would destroy case senstitivity in URL's, like in "background" return elem.style.cssText || undefined; }, set: function( elem, value ) { return ( elem.style.cssText = value + "" ); } }; } // Safari mis-reports the default selected property of an option // Accessing the parent's selectedIndex property fixes it if ( !jQuery.support.optSelected ) { jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { get: function( elem ) { var parent = elem.parentNode; if ( parent ) { parent.selectedIndex; // Make sure that it also works with optgroups, see #5701 if ( parent.parentNode ) { parent.parentNode.selectedIndex; } } return null; } }); } // IE6/7 call enctype encoding if ( !jQuery.support.enctype ) { jQuery.propFix.enctype = "encoding"; } // Radios and checkboxes getter/setter if ( !jQuery.support.checkOn ) { jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = { get: function( elem ) { // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified return elem.getAttribute("value") === null ? "on" : elem.value; } }; }); } jQuery.each([ "radio", "checkbox" ], function() { jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { set: function( elem, value ) { if ( jQuery.isArray( value ) ) { return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); } } }); }); var rformElems = /^(?:input|select|textarea)$/i, rkeyEvent = /^key/, rmouseEvent = /^(?:mouse|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; function returnTrue() { return true; } function returnFalse() { return false; } /* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */ jQuery.event = { global: {}, add: function( elem, types, handler, data, selector ) { var tmp, events, t, handleObjIn, special, eventHandle, handleObj, handlers, type, namespaces, origType, elemData = jQuery._data( elem ); // Don't attach events to noData or text/comment nodes (but allow plain objects) if ( !elemData ) { return; } // Caller can pass in an object of custom data in lieu of the handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } // Make sure that the handler has a unique ID, used to find/remove it later if ( !handler.guid ) { handler.guid = jQuery.guid++; } // Init the element's event structure and main handler, if this is the first if ( !(events = elemData.events) ) { events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events eventHandle.elem = elem; } // Handle multiple events separated by a space // jQuery(...).bind("mouseover mouseout", fn); types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // If event changes its type, use the special event handlers for the changed type special = jQuery.event.special[ type ] || {}; // If selector defined, determine special event api type, otherwise given type type = ( selector ? special.delegateType : special.bindType ) || type; // Update special based on newly reset type special = jQuery.event.special[ type ] || {}; // handleObj is passed to all event handlers handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); // Init the event handler queue if we're the first if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { // Bind the global event handler to the element if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } } } if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } // Add to the element's handler list, delegates in front if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } // Keep track of which events have ever been used, for event optimization jQuery.event.global[ type ] = true; } // Nullify elem to prevent memory leaks in IE elem = null; }, // Detach an event or set of events from an element remove: function( elem, types, handler, selector, mappedTypes ) { var j, handleObj, tmp, origCount, t, events, special, handlers, type, namespaces, origType, elemData = jQuery.hasData( elem ) && jQuery._data( elem ); if ( !elemData || !(events = elemData.events) ) { return; } // Once for each type.namespace in types; type may be omitted types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); // Unbind all events (on this namespace, if provided) for the element if ( !type ) { for ( type in events ) { jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); } continue; } special = jQuery.event.special[ type ] || {}; type = ( selector ? special.delegateType : special.bindType ) || type; handlers = events[ type ] || []; tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); // Remove matching events origCount = j = handlers.length; while ( j-- ) { handleObj = handlers[ j ]; if ( ( mappedTypes || origType === handleObj.origType ) && ( !handler || handler.guid === handleObj.guid ) && ( !tmp || tmp.test( handleObj.namespace ) ) && ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { handlers.splice( j, 1 ); if ( handleObj.selector ) { handlers.delegateCount--; } if ( special.remove ) { special.remove.call( elem, handleObj ); } } } // Remove generic event handler if we removed something and no more handlers exist // (avoids potential for endless recursion during removal of special event handlers) if ( origCount && !handlers.length ) { if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { jQuery.removeEvent( elem, type, elemData.handle ); } delete events[ type ]; } } // Remove the expando if it's no longer used if ( jQuery.isEmptyObject( events ) ) { delete elemData.handle; // removeData also checks for emptiness and clears the expando if empty // so use it instead of delete jQuery._removeData( elem, "events" ); } }, trigger: function( event, data, elem, onlyHandlers ) { var handle, ontype, cur, bubbleType, special, tmp, i, eventPath = [ elem || document ], type = core_hasOwn.call( event, "type" ) ? event.type : event, namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; // Don't do events on text and comment nodes if ( elem.nodeType === 3 || elem.nodeType === 8 ) { return; } // focus/blur morphs to focusin/out; ensure we're not firing them right now if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { return; } if ( type.indexOf(".") >= 0 ) { // Namespaced trigger; create a regexp to match event type in handle() namespaces = type.split("."); type = namespaces.shift(); namespaces.sort(); } ontype = type.indexOf(":") < 0 && "on" + type; // Caller can pass in a jQuery.Event object, Object, or just an event type string event = event[ jQuery.expando ] ? event : new jQuery.Event( type, typeof event === "object" && event ); event.isTrigger = true; event.namespace = namespaces.join("."); event.namespace_re = event.namespace ? new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : null; // Clean up the event in case it is being reused event.result = undefined; if ( !event.target ) { event.target = elem; } // Clone any incoming data and prepend the event, creating the handler arg list data = data == null ? [ event ] : jQuery.makeArray( data, [ event ] ); // Allow special events to draw outside the lines special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; } // Determine event propagation path in advance, per W3C events spec (#9951) // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { bubbleType = special.delegateType || type; if ( !rfocusMorph.test( bubbleType + type ) ) { cur = cur.parentNode; } for ( ; cur; cur = cur.parentNode ) { eventPath.push( cur ); tmp = cur; } // Only add window if we got to document (e.g., not plain obj or detached DOM) if ( tmp === (elem.ownerDocument || document) ) { eventPath.push( tmp.defaultView || tmp.parentWindow || window ); } } // Fire handlers on the event path i = 0; while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { event.type = i > 1 ? bubbleType : special.bindType || type; // jQuery handler handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); if ( handle ) { handle.apply( cur, data ); } // Native handler handle = ontype && cur[ ontype ]; if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { event.preventDefault(); } } event.type = type; // If nobody prevented the default action, do it now if ( !onlyHandlers && !event.isDefaultPrevented() ) { if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { // Call a native DOM method on the target with the same name name as the event. // Can't use an .isFunction() check here because IE6/7 fails that test. // Don't do default actions on window, that's where global variables be (#6170) if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { // Don't re-trigger an onFOO event when we call its FOO() method tmp = elem[ ontype ]; if ( tmp ) { elem[ ontype ] = null; } // Prevent re-triggering of the same event, since we already bubbled it above jQuery.event.triggered = type; try { elem[ type ](); } catch ( e ) { // IE<9 dies on focus/blur to hidden element (#1486,#12518) // only reproducible on winXP IE8 native, not IE9 in IE8 mode } jQuery.event.triggered = undefined; if ( tmp ) { elem[ ontype ] = tmp; } } } } return event.result; }, dispatch: function( event ) { // Make a writable jQuery.Event from the native event object event = jQuery.event.fix( event ); var i, ret, handleObj, matched, j, handlerQueue = [], args = core_slice.call( arguments ), handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; // Use the fix-ed jQuery.Event rather than the (read-only) native event args[0] = event; event.delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { return; } // Determine handlers handlerQueue = jQuery.event.handlers.call( this, event, handlers ); // Run delegates first; they may want to stop propagation beneath us i = 0; while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { event.currentTarget = matched.elem; j = 0; while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { // Triggered event must either 1) have no namespace, or // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { event.handleObj = handleObj; event.data = handleObj.data; ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args ); if ( ret !== undefined ) { if ( (event.result = ret) === false ) { event.preventDefault(); event.stopPropagation(); } } } } } // Call the postDispatch hook for the mapped type if ( special.postDispatch ) { special.postDispatch.call( this, event ); } return event.result; }, handlers: function( event, handlers ) { var sel, handleObj, matches, i, handlerQueue = [], delegateCount = handlers.delegateCount, cur = event.target; // Find delegate handlers // Black-hole SVG instance trees (#13180) // Avoid non-left-click bubbling in Firefox (#3861) if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { for ( ; cur != this; cur = cur.parentNode || this ) { // Don't check non-elements (#13208) // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { matches = []; for ( i = 0; i < delegateCount; i++ ) { handleObj = handlers[ i ]; // Don't conflict with Object.prototype properties (#13203) sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) { matches[ sel ] = handleObj.needsContext ? jQuery( sel, this ).index( cur ) >= 0 : jQuery.find( sel, this, null, [ cur ] ).length; } if ( matches[ sel ] ) { matches.push( handleObj ); } } if ( matches.length ) { handlerQueue.push({ elem: cur, handlers: matches }); } } } } // Add the remaining (directly-bound) handlers if ( delegateCount < handlers.length ) { handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); } return handlerQueue; }, fix: function( event ) { if ( event[ jQuery.expando ] ) { return event; } // Create a writable copy of the event object and normalize some properties var i, prop, copy, type = event.type, originalEvent = event, fixHook = this.fixHooks[ type ]; if ( !fixHook ) { this.fixHooks[ type ] = fixHook = rmouseEvent.test( type ) ? this.mouseHooks : rkeyEvent.test( type ) ? this.keyHooks : {}; } copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; event = new jQuery.Event( originalEvent ); i = copy.length; while ( i-- ) { prop = copy[ i ]; event[ prop ] = originalEvent[ prop ]; } // Support: IE<9 // Fix target property (#1925) if ( !event.target ) { event.target = originalEvent.srcElement || document; } // Support: Chrome 23+, Safari? // Target should not be a text node (#504, #13143) if ( event.target.nodeType === 3 ) { event.target = event.target.parentNode; } // Support: IE<9 // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) event.metaKey = !!event.metaKey; return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, // Includes some event props shared by KeyEvent and MouseEvent props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), fixHooks: {}, keyHooks: { props: "char charCode key keyCode".split(" "), filter: function( event, original ) { // Add which for key events if ( event.which == null ) { event.which = original.charCode != null ? original.charCode : original.keyCode; } return event; } }, mouseHooks: { props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), filter: function( event, original ) { var body, eventDoc, doc, button = original.button, fromElement = original.fromElement; // Calculate pageX/Y if missing and clientX/Y available if ( event.pageX == null && original.clientX != null ) { eventDoc = event.target.ownerDocument || document; doc = eventDoc.documentElement; body = eventDoc.body; event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); } // Add relatedTarget, if necessary if ( !event.relatedTarget && fromElement ) { event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; } // Add which for click: 1 === left; 2 === middle; 3 === right // Note: button is not normalized, so don't use it if ( !event.which && button !== undefined ) { event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); } return event; } }, special: { load: { // Prevent triggered image.load events from bubbling to window.load noBubble: true }, click: { // For checkbox, fire native event so checked state will be right trigger: function() { if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { this.click(); return false; } } }, focus: { // Fire native event if possible so blur/focus sequence is correct trigger: function() { if ( this !== document.activeElement && this.focus ) { try { this.focus(); return false; } catch ( e ) { // Support: IE<9 // If we error on focus to hidden element (#1486, #12518), // let .trigger() run the handlers } } }, delegateType: "focusin" }, blur: { trigger: function() { if ( this === document.activeElement && this.blur ) { this.blur(); return false; } }, delegateType: "focusout" }, beforeunload: { postDispatch: function( event ) { // Even when returnValue equals to undefined Firefox will still show alert if ( event.result !== undefined ) { event.originalEvent.returnValue = event.result; } } } }, simulate: function( type, elem, event, bubble ) { // Piggyback on a donor event to simulate a different one. // Fake originalEvent to avoid donor's stopPropagation, but if the // simulated event prevents default then we do the same on the donor. var e = jQuery.extend( new jQuery.Event(), event, { type: type, isSimulated: true, originalEvent: {} } ); if ( bubble ) { jQuery.event.trigger( e, null, elem ); } else { jQuery.event.dispatch.call( elem, e ); } if ( e.isDefaultPrevented() ) { event.preventDefault(); } } }; jQuery.removeEvent = document.removeEventListener ? function( elem, type, handle ) { if ( elem.removeEventListener ) { elem.removeEventListener( type, handle, false ); } } : function( elem, type, handle ) { var name = "on" + type; if ( elem.detachEvent ) { // #8545, #7054, preventing memory leaks for custom events in IE6-8 // detachEvent needed property on element, by name of that event, to properly expose it to GC if ( typeof elem[ name ] === core_strundefined ) { elem[ name ] = null; } elem.detachEvent( name, handle ); } }; jQuery.Event = function( src, props ) { // Allow instantiation without the 'new' keyword if ( !(this instanceof jQuery.Event) ) { return new jQuery.Event( src, props ); } // Event object if ( src && src.type ) { this.originalEvent = src; this.type = src.type; // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; // Event type } else { this.type = src; } // Put explicitly provided properties onto the event object if ( props ) { jQuery.extend( this, props ); } // Create a timestamp if incoming event doesn't have one this.timeStamp = src && src.timeStamp || jQuery.now(); // Mark it as fixed this[ jQuery.expando ] = true; }; // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html jQuery.Event.prototype = { isDefaultPrevented: returnFalse, isPropagationStopped: returnFalse, isImmediatePropagationStopped: returnFalse, preventDefault: function() { var e = this.originalEvent; this.isDefaultPrevented = returnTrue; if ( !e ) { return; } // If preventDefault exists, run it on the original event if ( e.preventDefault ) { e.preventDefault(); // Support: IE // Otherwise set the returnValue property of the original event to false } else { e.returnValue = false; } }, stopPropagation: function() { var e = this.originalEvent; this.isPropagationStopped = returnTrue; if ( !e ) { return; } // If stopPropagation exists, run it on the original event if ( e.stopPropagation ) { e.stopPropagation(); } // Support: IE // Set the cancelBubble property of the original event to true e.cancelBubble = true; }, stopImmediatePropagation: function() { this.isImmediatePropagationStopped = returnTrue; this.stopPropagation(); } }; // Create mouseenter/leave events using mouseover/out and event-time checks jQuery.each({ mouseenter: "mouseover", mouseleave: "mouseout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, bindType: fix, handle: function( event ) { var ret, target = this, related = event.relatedTarget, handleObj = event.handleObj; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !jQuery.contains( target, related )) ) { event.type = handleObj.origType; ret = handleObj.handler.apply( this, arguments ); event.type = fix; } return ret; } }; }); // IE submit delegation if ( !jQuery.support.submitBubbles ) { jQuery.event.special.submit = { setup: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Lazy-add a submit handler when a descendant form may potentially be submitted jQuery.event.add( this, "click._submit keypress._submit", function( e ) { // Node name check avoids a VML-related crash in IE (#9807) var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !jQuery._data( form, "submitBubbles" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); jQuery._data( form, "submitBubbles", true ); } }); // return undefined since we don't need an event listener }, postDispatch: function( event ) { // If form was submitted by the user, bubble the event up the tree if ( event._submit_bubble ) { delete event._submit_bubble; if ( this.parentNode && !event.isTrigger ) { jQuery.event.simulate( "submit", this.parentNode, event, true ); } } }, teardown: function() { // Only need this for delegated form submit events if ( jQuery.nodeName( this, "form" ) ) { return false; } // Remove delegated handlers; cleanData eventually reaps submit handlers attached above jQuery.event.remove( this, "._submit" ); } }; } // IE change delegation and checkbox/radio fix if ( !jQuery.support.changeBubbles ) { jQuery.event.special.change = { setup: function() { if ( rformElems.test( this.nodeName ) ) { // IE doesn't fire change on a check/radio until blur; trigger it on click // after a propertychange. Eat the blur-change in special.change.handle. // This still fires onchange a second time for check/radio after blur. if ( this.type === "checkbox" || this.type === "radio" ) { jQuery.event.add( this, "propertychange._change", function( event ) { if ( event.originalEvent.propertyName === "checked" ) { this._just_changed = true; } }); jQuery.event.add( this, "click._change", function( event ) { if ( this._just_changed && !event.isTrigger ) { this._just_changed = false; } // Allow triggered, simulated change events (#11500) jQuery.event.simulate( "change", this, event, true ); }); } return false; } // Delegated event; lazy-add a change handler on descendant inputs jQuery.event.add( this, "beforeactivate._change", function( e ) { var elem = e.target; if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { jQuery.event.add( elem, "change._change", function( event ) { if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { jQuery.event.simulate( "change", this.parentNode, event, true ); } }); jQuery._data( elem, "changeBubbles", true ); } }); }, handle: function( event ) { var elem = event.target; // Swallow native change events from checkbox/radio, we already triggered them above if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { return event.handleObj.handler.apply( this, arguments ); } }, teardown: function() { jQuery.event.remove( this, "._change" ); return !rformElems.test( this.nodeName ); } }; } // Create "bubbling" focus and blur events if ( !jQuery.support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { // Attach a single capturing handler while someone wants focusin/focusout var attaches = 0, handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { if ( attaches++ === 0 ) { document.addEventListener( orig, handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { document.removeEventListener( orig, handler, true ); } } }; }); } jQuery.fn.extend({ on: function( types, selector, data, fn, /*INTERNAL*/ one ) { var type, origFn; // Types can be a map of types/handlers if ( typeof types === "object" ) { // ( types-Object, selector, data ) if ( typeof selector !== "string" ) { // ( types-Object, data ) data = data || selector; selector = undefined; } for ( type in types ) { this.on( type, selector, data, types[ type ], one ); } return this; } if ( data == null && fn == null ) { // ( types, fn ) fn = selector; data = selector = undefined; } else if ( fn == null ) { if ( typeof selector === "string" ) { // ( types, selector, fn ) fn = data; data = undefined; } else { // ( types, data, fn ) fn = data; data = selector; selector = undefined; } } if ( fn === false ) { fn = returnFalse; } else if ( !fn ) { return this; } if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); } return this.each( function() { jQuery.event.add( this, types, fn, data, selector ); }); }, one: function( types, selector, data, fn ) { return this.on( types, selector, data, fn, 1 ); }, off: function( types, selector, fn ) { var handleObj, type; if ( types && types.preventDefault && types.handleObj ) { // ( event ) dispatched jQuery.Event handleObj = types.handleObj; jQuery( types.delegateTarget ).off( handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, handleObj.selector, handleObj.handler ); return this; } if ( typeof types === "object" ) { // ( types-object [, selector] ) for ( type in types ) { this.off( type, selector, types[ type ] ); } return this; } if ( selector === false || typeof selector === "function" ) { // ( types [, fn] ) fn = selector; selector = undefined; } if ( fn === false ) { fn = returnFalse; } return this.each(function() { jQuery.event.remove( this, types, fn, selector ); }); }, bind: function( types, data, fn ) { return this.on( types, null, data, fn ); }, unbind: function( types, fn ) { return this.off( types, null, fn ); }, delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }, undelegate: function( selector, types, fn ) { // ( namespace ) or ( selector, types [, fn] ) return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn ); }, trigger: function( type, data ) { return this.each(function() { jQuery.event.trigger( type, data, this ); }); }, triggerHandler: function( type, data ) { var elem = this[0]; if ( elem ) { return jQuery.event.trigger( type, data, elem, true ); } } }); /*! * Sizzle CSS Selector Engine * Copyright 2012 jQuery Foundation and other contributors * Released under the MIT license * http://sizzlejs.com/ */ (function( window, undefined ) { var i, cachedruns, Expr, getText, isXML, compile, hasDuplicate, outermostContext, // Local document vars setDocument, document, docElem, documentIsXML, rbuggyQSA, rbuggyMatches, matches, contains, sortOrder, // Instance-specific data expando = "sizzle" + -(new Date()), preferredDoc = window.document, support = {}, dirruns = 0, done = 0, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), // General-purpose constants strundefined = typeof undefined, MAX_NEGATIVE = 1 << 31, // Array methods arr = [], pop = arr.pop, push = arr.push, slice = arr.slice, // Use a stripped-down indexOf if we can't use a native one indexOf = arr.indexOf || function( elem ) { var i = 0, len = this.length; for ( ; i < len; i++ ) { if ( this[i] === elem ) { return i; } } return -1; }, // Regular expressions // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace whitespace = "[\\x20\\t\\r\\n\\f]", // http://www.w3.org/TR/css3-syntax/#characters characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", // Loosely modeled on CSS identifier characters // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = characterEncoding.replace( "w", "w#" ), // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors operators = "([*^$|!~]?=)", attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", // Prefer arguments quoted, // then not containing pseudos/brackets, // then attribute selectors/non-parenthetical expressions, // then anything else // These preferences are here to reduce the number of selectors // needing tokenize in the PSEUDO preFilter pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { "ID": new RegExp( "^#(" + characterEncoding + ")" ), "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), "ATTR": new RegExp( "^" + attributes ), "PSEUDO": new RegExp( "^" + pseudos ), "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, rsibling = /[\x20\t\r\n\f]*[+~]/, rnative = /^[^{]+\{\s*\[native code/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, rescape = /'|\\/g, rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g, funescape = function( _, escaped ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint return high !== high ? escaped : // BMP codepoint high < 0 ? String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }; // Use a stripped-down slice if we can't use a native one try { slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType; } catch ( e ) { slice = function( i ) { var elem, results = []; while ( (elem = this[i++]) ) { results.push( elem ); } return results; }; } /** * For feature detection * @param {Function} fn The function to test for native support */ function isNative( fn ) { return rnative.test( fn + "" ); } /** * Create key-value caches of limited size * @returns {Function(string, Object)} Returns the Object data after storing it on itself with * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) * deleting the oldest entry */ function createCache() { var cache, keys = []; return (cache = function( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) if ( keys.push( key += " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } return (cache[ key ] = value); }); } /** * Mark a function for special use by Sizzle * @param {Function} fn The function to mark */ function markFunction( fn ) { fn[ expando ] = true; return fn; } /** * Support testing using an element * @param {Function} fn Passed the created div and expects a boolean result */ function assert( fn ) { var div = document.createElement("div"); try { return fn( div ); } catch (e) { return false; } finally { // release memory in IE div = null; } } function Sizzle( selector, context, results, seed ) { var match, elem, m, nodeType, // QSA vars i, groups, old, nid, newContext, newSelector; if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { setDocument( context ); } context = context || document; results = results || []; if ( !selector || typeof selector !== "string" ) { return results; } if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { return []; } if ( !documentIsXML && !seed ) { // Shortcuts if ( (match = rquickExpr.exec( selector )) ) { // Speed-up: Sizzle("#ID") if ( (m = match[1]) ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 if ( elem && elem.parentNode ) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID if ( elem.id === m ) { results.push( elem ); return results; } } else { return results; } } else { // Context is not a document if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && contains( context, elem ) && elem.id === m ) { results.push( elem ); return results; } } // Speed-up: Sizzle("TAG") } else if ( match[2] ) { push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); return results; // Speed-up: Sizzle(".CLASS") } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) { push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); return results; } } // QSA path if ( support.qsa && !rbuggyQSA.test(selector) ) { old = true; nid = expando; newContext = context; newSelector = nodeType === 9 && selector; // qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector ); if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] "; i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && context.parentNode || context; newSelector = groups.join(","); } if ( newSelector ) { try { push.apply( results, slice.call( newContext.querySelectorAll( newSelector ), 0 ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } } } } // All others return select( selector.replace( rtrim, "$1" ), context, results, seed ); } /** * Detect xml * @param {Element|Object} elem An element or a document */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = elem && (elem.ownerDocument || elem).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { var doc = node ? node.ownerDocument || node : preferredDoc; // If no document and documentElement is available, return if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { return document; } // Set our document document = doc; docElem = doc.documentElement; // Support tests documentIsXML = isXML( doc ); // Check if getElementsByTagName("*") returns only elements support.tagNameNoComments = assert(function( div ) { div.appendChild( doc.createComment("") ); return !div.getElementsByTagName("*").length; }); // Check if attributes should be retrieved by attribute nodes support.attributes = assert(function( div ) { div.innerHTML = ""; var type = typeof div.lastChild.getAttribute("multiple"); // IE8 returns a string for some attributes even when not present return type !== "boolean" && type !== "string"; }); // Check if getElementsByClassName can be trusted support.getByClassName = assert(function( div ) { // Opera can't find a second classname (in 9.6) div.innerHTML = ""; if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { return false; } // Safari 3.2 caches class attributes and doesn't catch changes div.lastChild.className = "e"; return div.getElementsByClassName("e").length === 2; }); // Check if getElementById returns elements by name // Check if getElementsByName privileges form controls or returns elements by ID support.getByName = assert(function( div ) { // Inject content div.id = expando + 0; div.innerHTML = "
    "; docElem.insertBefore( div, docElem.firstChild ); // Test var pass = doc.getElementsByName && // buggy browsers will return fewer than the correct 2 doc.getElementsByName( expando ).length === 2 + // buggy browsers will return more than the correct 0 doc.getElementsByName( expando + 0 ).length; support.getIdNotName = !doc.getElementById( expando ); // Cleanup docElem.removeChild( div ); return pass; }); // IE6/7 return modified attributes Expr.attrHandle = assert(function( div ) { div.innerHTML = ""; return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && div.firstChild.getAttribute("href") === "#"; }) ? {} : { "href": function( elem ) { return elem.getAttribute( "href", 2 ); }, "type": function( elem ) { return elem.getAttribute("type"); } }; // ID find and filter if ( support.getIdNotName ) { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== strundefined && !documentIsXML ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 return m && m.parentNode ? [m] : []; } }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute("id") === attrId; }; }; } else { Expr.find["ID"] = function( id, context ) { if ( typeof context.getElementById !== strundefined && !documentIsXML ) { var m = context.getElementById( id ); return m ? m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? [m] : undefined : []; } }; Expr.filter["ID"] = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); return node && node.value === attrId; }; }; } // Tag Expr.find["TAG"] = support.tagNameNoComments ? function( tag, context ) { if ( typeof context.getElementsByTagName !== strundefined ) { return context.getElementsByTagName( tag ); } } : function( tag, context ) { var elem, tmp = [], i = 0, results = context.getElementsByTagName( tag ); // Filter out possible comments if ( tag === "*" ) { while ( (elem = results[i++]) ) { if ( elem.nodeType === 1 ) { tmp.push( elem ); } } return tmp; } return results; }; // Name Expr.find["NAME"] = support.getByName && function( tag, context ) { if ( typeof context.getElementsByName !== strundefined ) { return context.getElementsByName( name ); } }; // Class Expr.find["CLASS"] = support.getByClassName && function( className, context ) { if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) { return context.getElementsByClassName( className ); } }; // QSA and matchesSelector support // matchesSelector(:active) reports false when true (IE9/Opera 11.5) rbuggyMatches = []; // qSa(:focus) reports false when true (Chrome 21), // no need to also add to buggyMatches since matches checks buggyQSA // A support test would require too much code (would include document ready) rbuggyQSA = [ ":focus" ]; if ( (support.qsa = isNative(doc.querySelectorAll)) ) { // Build QSA regex // Regex strategy adopted from Diego Perini assert(function( div ) { // Select is set to empty string on purpose // This is to test IE's treatment of not explictly // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 div.innerHTML = ""; // IE8 - Some boolean attributes are not treated correctly if ( !div.querySelectorAll("[selected]").length ) { rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); } // Webkit/Opera - :checked should return selected option elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":checked").length ) { rbuggyQSA.push(":checked"); } }); assert(function( div ) { // Opera 10-12/IE8 - ^= $= *= and empty values // Should not select anything div.innerHTML = ""; if ( div.querySelectorAll("[i^='']").length ) { rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) // IE8 throws error here and will not see later tests if ( !div.querySelectorAll(":enabled").length ) { rbuggyQSA.push( ":enabled", ":disabled" ); } // Opera 10-11 does not throw on post-comma invalid pseudos div.querySelectorAll("*,:x"); rbuggyQSA.push(",.*:"); }); } if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector || docElem.mozMatchesSelector || docElem.webkitMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { assert(function( div ) { // Check to see if it's possible to do matchesSelector // on a disconnected node (IE 9) support.disconnectedMatch = matches.call( div, "div" ); // This should fail with an exception // Gecko does not error, returns false instead matches.call( div, "[s!='']:x" ); rbuggyMatches.push( "!=", pseudos ); }); } rbuggyQSA = new RegExp( rbuggyQSA.join("|") ); rbuggyMatches = new RegExp( rbuggyMatches.join("|") ); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; // Document order sorting sortOrder = docElem.compareDocumentPosition ? function( a, b ) { var compare; if ( a === b ) { hasDuplicate = true; return 0; } if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) { if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) { if ( a === doc || contains( preferredDoc, a ) ) { return -1; } if ( b === doc || contains( preferredDoc, b ) ) { return 1; } return 0; } return compare & 4 ? -1 : 1; } return a.compareDocumentPosition ? -1 : 1; } : function( a, b ) { var cur, i = 0, aup = a.parentNode, bup = b.parentNode, ap = [ a ], bp = [ b ]; // Exit early if the nodes are identical if ( a === b ) { hasDuplicate = true; return 0; // Parentless nodes are either documents or disconnected } else if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : bup ? 1 : 0; // If the nodes are siblings, we can do a quick check } else if ( aup === bup ) { return siblingCheck( a, b ); } // Otherwise we need full lists of their ancestors for comparison cur = a; while ( (cur = cur.parentNode) ) { ap.unshift( cur ); } cur = b; while ( (cur = cur.parentNode) ) { bp.unshift( cur ); } // Walk down the tree looking for a discrepancy while ( ap[i] === bp[i] ) { i++; } return i ? // Do a sibling check if the nodes have a common ancestor siblingCheck( ap[i], bp[i] ) : // Otherwise nodes in our document sort first ap[i] === preferredDoc ? -1 : bp[i] === preferredDoc ? 1 : 0; }; // Always assume the presence of duplicates if sort doesn't // pass them to our comparison function (as in Google Chrome). hasDuplicate = false; [0, 0].sort( sortOrder ); support.detectDuplicates = hasDuplicate; return document; }; Sizzle.matches = function( expr, elements ) { return Sizzle( expr, null, null, elements ); }; Sizzle.matchesSelector = function( elem, expr ) { // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } // Make sure that attribute selectors are quoted expr = expr.replace( rattributeQuotes, "='$1']" ); // rbuggyQSA always contains :focus, so no need for an existence check if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) { try { var ret = matches.call( elem, expr ); // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || // As well, disconnected nodes are said to be in a document // fragment in IE 9 elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch(e) {} } return Sizzle( expr, document, null, [elem] ).length > 0; }; Sizzle.contains = function( context, elem ) { // Set document vars if needed if ( ( context.ownerDocument || context ) !== document ) { setDocument( context ); } return contains( context, elem ); }; Sizzle.attr = function( elem, name ) { var val; // Set document vars if needed if ( ( elem.ownerDocument || elem ) !== document ) { setDocument( elem ); } if ( !documentIsXML ) { name = name.toLowerCase(); } if ( (val = Expr.attrHandle[ name ]) ) { return val( elem ); } if ( documentIsXML || support.attributes ) { return elem.getAttribute( name ); } return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ? name : val && val.specified ? val.value : null; }; Sizzle.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; // Document sorting and removing duplicates Sizzle.uniqueSort = function( results ) { var elem, duplicates = [], i = 1, j = 0; // Unless we *know* we can detect duplicates, assume their presence hasDuplicate = !support.detectDuplicates; results.sort( sortOrder ); if ( hasDuplicate ) { for ( ; (elem = results[i]); i++ ) { if ( elem === results[ i - 1 ] ) { j = duplicates.push( i ); } } while ( j-- ) { results.splice( duplicates[ j ], 1 ); } } return results; }; function siblingCheck( a, b ) { var cur = b && a, diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE ); // Use IE sourceIndex if available on both nodes if ( diff ) { return diff; } // Check if b follows a if ( cur ) { while ( (cur = cur.nextSibling) ) { if ( cur === b ) { return -1; } } } return a ? 1 : -1; } // Returns a function to use in pseudos for input types function createInputPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === type; }; } // Returns a function to use in pseudos for buttons function createButtonPseudo( type ) { return function( elem ) { var name = elem.nodeName.toLowerCase(); return (name === "input" || name === "button") && elem.type === type; }; } // Returns a function to use in pseudos for positionals function createPositionalPseudo( fn ) { return markFunction(function( argument ) { argument = +argument; return markFunction(function( seed, matches ) { var j, matchIndexes = fn( [], seed.length, argument ), i = matchIndexes.length; // Match elements found at the specified indexes while ( i-- ) { if ( seed[ (j = matchIndexes[i]) ] ) { seed[j] = !(matches[j] = seed[j]); } } }); }); } /** * Utility function for retrieving the text value of an array of DOM nodes * @param {Array|Element} elem */ getText = Sizzle.getText = function( elem ) { var node, ret = "", i = 0, nodeType = elem.nodeType; if ( !nodeType ) { // If no nodeType, this is expected to be an array for ( ; (node = elem[i]); i++ ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements // innerText usage removed for consistency of new lines (see #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { // Traverse its children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { ret += getText( elem ); } } } else if ( nodeType === 3 || nodeType === 4 ) { return elem.nodeValue; } // Do not include comment or processing instruction nodes return ret; }; Expr = Sizzle.selectors = { // Can be adjusted by the user cacheLength: 50, createPseudo: markFunction, match: matchExpr, find: {}, relative: { ">": { dir: "parentNode", first: true }, " ": { dir: "parentNode" }, "+": { dir: "previousSibling", first: true }, "~": { dir: "previousSibling" } }, preFilter: { "ATTR": function( match ) { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; } return match.slice( 0, 4 ); }, "CHILD": function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) 2 what (child|of-type) 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) 4 xn-component of xn+y argument ([+-]?\d*n|) 5 sign of xn-component 6 x of xn-component 7 sign of y-component 8 y of y-component */ match[1] = match[1].toLowerCase(); if ( match[1].slice( 0, 3 ) === "nth" ) { // nth-* requires argument if ( !match[3] ) { Sizzle.error( match[0] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); // other types prohibit arguments } else if ( match[3] ) { Sizzle.error( match[0] ); } return match; }, "PSEUDO": function( match ) { var excess, unquoted = !match[5] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is if ( match[4] ) { match[2] = match[4]; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && // Get excess from tokenize (recursively) (excess = tokenize( unquoted, true )) && // advance to the next closing parenthesis (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { // excess is a negative index match[0] = match[0].slice( 0, excess ); match[2] = unquoted.slice( 0, excess ); } // Return only captures needed by the pseudo filter method (type and argument) return match.slice( 0, 3 ); } }, filter: { "TAG": function( nodeName ) { if ( nodeName === "*" ) { return function() { return true; }; } nodeName = nodeName.replace( runescape, funescape ).toLowerCase(); return function( elem ) { return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; }; }, "CLASS": function( className ) { var pattern = classCache[ className + " " ]; return pattern || (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && classCache( className, function( elem ) { return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); }); }, "ATTR": function( name, operator, check ) { return function( elem ) { var result = Sizzle.attr( elem, name ); if ( result == null ) { return operator === "!="; } if ( !operator ) { return true; } result += ""; return operator === "=" ? result === check : operator === "!=" ? result !== check : operator === "^=" ? check && result.indexOf( check ) === 0 : operator === "*=" ? check && result.indexOf( check ) > -1 : operator === "$=" ? check && result.slice( -check.length ) === check : operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : false; }; }, "CHILD": function( type, what, argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; return first === 1 && last === 0 ? // Shortcut for :nth-*(n) function( elem ) { return !!elem.parentNode; } : function( elem, context, xml ) { var cache, outerCache, node, diff, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), useCache = !xml && !ofType; if ( parent ) { // :(first|last|only)-(child|of-type) if ( simple ) { while ( dir ) { node = elem; while ( (node = node[ dir ]) ) { if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { return false; } } // Reverse direction for :only-* (if we haven't yet done so) start = dir = type === "only" && !start && "nextSibling"; } return true; } start = [ forward ? parent.firstChild : parent.lastChild ]; // non-xml :nth-child(...) stores cache data on `parent` if ( forward && useCache ) { // Seek `elem` from a previously-cached index outerCache = parent[ expando ] || (parent[ expando ] = {}); cache = outerCache[ type ] || []; nodeIndex = cache[0] === dirruns && cache[1]; diff = cache[0] === dirruns && cache[2]; node = nodeIndex && parent.childNodes[ nodeIndex ]; while ( (node = ++nodeIndex && node && node[ dir ] || // Fallback to seeking `elem` from the start (diff = nodeIndex = 0) || start.pop()) ) { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } // Use previously-cached element index if available } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { diff = cache[1]; // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) } else { // Use the same loop as above to seek `elem` from the start while ( (node = ++nodeIndex && node && node[ dir ] || (diff = nodeIndex = 0) || start.pop()) ) { if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { // Cache the index of each encountered element if ( useCache ) { (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; } if ( node === elem ) { break; } } } } // Incorporate the offset, then check against cycle size diff -= last; return diff === first || ( diff % first === 0 && diff / first >= 0 ); } }; }, "PSEUDO": function( pseudo, argument ) { // pseudo-class names are case-insensitive // http://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || Sizzle.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function // just as Sizzle does if ( fn[ expando ] ) { return fn( argument ); } // But maintain support for old signatures if ( fn.length > 1 ) { args = [ pseudo, pseudo, "", argument ]; return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? markFunction(function( seed, matches ) { var idx, matched = fn( seed, argument ), i = matched.length; while ( i-- ) { idx = indexOf.call( seed, matched[i] ); seed[ idx ] = !( matches[ idx ] = matched[i] ); } }) : function( elem ) { return fn( elem, 0, args ); }; } return fn; } }, pseudos: { // Potentially complex pseudos "not": markFunction(function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], matcher = compile( selector.replace( rtrim, "$1" ) ); return matcher[ expando ] ? markFunction(function( seed, matches, context, xml ) { var elem, unmatched = matcher( seed, null, xml, [] ), i = seed.length; // Match elements unmatched by `matcher` while ( i-- ) { if ( (elem = unmatched[i]) ) { seed[i] = !(matches[i] = elem); } } }) : function( elem, context, xml ) { input[0] = elem; matcher( input, null, xml, results ); return !results.pop(); }; }), "has": markFunction(function( selector ) { return function( elem ) { return Sizzle( selector, elem ).length > 0; }; }), "contains": markFunction(function( text ) { return function( elem ) { return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; }; }), // "Whether an element is represented by a :lang() selector // is based solely on the element's language value // being equal to the identifier C, // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." // http://www.w3.org/TR/selectors/#lang-pseudo "lang": markFunction( function( lang ) { // lang value must be a valid identifider if ( !ridentifier.test(lang || "") ) { Sizzle.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { var elemLang; do { if ( (elemLang = documentIsXML ? elem.getAttribute("xml:lang") || elem.getAttribute("lang") : elem.lang) ) { elemLang = elemLang.toLowerCase(); return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; } } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); return false; }; }), // Miscellaneous "target": function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, "root": function( elem ) { return elem === docElem; }, "focus": function( elem ) { return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); }, // Boolean properties "enabled": function( elem ) { return elem.disabled === false; }, "disabled": function( elem ) { return elem.disabled === true; }, "checked": function( elem ) { // In CSS3, :checked should return both checked and selected elements // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked var nodeName = elem.nodeName.toLowerCase(); return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); }, "selected": function( elem ) { // Accessing this property makes selected-by-default // options in Safari work properly if ( elem.parentNode ) { elem.parentNode.selectedIndex; } return elem.selected === true; }, // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), // not comment, processing instructions, or others // Thanks to Diego Perini for the nodeName shortcut // Greater than "@" means alpha characters (specifically not starting with "#" or "?") for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { return false; } } return true; }, "parent": function( elem ) { return !Expr.pseudos["empty"]( elem ); }, // Element/input types "header": function( elem ) { return rheader.test( elem.nodeName ); }, "input": function( elem ) { return rinputs.test( elem.nodeName ); }, "button": function( elem ) { var name = elem.nodeName.toLowerCase(); return name === "input" && elem.type === "button" || name === "button"; }, "text": function( elem ) { var attr; // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); }, // Position-in-collection "first": createPositionalPseudo(function() { return [ 0 ]; }), "last": createPositionalPseudo(function( matchIndexes, length ) { return [ length - 1 ]; }), "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; }), "even": createPositionalPseudo(function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "odd": createPositionalPseudo(function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); } return matchIndexes; }), "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; }), "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); } return matchIndexes; }) } }; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { Expr.pseudos[ i ] = createInputPseudo( i ); } for ( i in { submit: true, reset: true } ) { Expr.pseudos[ i ] = createButtonPseudo( i ); } function tokenize( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; if ( cached ) { return parseOnly ? 0 : cached.slice( 0 ); } soFar = selector; groups = []; preFilters = Expr.preFilter; while ( soFar ) { // Comma and first run if ( !matched || (match = rcomma.exec( soFar )) ) { if ( match ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } groups.push( tokens = [] ); } matched = false; // Combinators if ( (match = rcombinators.exec( soFar )) ) { matched = match.shift(); tokens.push( { value: matched, // Cast descendant combinators to space type: match[0].replace( rtrim, " " ) } ); soFar = soFar.slice( matched.length ); } // Filters for ( type in Expr.filter ) { if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || (match = preFilters[ type ]( match ))) ) { matched = match.shift(); tokens.push( { value: matched, type: type, matches: match } ); soFar = soFar.slice( matched.length ); } } if ( !matched ) { break; } } // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens return parseOnly ? soFar.length : soFar ? Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); } function toSelector( tokens ) { var i = 0, len = tokens.length, selector = ""; for ( ; i < len; i++ ) { selector += tokens[i].value; } return selector; } function addCombinator( matcher, combinator, base ) { var dir = combinator.dir, checkNonElements = base && dir === "parentNode", doneName = done++; return combinator.first ? // Check against closest ancestor/preceding element function( elem, context, xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { return matcher( elem, context, xml ); } } } : // Check against all ancestor/preceding elements function( elem, context, xml ) { var data, cache, outerCache, dirkey = dirruns + " " + doneName; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching if ( xml ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { if ( matcher( elem, context, xml ) ) { return true; } } } } else { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { if ( (data = cache[1]) === true || data === cachedruns ) { return data === true; } } else { cache = outerCache[ dir ] = [ dirkey ]; cache[1] = matcher( elem, context, xml ) || cachedruns; if ( cache[1] === true ) { return true; } } } } } }; } function elementMatcher( matchers ) { return matchers.length > 1 ? function( elem, context, xml ) { var i = matchers.length; while ( i-- ) { if ( !matchers[i]( elem, context, xml ) ) { return false; } } return true; } : matchers[0]; } function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], i = 0, len = unmatched.length, mapped = map != null; for ( ; i < len; i++ ) { if ( (elem = unmatched[i]) ) { if ( !filter || filter( elem, context, xml ) ) { newUnmatched.push( elem ); if ( mapped ) { map.push( i ); } } } } return newUnmatched; } function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { if ( postFilter && !postFilter[ expando ] ) { postFilter = setMatcher( postFilter ); } if ( postFinder && !postFinder[ expando ] ) { postFinder = setMatcher( postFinder, postSelector ); } return markFunction(function( seed, results, context, xml ) { var temp, i, elem, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : elems, matcherOut = matcher ? // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, postFinder || ( seed ? preFilter : preexisting || postFilter ) ? // ...intermediate processing is necessary [] : // ...otherwise use results directly results : matcherIn; // Find primary matches if ( matcher ) { matcher( matcherIn, matcherOut, context, xml ); } // Apply postFilter if ( postFilter ) { temp = condense( matcherOut, postMap ); postFilter( temp, [], context, xml ); // Un-match failing elements by moving them back to matcherIn i = temp.length; while ( i-- ) { if ( (elem = temp[i]) ) { matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); } } } if ( seed ) { if ( postFinder || preFilter ) { if ( postFinder ) { // Get the final matcherOut by condensing this intermediate into postFinder contexts temp = []; i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) ) { // Restore matcherIn since elem is not yet a final match temp.push( (matcherIn[i] = elem) ); } } postFinder( null, (matcherOut = []), temp, xml ); } // Move matched elements from seed to results to keep them synchronized i = matcherOut.length; while ( i-- ) { if ( (elem = matcherOut[i]) && (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { seed[temp] = !(results[temp] = elem); } } } // Add elements to results, through postFinder if defined } else { matcherOut = condense( matcherOut === results ? matcherOut.splice( preexisting, matcherOut.length ) : matcherOut ); if ( postFinder ) { postFinder( null, results, matcherOut, xml ); } else { push.apply( results, matcherOut ); } } }); } function matcherFromTokens( tokens ) { var checkContext, matcher, j, len = tokens.length, leadingRelative = Expr.relative[ tokens[0].type ], implicitRelative = leadingRelative || Expr.relative[" "], i = leadingRelative ? 1 : 0, // The foundational matcher ensures that elements are reachable from top-level context(s) matchContext = addCombinator( function( elem ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( (checkContext = context).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); } ]; for ( ; i < len; i++ ) { if ( (matcher = Expr.relative[ tokens[i].type ]) ) { matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; } else { matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); // Return special upon seeing a positional matcher if ( matcher[ expando ] ) { // Find the next relative operator (if any) for proper handling j = ++i; for ( ; j < len; j++ ) { if ( Expr.relative[ tokens[j].type ] ) { break; } } return setMatcher( i > 1 && elementMatcher( matchers ), i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), j < len && toSelector( tokens ) ); } matchers.push( matcher ); } } return elementMatcher( matchers ); } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { // A counter to specify which element is currently being matched var matcherCachedRuns = 0, bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, superMatcher = function( seed, context, xml, results, expandContext ) { var elem, j, matcher, setMatched = [], matchedCount = 0, i = "0", unmatched = seed && [], outermost = expandContext != null, contextBackup = outermostContext, // We must always have either seed elements or context elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); if ( outermost ) { outermostContext = context !== document && context; cachedruns = matcherCachedRuns; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below for ( ; (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; while ( (matcher = elementMatchers[j++]) ) { if ( matcher( elem, context, xml ) ) { results.push( elem ); break; } } if ( outermost ) { dirruns = dirrunsUnique; cachedruns = ++matcherCachedRuns; } } // Track unmatched elements for set filters if ( bySet ) { // They will have gone through all possible matchers if ( (elem = !matcher && elem) ) { matchedCount--; } // Lengthen the array for every element, matched or not if ( seed ) { unmatched.push( elem ); } } } // Apply set filters to unmatched elements matchedCount += i; if ( bySet && i !== matchedCount ) { j = 0; while ( (matcher = setMatchers[j++]) ) { matcher( unmatched, setMatched, context, xml ); } if ( seed ) { // Reintegrate element matches to eliminate the need for sorting if ( matchedCount > 0 ) { while ( i-- ) { if ( !(unmatched[i] || setMatched[i]) ) { setMatched[i] = pop.call( results ); } } } // Discard index placeholder values to get only actual matches setMatched = condense( setMatched ); } // Add matches to results push.apply( results, setMatched ); // Seedless set matches succeeding multiple successful matchers stipulate sorting if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { Sizzle.uniqueSort( results ); } } // Override manipulation of globals by nested matchers if ( outermost ) { dirruns = dirrunsUnique; outermostContext = contextBackup; } return unmatched; }; return bySet ? markFunction( superMatcher ) : superMatcher; } compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], cached = compilerCache[ selector + " " ]; if ( !cached ) { // Generate a function of recursive functions that can be used to check each element if ( !group ) { group = tokenize( selector ); } i = group.length; while ( i-- ) { cached = matcherFromTokens( group[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { elementMatchers.push( cached ); } } // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); } return cached; }; function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { Sizzle( selector, contexts[i], results ); } return results; } function select( selector, context, results, seed ) { var i, tokens, token, type, find, match = tokenize( selector ); if ( !seed ) { // Try to minimize operations if there is only one group if ( match.length === 1 ) { // Take a shortcut and set the context if the root selector is an ID tokens = match[0] = match[0].slice( 0 ); if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && context.nodeType === 9 && !documentIsXML && Expr.relative[ tokens[1].type ] ) { context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0]; if ( !context ) { return results; } selector = selector.slice( tokens.shift().value.length ); } // Fetch a seed set for right-to-left matching i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[i]; // Abort if we hit a combinator if ( Expr.relative[ (type = token.type) ] ) { break; } if ( (find = Expr.find[ type ]) ) { // Search, expanding context for leading sibling combinators if ( (seed = find( token.matches[0].replace( runescape, funescape ), rsibling.test( tokens[0].type ) && context.parentNode || context )) ) { // If seed is empty or no tokens remain, we can return early tokens.splice( i, 1 ); selector = seed.length && toSelector( tokens ); if ( !selector ) { push.apply( results, slice.call( seed, 0 ) ); return results; } break; } } } } } // Compile and execute a filtering function // Provide `match` to avoid retokenization if we modified the selector above compile( selector, match )( seed, context, documentIsXML, results, rsibling.test( selector ) ); return results; } // Deprecated Expr.pseudos["nth"] = Expr.pseudos["eq"]; // Easy API for creating new setFilters function setFilters() {} Expr.filters = setFilters.prototype = Expr.pseudos; Expr.setFilters = new setFilters(); // Initialize with the default document setDocument(); // Override sizzle attribute retrieval Sizzle.attr = jQuery.attr; jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; jQuery.unique = Sizzle.uniqueSort; jQuery.text = Sizzle.getText; jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; })( window ); var runtil = /Until$/, rparentsprev = /^(?:parents|prev(?:Until|All))/, isSimple = /^.[^:#\[\.,]*$/, rneedsContext = jQuery.expr.match.needsContext, // methods guaranteed to produce a unique set when starting from a unique set guaranteedUnique = { children: true, contents: true, next: true, prev: true }; jQuery.fn.extend({ find: function( selector ) { var i, ret, self, len = this.length; if ( typeof selector !== "string" ) { self = this; return this.pushStack( jQuery( selector ).filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( self[ i ], this ) ) { return true; } } }) ); } ret = []; for ( i = 0; i < len; i++ ) { jQuery.find( selector, this[ i ], ret ); } // Needed because $( selector, context ) becomes $( context ).find( selector ) ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); ret.selector = ( this.selector ? this.selector + " " : "" ) + selector; return ret; }, has: function( target ) { var i, targets = jQuery( target, this ), len = targets.length; return this.filter(function() { for ( i = 0; i < len; i++ ) { if ( jQuery.contains( this, targets[i] ) ) { return true; } } }); }, not: function( selector ) { return this.pushStack( winnow(this, selector, false) ); }, filter: function( selector ) { return this.pushStack( winnow(this, selector, true) ); }, is: function( selector ) { return !!selector && ( typeof selector === "string" ? // If this is a positional/relative selector, check membership in the returned set // so $("p:first").is("p:last") won't return true for a doc with two "p". rneedsContext.test( selector ) ? jQuery( selector, this.context ).index( this[0] ) >= 0 : jQuery.filter( selector, this ).length > 0 : this.filter( selector ).length > 0 ); }, closest: function( selectors, context ) { var cur, i = 0, l = this.length, ret = [], pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? jQuery( selectors, context || this.context ) : 0; for ( ; i < l; i++ ) { cur = this[i]; while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) { if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { ret.push( cur ); break; } cur = cur.parentNode; } } return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { // No argument, return index in parent if ( !elem ) { return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; } // index in selector if ( typeof elem === "string" ) { return jQuery.inArray( this[0], jQuery( elem ) ); } // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem.jquery ? elem[0] : elem, this ); }, add: function( selector, context ) { var set = typeof selector === "string" ? jQuery( selector, context ) : jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), all = jQuery.merge( this.get(), set ); return this.pushStack( jQuery.unique(all) ); }, addBack: function( selector ) { return this.add( selector == null ? this.prevObject : this.prevObject.filter(selector) ); } }); jQuery.fn.andSelf = jQuery.fn.addBack; function sibling( cur, dir ) { do { cur = cur[ dir ]; } while ( cur && cur.nodeType !== 1 ); return cur; } jQuery.each({ parent: function( elem ) { var parent = elem.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, parents: function( elem ) { return jQuery.dir( elem, "parentNode" ); }, parentsUntil: function( elem, i, until ) { return jQuery.dir( elem, "parentNode", until ); }, next: function( elem ) { return sibling( elem, "nextSibling" ); }, prev: function( elem ) { return sibling( elem, "previousSibling" ); }, nextAll: function( elem ) { return jQuery.dir( elem, "nextSibling" ); }, prevAll: function( elem ) { return jQuery.dir( elem, "previousSibling" ); }, nextUntil: function( elem, i, until ) { return jQuery.dir( elem, "nextSibling", until ); }, prevUntil: function( elem, i, until ) { return jQuery.dir( elem, "previousSibling", until ); }, siblings: function( elem ) { return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); }, children: function( elem ) { return jQuery.sibling( elem.firstChild ); }, contents: function( elem ) { return jQuery.nodeName( elem, "iframe" ) ? elem.contentDocument || elem.contentWindow.document : jQuery.merge( [], elem.childNodes ); } }, function( name, fn ) { jQuery.fn[ name ] = function( until, selector ) { var ret = jQuery.map( this, fn, until ); if ( !runtil.test( name ) ) { selector = until; } if ( selector && typeof selector === "string" ) { ret = jQuery.filter( selector, ret ); } ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; if ( this.length > 1 && rparentsprev.test( name ) ) { ret = ret.reverse(); } return this.pushStack( ret ); }; }); jQuery.extend({ filter: function( expr, elems, not ) { if ( not ) { expr = ":not(" + expr + ")"; } return elems.length === 1 ? jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : jQuery.find.matches(expr, elems); }, dir: function( elem, dir, until ) { var matched = [], cur = elem[ dir ]; while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { if ( cur.nodeType === 1 ) { matched.push( cur ); } cur = cur[dir]; } return matched; }, sibling: function( n, elem ) { var r = []; for ( ; n; n = n.nextSibling ) { if ( n.nodeType === 1 && n !== elem ) { r.push( n ); } } return r; } }); // Implement the identical functionality for filter and not function winnow( elements, qualifier, keep ) { // Can't pass null or undefined to indexOf in Firefox 4 // Set to 0 to skip string check qualifier = qualifier || 0; if ( jQuery.isFunction( qualifier ) ) { return jQuery.grep(elements, function( elem, i ) { var retVal = !!qualifier.call( elem, i, elem ); return retVal === keep; }); } else if ( qualifier.nodeType ) { return jQuery.grep(elements, function( elem ) { return ( elem === qualifier ) === keep; }); } else if ( typeof qualifier === "string" ) { var filtered = jQuery.grep(elements, function( elem ) { return elem.nodeType === 1; }); if ( isSimple.test( qualifier ) ) { return jQuery.filter(qualifier, filtered, !keep); } else { qualifier = jQuery.filter( qualifier, filtered ); } } return jQuery.grep(elements, function( elem ) { return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; }); } function createSafeFragment( document ) { var list = nodeNames.split( "|" ), safeFrag = document.createDocumentFragment(); if ( safeFrag.createElement ) { while ( list.length ) { safeFrag.createElement( list.pop() ); } } return safeFrag; } var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), rleadingWhitespace = /^\s+/, rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rtbody = /\s*$/g, // We have to close these tags to support XHTML (#13200) wrapMap = { option: [ 1, "" ], legend: [ 1, "
    ", "
    " ], area: [ 1, "", "" ], param: [ 1, "", "" ], thead: [ 1, "", "
    " ], tr: [ 2, "", "
    " ], col: [ 2, "", "
    " ], td: [ 3, "", "
    " ], // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, // unless wrapped in a div with non-breaking characters in front of it. _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
    ", "
    " ] }, safeFragment = createSafeFragment( document ), fragmentDiv = safeFragment.appendChild( document.createElement("div") ); wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; jQuery.fn.extend({ text: function( value ) { return jQuery.access( this, function( value ) { return value === undefined ? jQuery.text( this ) : this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); }, null, value, arguments.length ); }, wrapAll: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each(function(i) { jQuery(this).wrapAll( html.call(this, i) ); }); } if ( this[0] ) { // The elements to wrap the target around var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); if ( this[0].parentNode ) { wrap.insertBefore( this[0] ); } wrap.map(function() { var elem = this; while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { elem = elem.firstChild; } return elem; }).append( this ); } return this; }, wrapInner: function( html ) { if ( jQuery.isFunction( html ) ) { return this.each(function(i) { jQuery(this).wrapInner( html.call(this, i) ); }); } return this.each(function() { var self = jQuery( this ), contents = self.contents(); if ( contents.length ) { contents.wrapAll( html ); } else { self.append( html ); } }); }, wrap: function( html ) { var isFunction = jQuery.isFunction( html ); return this.each(function(i) { jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); }); }, unwrap: function() { return this.parent().each(function() { if ( !jQuery.nodeName( this, "body" ) ) { jQuery( this ).replaceWith( this.childNodes ); } }).end(); }, append: function() { return this.domManip(arguments, true, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.appendChild( elem ); } }); }, prepend: function() { return this.domManip(arguments, true, function( elem ) { if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { this.insertBefore( elem, this.firstChild ); } }); }, before: function() { return this.domManip( arguments, false, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this ); } }); }, after: function() { return this.domManip( arguments, false, function( elem ) { if ( this.parentNode ) { this.parentNode.insertBefore( elem, this.nextSibling ); } }); }, // keepData is for internal use only--do not document remove: function( selector, keepData ) { var elem, i = 0; for ( ; (elem = this[i]) != null; i++ ) { if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) { if ( !keepData && elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem ) ); } if ( elem.parentNode ) { if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { setGlobalEval( getAll( elem, "script" ) ); } elem.parentNode.removeChild( elem ); } } } return this; }, empty: function() { var elem, i = 0; for ( ; (elem = this[i]) != null; i++ ) { // Remove element nodes and prevent memory leaks if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); } // Remove any remaining nodes while ( elem.firstChild ) { elem.removeChild( elem.firstChild ); } // If this is a select, ensure that it displays empty (#12336) // Support: IE<9 if ( elem.options && jQuery.nodeName( elem, "select" ) ) { elem.options.length = 0; } } return this; }, clone: function( dataAndEvents, deepDataAndEvents ) { dataAndEvents = dataAndEvents == null ? false : dataAndEvents; deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; return this.map( function () { return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); }); }, html: function( value ) { return jQuery.access( this, function( value ) { var elem = this[0] || {}, i = 0, l = this.length; if ( value === undefined ) { return elem.nodeType === 1 ? elem.innerHTML.replace( rinlinejQuery, "" ) : undefined; } // See if we can take a shortcut and just use innerHTML if ( typeof value === "string" && !rnoInnerhtml.test( value ) && ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { value = value.replace( rxhtmlTag, "<$1>" ); try { for (; i < l; i++ ) { // Remove element nodes and prevent memory leaks elem = this[i] || {}; if ( elem.nodeType === 1 ) { jQuery.cleanData( getAll( elem, false ) ); elem.innerHTML = value; } } elem = 0; // If using innerHTML throws an exception, use the fallback method } catch(e) {} } if ( elem ) { this.empty().append( value ); } }, null, value, arguments.length ); }, replaceWith: function( value ) { var isFunc = jQuery.isFunction( value ); // Make sure that the elements are removed from the DOM before they are inserted // this can help fix replacing a parent with child elements if ( !isFunc && typeof value !== "string" ) { value = jQuery( value ).not( this ).detach(); } return this.domManip( [ value ], true, function( elem ) { var next = this.nextSibling, parent = this.parentNode; if ( parent ) { jQuery( this ).remove(); parent.insertBefore( elem, next ); } }); }, detach: function( selector ) { return this.remove( selector, true ); }, domManip: function( args, table, callback ) { // Flatten any nested arrays args = core_concat.apply( [], args ); var first, node, hasScripts, scripts, doc, fragment, i = 0, l = this.length, set = this, iNoClone = l - 1, value = args[0], isFunction = jQuery.isFunction( value ); // We can't cloneNode fragments that contain checked, in WebKit if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { return this.each(function( index ) { var self = set.eq( index ); if ( isFunction ) { args[0] = value.call( this, index, table ? self.html() : undefined ); } self.domManip( args, table, callback ); }); } if ( l ) { fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); first = fragment.firstChild; if ( fragment.childNodes.length === 1 ) { fragment = first; } if ( first ) { table = table && jQuery.nodeName( first, "tr" ); scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); hasScripts = scripts.length; // Use the original fragment for the last item instead of the first because it can end up // being emptied incorrectly in certain situations (#8070). for ( ; i < l; i++ ) { node = fragment; if ( i !== iNoClone ) { node = jQuery.clone( node, true, true ); // Keep references to cloned scripts for later restoration if ( hasScripts ) { jQuery.merge( scripts, getAll( node, "script" ) ); } } callback.call( table && jQuery.nodeName( this[i], "table" ) ? findOrAppend( this[i], "tbody" ) : this[i], node, i ); } if ( hasScripts ) { doc = scripts[ scripts.length - 1 ].ownerDocument; // Reenable scripts jQuery.map( scripts, restoreScript ); // Evaluate executable scripts on first document insertion for ( i = 0; i < hasScripts; i++ ) { node = scripts[ i ]; if ( rscriptType.test( node.type || "" ) && !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { if ( node.src ) { // Hope ajax is available... jQuery.ajax({ url: node.src, type: "GET", dataType: "script", async: false, global: false, "throws": true }); } else { jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); } } } } // Fix #11809: Avoid leaking memory fragment = first = null; } } return this; } }); function findOrAppend( elem, tag ) { return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) ); } // Replace/restore the type attribute of script elements for safe DOM manipulation function disableScript( elem ) { var attr = elem.getAttributeNode("type"); elem.type = ( attr && attr.specified ) + "/" + elem.type; return elem; } function restoreScript( elem ) { var match = rscriptTypeMasked.exec( elem.type ); if ( match ) { elem.type = match[1]; } else { elem.removeAttribute("type"); } return elem; } // Mark scripts as having already been evaluated function setGlobalEval( elems, refElements ) { var elem, i = 0; for ( ; (elem = elems[i]) != null; i++ ) { jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); } } function cloneCopyEvent( src, dest ) { if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { return; } var type, i, l, oldData = jQuery._data( src ), curData = jQuery._data( dest, oldData ), events = oldData.events; if ( events ) { delete curData.handle; curData.events = {}; for ( type in events ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) { jQuery.event.add( dest, type, events[ type ][ i ] ); } } } // make the cloned public data object a copy from the original if ( curData.data ) { curData.data = jQuery.extend( {}, curData.data ); } } function fixCloneNodeIssues( src, dest ) { var nodeName, e, data; // We do not need to do anything for non-Elements if ( dest.nodeType !== 1 ) { return; } nodeName = dest.nodeName.toLowerCase(); // IE6-8 copies events bound via attachEvent when using cloneNode. if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { data = jQuery._data( dest ); for ( e in data.events ) { jQuery.removeEvent( dest, e, data.handle ); } // Event data gets referenced instead of copied if the expando gets copied too dest.removeAttribute( jQuery.expando ); } // IE blanks contents when cloning scripts, and tries to evaluate newly-set text if ( nodeName === "script" && dest.text !== src.text ) { disableScript( dest ).text = src.text; restoreScript( dest ); // IE6-10 improperly clones children of object elements using classid. // IE10 throws NoModificationAllowedError if parent is null, #12132. } else if ( nodeName === "object" ) { if ( dest.parentNode ) { dest.outerHTML = src.outerHTML; } // This path appears unavoidable for IE9. When cloning an object // element in IE9, the outerHTML strategy above is not sufficient. // If the src has innerHTML and the destination does not, // copy the src.innerHTML into the dest.innerHTML. #10324 if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { dest.innerHTML = src.innerHTML; } } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { // IE6-8 fails to persist the checked state of a cloned checkbox // or radio button. Worse, IE6-7 fail to give the cloned element // a checked appearance if the defaultChecked value isn't also set dest.defaultChecked = dest.checked = src.checked; // IE6-7 get confused and end up setting the value of a cloned // checkbox/radio button to an empty string instead of "on" if ( dest.value !== src.value ) { dest.value = src.value; } // IE6-8 fails to return the selected option to the default selected // state when cloning options } else if ( nodeName === "option" ) { dest.defaultSelected = dest.selected = src.defaultSelected; // IE6-8 fails to set the defaultValue to the correct value when // cloning other types of input fields } else if ( nodeName === "input" || nodeName === "textarea" ) { dest.defaultValue = src.defaultValue; } } jQuery.each({ appendTo: "append", prependTo: "prepend", insertBefore: "before", insertAfter: "after", replaceAll: "replaceWith" }, function( name, original ) { jQuery.fn[ name ] = function( selector ) { var elems, i = 0, ret = [], insert = jQuery( selector ), last = insert.length - 1; for ( ; i <= last; i++ ) { elems = i === last ? this : this.clone(true); jQuery( insert[i] )[ original ]( elems ); // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() core_push.apply( ret, elems.get() ); } return this.pushStack( ret ); }; }); function getAll( context, tag ) { var elems, elem, i = 0, found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : undefined; if ( !found ) { for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { if ( !tag || jQuery.nodeName( elem, tag ) ) { found.push( elem ); } else { jQuery.merge( found, getAll( elem, tag ) ); } } } return tag === undefined || tag && jQuery.nodeName( context, tag ) ? jQuery.merge( [ context ], found ) : found; } // Used in buildFragment, fixes the defaultChecked property function fixDefaultChecked( elem ) { if ( manipulation_rcheckableType.test( elem.type ) ) { elem.defaultChecked = elem.checked; } } jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { var destElements, node, clone, i, srcElements, inPage = jQuery.contains( elem.ownerDocument, elem ); if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { clone = elem.cloneNode( true ); // IE<=8 does not properly clone detached, unknown element nodes } else { fragmentDiv.innerHTML = elem.outerHTML; fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); } if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); // Fix all IE cloning issues for ( i = 0; (node = srcElements[i]) != null; ++i ) { // Ensure that the destination node is not null; Fixes #9587 if ( destElements[i] ) { fixCloneNodeIssues( node, destElements[i] ); } } } // Copy the events from the original to the clone if ( dataAndEvents ) { if ( deepDataAndEvents ) { srcElements = srcElements || getAll( elem ); destElements = destElements || getAll( clone ); for ( i = 0; (node = srcElements[i]) != null; i++ ) { cloneCopyEvent( node, destElements[i] ); } } else { cloneCopyEvent( elem, clone ); } } // Preserve script evaluation history destElements = getAll( clone, "script" ); if ( destElements.length > 0 ) { setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); } destElements = srcElements = node = null; // Return the cloned set return clone; }, buildFragment: function( elems, context, scripts, selection ) { var j, elem, contains, tmp, tag, tbody, wrap, l = elems.length, // Ensure a safe fragment safe = createSafeFragment( context ), nodes = [], i = 0; for ( ; i < l; i++ ) { elem = elems[ i ]; if ( elem || elem === 0 ) { // Add nodes directly if ( jQuery.type( elem ) === "object" ) { jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node } else if ( !rhtml.test( elem ) ) { nodes.push( context.createTextNode( elem ) ); // Convert html into DOM nodes } else { tmp = tmp || safe.appendChild( context.createElement("div") ); // Deserialize a standard representation tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; // Descend through wrappers to the right content j = wrap[0]; while ( j-- ) { tmp = tmp.lastChild; } // Manually add leading whitespace removed by IE if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); } // Remove IE's autoinserted from table fragments if ( !jQuery.support.tbody ) { // String was a , *may* have spurious elem = tag === "table" && !rtbody.test( elem ) ? tmp.firstChild : // String was a bare or wrap[1] === "
    " && !rtbody.test( elem ) ? tmp : 0; j = elem && elem.childNodes.length; while ( j-- ) { if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { elem.removeChild( tbody ); } } } jQuery.merge( nodes, tmp.childNodes ); // Fix #12392 for WebKit and IE > 9 tmp.textContent = ""; // Fix #12392 for oldIE while ( tmp.firstChild ) { tmp.removeChild( tmp.firstChild ); } // Remember the top-level container for proper cleanup tmp = safe.lastChild; } } } // Fix #11356: Clear elements from fragment if ( tmp ) { safe.removeChild( tmp ); } // Reset defaultChecked for any radios and checkboxes // about to be appended to the DOM in IE 6/7 (#8060) if ( !jQuery.support.appendChecked ) { jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); } i = 0; while ( (elem = nodes[ i++ ]) ) { // #4087 - If origin and destination elements are the same, and this is // that element, do not do anything if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { continue; } contains = jQuery.contains( elem.ownerDocument, elem ); // Append to fragment tmp = getAll( safe.appendChild( elem ), "script" ); // Preserve script evaluation history if ( contains ) { setGlobalEval( tmp ); } // Capture executables if ( scripts ) { j = 0; while ( (elem = tmp[ j++ ]) ) { if ( rscriptType.test( elem.type || "" ) ) { scripts.push( elem ); } } } } tmp = null; return safe; }, cleanData: function( elems, /* internal */ acceptData ) { var elem, type, id, data, i = 0, internalKey = jQuery.expando, cache = jQuery.cache, deleteExpando = jQuery.support.deleteExpando, special = jQuery.event.special; for ( ; (elem = elems[i]) != null; i++ ) { if ( acceptData || jQuery.acceptData( elem ) ) { id = elem[ internalKey ]; data = id && cache[ id ]; if ( data ) { if ( data.events ) { for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); // This is a shortcut to avoid jQuery.event.remove's overhead } else { jQuery.removeEvent( elem, type, data.handle ); } } } // Remove cache only if it was not already removed by jQuery.event.remove if ( cache[ id ] ) { delete cache[ id ]; // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( deleteExpando ) { delete elem[ internalKey ]; } else if ( typeof elem.removeAttribute !== core_strundefined ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } core_deletedIds.push( id ); } } } } } }); var iframe, getStyles, curCSS, ralpha = /alpha\([^)]*\)/i, ropacity = /opacity\s*=\s*([^)]*)/, rposition = /^(top|right|bottom|left)$/, // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display rdisplayswap = /^(none|table(?!-c[ea]).+)/, rmargin = /^margin/, rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), elemdisplay = { BODY: "block" }, cssShow = { position: "absolute", visibility: "hidden", display: "block" }, cssNormalTransform = { letterSpacing: 0, fontWeight: 400 }, cssExpand = [ "Top", "Right", "Bottom", "Left" ], cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; // return a css property mapped to a potentially vendor prefixed property function vendorPropName( style, name ) { // shortcut for names that are not vendor prefixed if ( name in style ) { return name; } // check for vendor prefixed names var capName = name.charAt(0).toUpperCase() + name.slice(1), origName = name, i = cssPrefixes.length; while ( i-- ) { name = cssPrefixes[ i ] + capName; if ( name in style ) { return name; } } return origName; } function isHidden( elem, el ) { // isHidden might be called from jQuery#filter function; // in that case, element will be second argument elem = el || elem; return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); } function showHide( elements, show ) { var display, elem, hidden, values = [], index = 0, length = elements.length; for ( ; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } values[ index ] = jQuery._data( elem, "olddisplay" ); display = elem.style.display; if ( show ) { // Reset the inline display of this element to learn if it is // being hidden by cascaded rules or not if ( !values[ index ] && display === "none" ) { elem.style.display = ""; } // Set elements which have been overridden with display: none // in a stylesheet to whatever the default browser style is // for such an element if ( elem.style.display === "" && isHidden( elem ) ) { values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); } } else { if ( !values[ index ] ) { hidden = isHidden( elem ); if ( display && display !== "none" || !hidden ) { jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); } } } } // Set the display of most of the elements in a second loop // to avoid the constant reflow for ( index = 0; index < length; index++ ) { elem = elements[ index ]; if ( !elem.style ) { continue; } if ( !show || elem.style.display === "none" || elem.style.display === "" ) { elem.style.display = show ? values[ index ] || "" : "none"; } } return elements; } jQuery.fn.extend({ css: function( name, value ) { return jQuery.access( this, function( elem, name, value ) { var len, styles, map = {}, i = 0; if ( jQuery.isArray( name ) ) { styles = getStyles( elem ); len = name.length; for ( ; i < len; i++ ) { map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); } return map; } return value !== undefined ? jQuery.style( elem, name, value ) : jQuery.css( elem, name ); }, name, value, arguments.length > 1 ); }, show: function() { return showHide( this, true ); }, hide: function() { return showHide( this ); }, toggle: function( state ) { var bool = typeof state === "boolean"; return this.each(function() { if ( bool ? state : isHidden( this ) ) { jQuery( this ).show(); } else { jQuery( this ).hide(); } }); } }); jQuery.extend({ // Add in style property hooks for overriding the default // behavior of getting and setting a style property cssHooks: { opacity: { get: function( elem, computed ) { if ( computed ) { // We should always get a number back from opacity var ret = curCSS( elem, "opacity" ); return ret === "" ? "1" : ret; } } } }, // Exclude the following css properties to add px cssNumber: { "columnCount": true, "fillOpacity": true, "fontWeight": true, "lineHeight": true, "opacity": true, "orphans": true, "widows": true, "zIndex": true, "zoom": true }, // Add in properties whose names you wish to fix before // setting or getting the value cssProps: { // normalize float css property "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" }, // Get and set the style property on a DOM Node style: function( elem, name, value, extra ) { // Don't set styles on text and comment nodes if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { return; } // Make sure that we're working with the right name var ret, type, hooks, origName = jQuery.camelCase( name ), style = elem.style; name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); // gets hook for the prefixed version // followed by the unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // Check if we're setting a value if ( value !== undefined ) { type = typeof value; // convert relative number strings (+= or -=) to relative numbers. #7345 if ( type === "string" && (ret = rrelNum.exec( value )) ) { value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); // Fixes bug #9237 type = "number"; } // Make sure that NaN and null values aren't set. See: #7116 if ( value == null || type === "number" && isNaN( value ) ) { return; } // If a number was passed in, add 'px' to the (except for certain CSS properties) if ( type === "number" && !jQuery.cssNumber[ origName ] ) { value += "px"; } // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, // but it would mean to define eight (for every problematic property) identical functions if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { style[ name ] = "inherit"; } // If a hook was provided, use that value, otherwise just set the specified value if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { // Wrapped to prevent IE from throwing errors when 'invalid' values are provided // Fixes bug #5509 try { style[ name ] = value; } catch(e) {} } } else { // If a hook was provided get the non-computed value from there if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { return ret; } // Otherwise just get the value from the style object return style[ name ]; } }, css: function( elem, name, extra, styles ) { var num, val, hooks, origName = jQuery.camelCase( name ); // Make sure that we're working with the right name name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); // gets hook for the prefixed version // followed by the unprefixed version hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; // If a hook was provided get the computed value from there if ( hooks && "get" in hooks ) { val = hooks.get( elem, true, extra ); } // Otherwise, if a way to get the computed value exists, use that if ( val === undefined ) { val = curCSS( elem, name, styles ); } //convert "normal" to computed value if ( val === "normal" && name in cssNormalTransform ) { val = cssNormalTransform[ name ]; } // Return, converting to number if forced or a qualifier was provided and val looks numeric if ( extra === "" || extra ) { num = parseFloat( val ); return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; } return val; }, // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback, args ) { var ret, name, old = {}; // Remember the old values, and insert the new ones for ( name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } ret = callback.apply( elem, args || [] ); // Revert the old values for ( name in options ) { elem.style[ name ] = old[ name ]; } return ret; } }); // NOTE: we've included the "window" in window.getComputedStyle // because jsdom on node.js will break without it. if ( window.getComputedStyle ) { getStyles = function( elem ) { return window.getComputedStyle( elem, null ); }; curCSS = function( elem, name, _computed ) { var width, minWidth, maxWidth, computed = _computed || getStyles( elem ), // getPropertyValue is only needed for .css('filter') in IE9, see #12537 ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, style = elem.style; if ( computed ) { if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { ret = jQuery.style( elem, name ); } // A tribute to the "awesome hack by Dean Edwards" // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { // Remember the original values width = style.width; minWidth = style.minWidth; maxWidth = style.maxWidth; // Put in the new values to get a computed value out style.minWidth = style.maxWidth = style.width = ret; ret = computed.width; // Revert the changed values style.width = width; style.minWidth = minWidth; style.maxWidth = maxWidth; } } return ret; }; } else if ( document.documentElement.currentStyle ) { getStyles = function( elem ) { return elem.currentStyle; }; curCSS = function( elem, name, _computed ) { var left, rs, rsLeft, computed = _computed || getStyles( elem ), ret = computed ? computed[ name ] : undefined, style = elem.style; // Avoid setting ret to empty string here // so we don't default to auto if ( ret == null && style && style[ name ] ) { ret = style[ name ]; } // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels // but not position css attributes, as those are proportional to the parent element instead // and we can't measure the parent instead because it might trigger a "stacking dolls" problem if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { // Remember the original values left = style.left; rs = elem.runtimeStyle; rsLeft = rs && rs.left; // Put in the new values to get a computed value out if ( rsLeft ) { rs.left = elem.currentStyle.left; } style.left = name === "fontSize" ? "1em" : ret; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; if ( rsLeft ) { rs.left = rsLeft; } } return ret === "" ? "auto" : ret; }; } function setPositiveNumber( elem, value, subtract ) { var matches = rnumsplit.exec( value ); return matches ? // Guard against undefined "subtract", e.g., when used as in cssHooks Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : value; } function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { var i = extra === ( isBorderBox ? "border" : "content" ) ? // If we already have the right measurement, avoid augmentation 4 : // Otherwise initialize for horizontal or vertical properties name === "width" ? 1 : 0, val = 0; for ( ; i < 4; i += 2 ) { // both box models exclude margin, so add it if we want it if ( extra === "margin" ) { val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); } if ( isBorderBox ) { // border-box includes padding, so remove it if we want content if ( extra === "content" ) { val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); } // at this point, extra isn't border nor margin, so remove border if ( extra !== "margin" ) { val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } else { // at this point, extra isn't content, so add padding val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); // at this point, extra isn't content nor padding, so add border if ( extra !== "padding" ) { val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); } } } return val; } function getWidthOrHeight( elem, name, extra ) { // Start with offset property, which is equivalent to the border-box value var valueIsBorderBox = true, val = name === "width" ? elem.offsetWidth : elem.offsetHeight, styles = getStyles( elem ), isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; // some non-html elements return undefined for offsetWidth, so check for null/undefined // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 if ( val <= 0 || val == null ) { // Fall back to computed then uncomputed css if necessary val = curCSS( elem, name, styles ); if ( val < 0 || val == null ) { val = elem.style[ name ]; } // Computed unit is not pixels. Stop here and return. if ( rnumnonpx.test(val) ) { return val; } // we need the check for style in case a browser which returns unreliable values // for getComputedStyle silently falls back to the reliable elem.style valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); // Normalize "", auto, and prepare for extra val = parseFloat( val ) || 0; } // use the active box-sizing model to add/subtract irrelevant styles return ( val + augmentWidthOrHeight( elem, name, extra || ( isBorderBox ? "border" : "content" ), valueIsBorderBox, styles ) ) + "px"; } // Try to determine the default display value of an element function css_defaultDisplay( nodeName ) { var doc = document, display = elemdisplay[ nodeName ]; if ( !display ) { display = actualDisplay( nodeName, doc ); // If the simple way fails, read from inside an iframe if ( display === "none" || !display ) { // Use the already-created iframe if possible iframe = ( iframe || jQuery("';d.extend(x.prototype,{_scroll:function(){var b=this.qtip.elements.overlay;b&&(b[0].style.top=d(a).scrollTop()+"px")},init:function(c){var e=c.tooltip;d("select, object").length<1&&(this.bgiframe=c.elements.bgiframe=d(xb).appendTo(e),c._bind(e,"tooltipmove",this.adjustBGIFrame,this._ns,this)),this.redrawContainer=d("
    ",{id:S+"-rcontainer"}).appendTo(b.body),c.elements.overlay&&c.elements.overlay.addClass("qtipmodal-ie6fix")&&(c._bind(a,["scroll","resize"],this._scroll,this._ns,this),c._bind(e,["tooltipshow"],this._scroll,this._ns,this)),this.redraw()},adjustBGIFrame:function(){var a,b,c=this.qtip.tooltip,d={height:c.outerHeight(E),width:c.outerWidth(E)},e=this.qtip.plugins.tip,f=this.qtip.elements.tip;b=parseInt(c.css("borderLeftWidth"),10)||0,b={left:-b,top:-b},e&&f&&(a="x"===e.corner.precedance?[I,L]:[J,K],b[a[1]]-=f[a[0]]()),this.bgiframe.css(b).css(d)},redraw:function(){if(this.qtip.rendered<1||this.drawing)return self;var a,b,c,d,e=this.qtip.tooltip,f=this.qtip.options.style,g=this.qtip.options.position.container;return this.qtip.drawing=1,f.height&&e.css(J,f.height),f.width?e.css(I,f.width):(e.css(I,"").appendTo(this.redrawContainer),b=e.width(),1>b%2&&(b+=1),c=e.css("maxWidth")||"",d=e.css("minWidth")||"",a=(c+d).indexOf("%")>-1?g.width()/100:0,c=(c.indexOf("%")>-1?a:1)*parseInt(c,10)||b,d=(d.indexOf("%")>-1?a:1)*parseInt(d,10)||0,b=c+d?Math.min(Math.max(b,d),c):b,e.css(I,Math.round(b)).appendTo(g)),this.drawing=0,self},destroy:function(){this.bgiframe&&this.bgiframe.remove(),this.qtip._unbind([a,this.qtip.tooltip],this._ns)}}),wb=R.ie6=function(a){return 6===BROWSER.ie?new x(a):E},wb.initialize="render",B.ie6={"^content|style$":function(){this.redraw()}}})}(window,document);ganglia-web-3.6.1/js/jquery.scrollTo-1.4.2.js000066400000000000000000000173711231750357400204470ustar00rootroot00000000000000/** * jQuery.ScrollTo * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * Dual licensed under MIT and GPL. * Date: 5/25/2009 * * @projectDescription Easy element scrolling using jQuery. * http://flesler.blogspot.com/2007/10/jqueryscrollto.html * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP. * * @author Ariel Flesler * @version 1.4.2 * * @id jQuery.scrollTo * @id jQuery.fn.scrollTo * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. * The different options for target are: * - A number position (will be applied to all axes). * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes * - A jQuery/DOM element ( logically, child of the element to scroll ) * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. * - A percentage of the container's dimension/s, for example: 50% to go to the middle. * - The string 'max' for go-to-end. * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. * @param {Object,Function} settings Optional set of settings or the onAfter callback. * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. * @option {Number} duration The OVERALL length of the animation. * @option {String} easing The easing method for the animation. * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. * @option {Function} onAfter Function to be called after the scrolling ends. * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. * @return {jQuery} Returns the same jQuery object, for chaining. * * @desc Scroll to a fixed position * @example $('div').scrollTo( 340 ); * * @desc Scroll relatively to the actual position * @example $('div').scrollTo( '+=340px', { axis:'y' } ); * * @dec Scroll using a selector (relative to the scrolled element) * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); * * @ Scroll to a DOM element (same for jQuery object) * @example var second_child = document.getElementById('container').firstChild.nextSibling; * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ * alert('scrolled!!'); * }}); * * @desc Scroll on both axes, to different values * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); */ ;(function( $ ){ var $scrollTo = $.scrollTo = function( target, duration, settings ){ $(window).scrollTo( target, duration, settings ); }; $scrollTo.defaults = { axis:'xy', duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 }; // Returns the element that needs to be animated to scroll the window. // Kept for backwards compatibility (specially for localScroll & serialScroll) $scrollTo.window = function( scope ){ return $(window)._scrollable(); }; // Hack, hack, hack :) // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) $.fn._scrollable = function(){ return this.map(function(){ var elem = this, isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; if( !isWin ) return elem; var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; return $.browser.safari || doc.compatMode == 'BackCompat' ? doc.body : doc.documentElement; }); }; $.fn.scrollTo = function( target, duration, settings ){ if( typeof duration == 'object' ){ settings = duration; duration = 0; } if( typeof settings == 'function' ) settings = { onAfter:settings }; if( target == 'max' ) target = 9e9; settings = $.extend( {}, $scrollTo.defaults, settings ); // Speed is still recognized for backwards compatibility duration = duration || settings.speed || settings.duration; // Make sure the settings are given right settings.queue = settings.queue && settings.axis.length > 1; if( settings.queue ) // Let's keep the overall duration duration /= 2; settings.offset = both( settings.offset ); settings.over = both( settings.over ); return this._scrollable().each(function(){ var elem = this, $elem = $(elem), targ = target, toff, attr = {}, win = $elem.is('html,body'); switch( typeof targ ){ // A number will pass the regex case 'number': case 'string': if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ targ = both( targ ); // We are done break; } // Relative selector, no break! targ = $(targ,this); case 'object': // DOMElement / jQuery if( targ.is || targ.style ) // Get the real position of the target toff = (targ = $(targ)).offset(); } $.each( settings.axis.split(''), function( i, axis ){ var Pos = axis == 'x' ? 'Left' : 'Top', pos = Pos.toLowerCase(), key = 'scroll' + Pos, old = elem[key], max = $scrollTo.max(elem, axis); if( toff ){// jQuery / DOMElement attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); // If it's a dom element, reduce the margin if( settings.margin ){ attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; } attr[key] += settings.offset[pos] || 0; if( settings.over[pos] ) // Scroll to a fraction of its width/height attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; }else{ var val = targ[pos]; // Handle percentage values attr[key] = val.slice && val.slice(-1) == '%' ? parseFloat(val) / 100 * max : val; } // Number or 'number' if( /^\d+$/.test(attr[key]) ) // Check the limits attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); // Queueing axes if( !i && settings.queue ){ // Don't waste time animating, if there's no need. if( old != attr[key] ) // Intermediate animation animate( settings.onAfterFirst ); // Don't animate this axis again in the next iteration. delete attr[key]; } }); animate( settings.onAfter ); function animate( callback ){ $elem.animate( attr, duration, settings.easing, callback && function(){ callback.call(this, target, settings); }); }; }).end(); }; // Max scrolling position, works on quirks mode // It only fails (not too badly) on IE, quirks mode. $scrollTo.max = function( elem, axis ){ var Dim = axis == 'x' ? 'Width' : 'Height', scroll = 'scroll'+Dim; if( !$(elem).is('html,body') ) return elem[scroll] - $(elem)[Dim.toLowerCase()](); var size = 'client' + Dim, html = elem.ownerDocument.documentElement, body = elem.ownerDocument.body; return Math.max( html[scroll], body[scroll] ) - Math.min( html[size] , body[size] ); }; function both( val ){ return typeof val == 'object' ? val : { top:val, left:val }; }; })( jQuery );ganglia-web-3.6.1/js/jquery.scrollTo-1.4.3.1-min.js000066400000000000000000000046021231750357400213610ustar00rootroot00000000000000/** * Copyright (c) 2007-2012 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com * Dual licensed under MIT and GPL. * @author Ariel Flesler * @version 1.4.3.1 */ ;(function($){var h=$.scrollTo=function(a,b,c){$(window).scrollTo(a,b,c)};h.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};h.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(e,f,g){if(typeof f=='object'){g=f;f=0}if(typeof g=='function')g={onAfter:g};if(e=='max')e=9e9;g=$.extend({},h.defaults,g);f=f||g.duration;g.queue=g.queue&&g.axis.length>1;if(g.queue)f/=2;g.offset=both(g.offset);g.over=both(g.over);return this._scrollable().each(function(){if(e==null)return;var d=this,$elem=$(d),targ=e,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}$.each(g.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=h.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(g.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=g.offset[pos]||0;if(g.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*g.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(g.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&g.queue){if(old!=attr[key])animate(g.onAfterFirst);delete attr[key]}});animate(g.onAfter);function animate(a){$elem.animate(attr,f,g.easing,a&&function(){a.call(this,e,g)})}}).end()};h.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery);ganglia-web-3.6.1/js/protovis-d3.2.js000066400000000000000000017432261231750357400172270ustar00rootroot00000000000000/** * @class The built-in Array class. * @name Array */ /** * Creates a new array with the results of calling a provided function on every * element in this array. Implemented in Javascript 1.6. * * @function * @name Array.prototype.map * @see map * documentation. * @param {function} f function that produces an element of the new Array from * an element of the current one. * @param [o] object to use as this when executing f. */ if (!Array.prototype.map) Array.prototype.map = function(f, o) { var n = this.length; var result = new Array(n); for (var i = 0; i < n; i++) { if (i in this) { result[i] = f.call(o, this[i], i, this); } } return result; }; /** * Creates a new array with all elements that pass the test implemented by the * provided function. Implemented in Javascript 1.6. * * @function * @name Array.prototype.filter * @see filter * documentation. * @param {function} f function to test each element of the array. * @param [o] object to use as this when executing f. */ if (!Array.prototype.filter) Array.prototype.filter = function(f, o) { var n = this.length; var result = new Array(); for (var i = 0; i < n; i++) { if (i in this) { var v = this[i]; if (f.call(o, v, i, this)) result.push(v); } } return result; }; /** * Executes a provided function once per array element. Implemented in * Javascript 1.6. * * @function * @name Array.prototype.forEach * @see forEach * documentation. * @param {function} f function to execute for each element. * @param [o] object to use as this when executing f. */ if (!Array.prototype.forEach) Array.prototype.forEach = function(f, o) { var n = this.length >>> 0; for (var i = 0; i < n; i++) { if (i in this) f.call(o, this[i], i, this); } }; /** * Apply a function against an accumulator and each value of the array (from * left-to-right) as to reduce it to a single value. Implemented in Javascript * 1.8. * * @function * @name Array.prototype.reduce * @see reduce * documentation. * @param {function} f function to execute on each value in the array. * @param [v] object to use as the first argument to the first call of * t. */ if (!Array.prototype.reduce) Array.prototype.reduce = function(f, v) { var len = this.length; if (!len && (arguments.length == 1)) { throw new Error("reduce: empty array, no initial value"); } var i = 0; if (arguments.length < 2) { while (true) { if (i in this) { v = this[i++]; break; } if (++i >= len) { throw new Error("reduce: no values, no initial value"); } } } for (; i < len; i++) { if (i in this) { v = f(v, this[i], i, this); } } return v; }; /** * The top-level Protovis namespace. All public methods and fields should be * registered on this object. Note that core Protovis source is surrounded by an * anonymous function, so any other declared globals will not be visible outside * of core methods. This also allows multiple versions of Protovis to coexist, * since each version will see their own pv namespace. * * @namespace The top-level Protovis namespace, pv. */ var pv = {}; /** * Protovis version number. See semver.org. * * @type string * @constant */ pv.version = "3.2.0"; /** * Returns the passed-in argument, x; the identity function. This method * is provided for convenience since it is used as the default behavior for a * number of property functions. * * @param x a value. * @returns the value x. */ pv.identity = function(x) { return x; }; /** * Returns this.index. This method is provided for convenience for use * with scales. For example, to color bars by their index, say: * *
    .fillStyle(pv.Colors.category10().by(pv.index))
    * * This method is equivalent to function() this.index, but more * succinct. Note that the index property is also supported for * accessor functions with {@link pv.max}, {@link pv.min} and other array * utility methods. * * @see pv.Scale * @see pv.Mark#index */ pv.index = function() { return this.index; }; /** * Returns this.childIndex. This method is provided for convenience for * use with scales. For example, to color bars by their child index, say: * *
    .fillStyle(pv.Colors.category10().by(pv.child))
    * * This method is equivalent to function() this.childIndex, but more * succinct. * * @see pv.Scale * @see pv.Mark#childIndex */ pv.child = function() { return this.childIndex; }; /** * Returns this.parent.index. This method is provided for convenience * for use with scales. This method is provided for convenience for use with * scales. For example, to color bars by their parent index, say: * *
    .fillStyle(pv.Colors.category10().by(pv.parent))
    * * Tthis method is equivalent to function() this.parent.index, but more * succinct. * * @see pv.Scale * @see pv.Mark#index */ pv.parent = function() { return this.parent.index; }; /** * Stores the current event. This field is only set within event handlers. * * @type Event * @name pv.event */ /** * @private Returns a prototype object suitable for extending the given class * f. Rather than constructing a new instance of f to serve as * the prototype (which unnecessarily runs the constructor on the created * prototype object, potentially polluting it), an anonymous function is * generated internally that shares the same prototype: * *
    function g() {}
     * g.prototype = f.prototype;
     * return new g();
    * * For more details, see Douglas Crockford's essay on prototypal inheritance. * * @param {function} f a constructor. * @returns a suitable prototype object. * @see Douglas Crockford's essay on prototypal * inheritance. */ pv.extend = function(f) { function g() {} g.prototype = f.prototype || f; return new g(); }; try { eval("pv.parse = function(x) x;"); // native support } catch (e) { /** * @private Parses a Protovis specification, which may use JavaScript 1.8 * function expresses, replacing those function expressions with proper * functions such that the code can be run by a JavaScript 1.6 interpreter. This * hack only supports function expressions (using clumsy regular expressions, no * less), and not other JavaScript 1.8 features such as let expressions. * * @param {string} s a Protovis specification (i.e., a string of JavaScript 1.8 * source code). * @returns {string} a conformant JavaScript 1.6 source code. */ pv.parse = function(js) { // hacky regex support var re = new RegExp("function\\s*(\\b\\w+)?\\s*\\([^)]*\\)\\s*", "mg"), m, d, i = 0, s = ""; while (m = re.exec(js)) { var j = m.index + m[0].length; if (js.charAt(j) != '{') { s += js.substring(i, j) + "{return "; i = j; for (var p = 0; p >= 0 && j < js.length; j++) { var c = js.charAt(j); switch (c) { case '"': case '\'': { while (++j < js.length && (d = js.charAt(j)) != c) { if (d == '\\') j++; } break; } case '[': case '(': p++; break; case ']': case ')': p--; break; case ';': case ',': if (p == 0) p--; break; } } s += pv.parse(js.substring(i, --j)) + ";}"; i = j; } re.lastIndex = j; } s += js.substring(i); return s; }; } /** * @private Computes the value of the specified CSS property p on the * specified element e. * * @param {string} p the name of the CSS property. * @param e the element on which to compute the CSS property. */ pv.css = function(e, p) { return window.getComputedStyle ? window.getComputedStyle(e, null).getPropertyValue(p) : e.currentStyle[p]; }; /** * @private Reports the specified error to the JavaScript console. Mozilla only * allows logging to the console for privileged code; if the console is * unavailable, the alert dialog box is used instead. * * @param e the exception that triggered the error. */ pv.error = function(e) { (typeof console == "undefined") ? alert(e) : console.error(e); }; /** * @private Registers the specified listener for events of the specified type on * the specified target. For standards-compliant browsers, this method uses * addEventListener; for Internet Explorer, attachEvent. * * @param target a DOM element. * @param {string} type the type of event, such as "click". * @param {function} the event handler callback. */ pv.listen = function(target, type, listener) { listener = pv.listener(listener); return target.addEventListener ? target.addEventListener(type, listener, false) : target.attachEvent("on" + type, listener); }; /** * @private Returns a wrapper for the specified listener function such that the * {@link pv.event} is set for the duration of the listener's invocation. The * wrapper is cached on the returned function, such that duplicate registrations * of the wrapped event handler are ignored. * * @param {function} f an event handler. * @returns {function} the wrapped event handler. */ pv.listener = function(f) { return f.$listener || (f.$listener = function(e) { try { pv.event = e; return f.call(this, e); } finally { delete pv.event; } }); }; /** * @private Returns true iff a is an ancestor of e. This is useful * for ignoring mouseout and mouseover events that are contained within the * target element. */ pv.ancestor = function(a, e) { while (e) { if (e == a) return true; e = e.parentNode; } return false; }; /** @private Returns a locally-unique positive id. */ pv.id = function() { var id = 1; return function() { return id++; }; }(); /** @private Returns a function wrapping the specified constant. */ pv.functor = function(v) { return typeof v == "function" ? v : function() { return v; }; }; /* * Parses the Protovis specifications on load, allowing the use of JavaScript * 1.8 function expressions on browsers that only support JavaScript 1.6. * * @see pv.parse */ pv.listen(window, "load", function() { /* * Note: in Firefox any variables declared here are visible to the eval'd * script below. Even worse, any global variables declared by the script * could overwrite local variables here (such as the index, `i`)! To protect * against this, all variables are explicitly scoped on a pv.$ object. */ pv.$ = {i:0, x:document.getElementsByTagName("script")}; for (; pv.$.i < pv.$.x.length; pv.$.i++) { pv.$.s = pv.$.x[pv.$.i]; if (pv.$.s.type == "text/javascript+protovis") { try { window.eval(pv.parse(pv.$.s.text)); } catch (e) { pv.error(e); } } } delete pv.$; }); /** * Abstract; see an implementing class. * * @class Represents an abstract text formatter and parser. A format is a * function that converts an object of a given type, such as a Date, to * a human-readable string representation. The format may also have a * {@link #parse} method for converting a string representation back to the * given object type. * *

    Because formats are themselves functions, they can be used directly as * mark properties. For example, if the data associated with a label are dates, * a date format can be used as label text: * *

        .text(pv.Format.date("%m/%d/%y"))
    * * And as with scales, if the format is used in multiple places, it can be * convenient to declare it as a global variable and then reference it from the * appropriate property functions. For example, if the data has a date * attribute, and format references a given date format: * *
        .text(function(d) format(d.date))
    * * Similarly, to parse a string into a date: * *
    var date = format.parse("4/30/2010");
    * * Not all format implementations support parsing. See the implementing class * for details. * * @see pv.Format.date * @see pv.Format.number * @see pv.Format.time */ pv.Format = {}; /** * Formats the specified object, returning the string representation. * * @function * @name pv.Format.prototype.format * @param {object} x the object to format. * @returns {string} the formatted string. */ /** * Parses the specified string, returning the object representation. * * @function * @name pv.Format.prototype.parse * @param {string} x the string to parse. * @returns {object} the parsed object. */ /** * @private Given a string that may be used as part of a regular expression, * this methods returns an appropriately quoted version of the specified string, * with any special characters escaped. * * @param {string} s a string to quote. * @returns {string} the quoted string. */ pv.Format.re = function(s) { return s.replace(/[\\\^\$\*\+\?\[\]\(\)\.\{\}]/g, "\\$&"); }; /** * @private Optionally pads the specified string s so that it is at least * n characters long, using the padding character c. * * @param {string} c the padding character. * @param {number} n the minimum string length. * @param {string} s the string to pad. * @returns {string} the padded string. */ pv.Format.pad = function(c, n, s) { var m = n - String(s).length; return (m < 1) ? s : new Array(m + 1).join(c) + s; }; /** * Constructs a new date format with the specified string pattern. * * @class The format string is in the same format expected by the * strftime function in C. The following conversion specifications are * supported:
      * *
    • %a - abbreviated weekday name.
    • *
    • %A - full weekday name.
    • *
    • %b - abbreviated month names.
    • *
    • %B - full month names.
    • *
    • %c - locale's appropriate date and time.
    • *
    • %C - century number.
    • *
    • %d - day of month [01,31] (zero padded).
    • *
    • %D - same as %m/%d/%y.
    • *
    • %e - day of month [ 1,31] (space padded).
    • *
    • %h - same as %b.
    • *
    • %H - hour (24-hour clock) [00,23] (zero padded).
    • *
    • %I - hour (12-hour clock) [01,12] (zero padded).
    • *
    • %m - month number [01,12] (zero padded).
    • *
    • %M - minute [0,59] (zero padded).
    • *
    • %n - newline character.
    • *
    • %p - locale's equivalent of a.m. or p.m.
    • *
    • %r - same as %I:%M:%S %p.
    • *
    • %R - same as %H:%M.
    • *
    • %S - second [00,61] (zero padded).
    • *
    • %t - tab character.
    • *
    • %T - same as %H:%M:%S.
    • *
    • %x - same as %m/%d/%y.
    • *
    • %X - same as %I:%M:%S %p.
    • *
    • %y - year with century [00,99] (zero padded).
    • *
    • %Y - year including century.
    • *
    • %% - %.
    • * *
    The following conversion specifications are currently unsupported * for formatting:
      * *
    • %j - day number [1,366].
    • *
    • %u - weekday number [1,7].
    • *
    • %U - week number [00,53].
    • *
    • %V - week number [01,53].
    • *
    • %w - weekday number [0,6].
    • *
    • %W - week number [00,53].
    • *
    • %Z - timezone name or abbreviation.
    • * *
    In addition, the following conversion specifications are currently * unsupported for parsing:
      * *
    • %a - day of week, either abbreviated or full name.
    • *
    • %A - same as %a.
    • *
    • %c - locale's appropriate date and time.
    • *
    • %C - century number.
    • *
    • %D - same as %m/%d/%y.
    • *
    • %I - hour (12-hour clock) [1,12].
    • *
    • %n - any white space.
    • *
    • %p - locale's equivalent of a.m. or p.m.
    • *
    • %r - same as %I:%M:%S %p.
    • *
    • %R - same as %H:%M.
    • *
    • %t - same as %n.
    • *
    • %T - same as %H:%M:%S.
    • *
    • %x - locale's equivalent to %m/%d/%y.
    • *
    • %X - locale's equivalent to %I:%M:%S %p.
    • * *
    * * @see strftime * documentation. * @see strptime * documentation. * @extends pv.Format * @param {string} pattern the format pattern. */ pv.Format.date = function(pattern) { var pad = pv.Format.pad; /** @private */ function format(d) { return pattern.replace(/%[a-zA-Z0-9]/g, function(s) { switch (s) { case '%a': return [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ][d.getDay()]; case '%A': return [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ][d.getDay()]; case '%h': case '%b': return [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ][d.getMonth()]; case '%B': return [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ][d.getMonth()]; case '%c': return d.toLocaleString(); case '%C': return pad("0", 2, Math.floor(d.getFullYear() / 100) % 100); case '%d': return pad("0", 2, d.getDate()); case '%x': case '%D': return pad("0", 2, d.getMonth() + 1) + "/" + pad("0", 2, d.getDate()) + "/" + pad("0", 2, d.getFullYear() % 100); case '%e': return pad(" ", 2, d.getDate()); case '%H': return pad("0", 2, d.getHours()); case '%I': { var h = d.getHours() % 12; return h ? pad("0", 2, h) : 12; } // TODO %j: day of year as a decimal number [001,366] case '%m': return pad("0", 2, d.getMonth() + 1); case '%M': return pad("0", 2, d.getMinutes()); case '%n': return "\n"; case '%p': return d.getHours() < 12 ? "AM" : "PM"; case '%T': case '%X': case '%r': { var h = d.getHours() % 12; return (h ? pad("0", 2, h) : 12) + ":" + pad("0", 2, d.getMinutes()) + ":" + pad("0", 2, d.getSeconds()) + " " + (d.getHours() < 12 ? "AM" : "PM"); } case '%R': return pad("0", 2, d.getHours()) + ":" + pad("0", 2, d.getMinutes()); case '%S': return pad("0", 2, d.getSeconds()); case '%Q': return pad("0", 3, d.getMilliseconds()); case '%t': return "\t"; case '%u': { var w = d.getDay(); return w ? w : 1; } // TODO %U: week number (sunday first day) [00,53] // TODO %V: week number (monday first day) [01,53] ... with weirdness case '%w': return d.getDay(); // TODO %W: week number (monday first day) [00,53] ... with weirdness case '%y': return pad("0", 2, d.getFullYear() % 100); case '%Y': return d.getFullYear(); // TODO %Z: timezone name or abbreviation case '%%': return "%"; } return s; }); } /** * Converts a date to a string using the associated formatting pattern. * * @function * @name pv.Format.date.prototype.format * @param {Date} date a date to format. * @returns {string} the formatted date as a string. */ format.format = format; /** * Parses a date from a string using the associated formatting pattern. * * @function * @name pv.Format.date.prototype.parse * @param {string} s the string to parse as a date. * @returns {Date} the parsed date. */ format.parse = function(s) { var year = 1970, month = 0, date = 1, hour = 0, minute = 0, second = 0; var fields = [function() {}]; /* Register callbacks for each field in the format pattern. */ var re = pv.Format.re(pattern).replace(/%[a-zA-Z0-9]/g, function(s) { switch (s) { // TODO %a: day of week, either abbreviated or full name // TODO %A: same as %a case '%b': { fields.push(function(x) { month = { Jan: 0, Feb: 1, Mar: 2, Apr: 3, May: 4, Jun: 5, Jul: 6, Aug: 7, Sep: 8, Oct: 9, Nov: 10, Dec: 11 }[x]; }); return "([A-Za-z]+)"; } case '%h': case '%B': { fields.push(function(x) { month = { January: 0, February: 1, March: 2, April: 3, May: 4, June: 5, July: 6, August: 7, September: 8, October: 9, November: 10, December: 11 }[x]; }); return "([A-Za-z]+)"; } // TODO %c: locale's appropriate date and time // TODO %C: century number[0,99] case '%e': case '%d': { fields.push(function(x) { date = x; }); return "([0-9]+)"; } // TODO %D: same as %m/%d/%y case '%I': case '%H': { fields.push(function(x) { hour = x; }); return "([0-9]+)"; } // TODO %j: day number [1,366] case '%m': { fields.push(function(x) { month = x - 1; }); return "([0-9]+)"; } case '%M': { fields.push(function(x) { minute = x; }); return "([0-9]+)"; } // TODO %n: any white space // TODO %p: locale's equivalent of a.m. or p.m. case '%p': { // TODO this is a hack fields.push(function(x) { if (hour == 12) { if (x == "am") hour = 0; } else if (x == "pm") { hour = Number(hour) + 12; } }); return "(am|pm)"; } // TODO %r: %I:%M:%S %p // TODO %R: %H:%M case '%S': { fields.push(function(x) { second = x; }); return "([0-9]+)"; } // TODO %t: any white space // TODO %T: %H:%M:%S // TODO %U: week number [00,53] // TODO %w: weekday [0,6] // TODO %W: week number [00, 53] // TODO %x: locale date (%m/%d/%y) // TODO %X: locale time (%I:%M:%S %p) case '%y': { fields.push(function(x) { x = Number(x); year = x + (((0 <= x) && (x < 69)) ? 2000 : (((x >= 69) && (x < 100) ? 1900 : 0))); }); return "([0-9]+)"; } case '%Y': { fields.push(function(x) { year = x; }); return "([0-9]+)"; } case '%%': { fields.push(function() {}); return "%"; } } return s; }); var match = s.match(re); if (match) match.forEach(function(m, i) { fields[i](m); }); return new Date(year, month, date, hour, minute, second); }; return format; }; /** * Returns a time format of the given type, either "short" or "long". * * @class Represents a time format, converting between a number * representing a duration in milliseconds, and a string. Two types of * time formats are supported: "short" and "long". The short format type * returns a string such as "3.3 days" or "12.1 minutes", while the long * format returns "13:04:12" or similar. * * @extends pv.Format * @param {string} type the type; "short" or "long". */ pv.Format.time = function(type) { var pad = pv.Format.pad; /* * MILLISECONDS = 1 * SECONDS = 1e3 * MINUTES = 6e4 * HOURS = 36e5 * DAYS = 864e5 * WEEKS = 6048e5 * MONTHS = 2592e6 * YEARS = 31536e6 */ /** @private */ function format(t) { t = Number(t); // force conversion from Date switch (type) { case "short": { if (t >= 31536e6) { return (t / 31536e6).toFixed(1) + " years"; } else if (t >= 6048e5) { return (t / 6048e5).toFixed(1) + " weeks"; } else if (t >= 864e5) { return (t / 864e5).toFixed(1) + " days"; } else if (t >= 36e5) { return (t / 36e5).toFixed(1) + " hours"; } else if (t >= 6e4) { return (t / 6e4).toFixed(1) + " minutes"; } return (t / 1e3).toFixed(1) + " seconds"; } case "long": { var a = [], s = ((t % 6e4) / 1e3) >> 0, m = ((t % 36e5) / 6e4) >> 0; a.push(pad("0", 2, s)); if (t >= 36e5) { var h = ((t % 864e5) / 36e5) >> 0; a.push(pad("0", 2, m)); if (t >= 864e5) { a.push(pad("0", 2, h)); a.push(Math.floor(t / 864e5).toFixed()); } else { a.push(h.toFixed()); } } else { a.push(m.toFixed()); } return a.reverse().join(":"); } } } /** * Formats the specified time, returning the string representation. * * @function * @name pv.Format.time.prototype.format * @param {number} t the duration in milliseconds. May also be a Date. * @returns {string} the formatted string. */ format.format = format; /** * Parses the specified string, returning the time in milliseconds. * * @function * @name pv.Format.time.prototype.parse * @param {string} s a formatted string. * @returns {number} the parsed duration in milliseconds. */ format.parse = function(s) { switch (type) { case "short": { var re = /([0-9,.]+)\s*([a-z]+)/g, a, t = 0; while (a = re.exec(s)) { var f = parseFloat(a[0].replace(",", "")), u = 0; switch (a[2].toLowerCase()) { case "year": case "years": u = 31536e6; break; case "week": case "weeks": u = 6048e5; break; case "day": case "days": u = 864e5; break; case "hour": case "hours": u = 36e5; break; case "minute": case "minutes": u = 6e4; break; case "second": case "seconds": u = 1e3; break; } t += f * u; } return t; } case "long": { var a = s.replace(",", "").split(":").reverse(), t = 0; if (a.length) t += parseFloat(a[0]) * 1e3; if (a.length > 1) t += parseFloat(a[1]) * 6e4; if (a.length > 2) t += parseFloat(a[2]) * 36e5; if (a.length > 3) t += parseFloat(a[3]) * 864e5; return t; } } } return format; }; /** * Returns a default number format. * * @class Represents a number format, converting between a number and a * string. This class allows numbers to be formatted with variable * precision (both for the integral and fractional part of the number), optional * thousands grouping, and optional padding. The thousands (",") and decimal * (".") separator can be customized. * * @returns {pv.Format.number} a number format. */ pv.Format.number = function() { var mini = 0, // default minimum integer digits maxi = Infinity, // default maximum integer digits mins = 0, // mini, including group separators minf = 0, // default minimum fraction digits maxf = 0, // default maximum fraction digits maxk = 1, // 10^maxf padi = "0", // default integer pad padf = "0", // default fraction pad padg = true, // whether group separator affects integer padding decimal = ".", // default decimal separator group = ","; // default group separator /** @private */ function format(x) { /* Round the fractional part, and split on decimal separator. */ if (Infinity > maxf) x = Math.round(x * maxk) / maxk; var s = String(Math.abs(x)).split("."); /* Pad, truncate and group the integral part. */ var i = s[0], n = (x < 0) ? "-" : ""; if (i.length > maxi) i = i.substring(i.length - maxi); if (padg && (i.length < mini)) i = n + new Array(mini - i.length + 1).join(padi) + i; if (i.length > 3) i = i.replace(/\B(?=(?:\d{3})+(?!\d))/g, group); if (!padg && (i.length < mins)) i = new Array(mins - i.length + 1).join(padi) + n + i; s[0] = i; /* Pad the fractional part. */ var f = s[1] || ""; if (f.length < minf) s[1] = f + new Array(minf - f.length + 1).join(padf); return s.join(decimal); } /** * @function * @name pv.Format.number.prototype.format * @param {number} x * @returns {string} */ format.format = format; /** * Parses the specified string as a number. Before parsing, leading and * trailing padding is removed. Group separators are also removed, and the * decimal separator is replaced with the standard point ("."). The integer * part is truncated per the maximum integer digits, and the fraction part is * rounded per the maximum fraction digits. * * @function * @name pv.Format.number.prototype.parse * @param {string} x the string to parse. * @returns {number} the parsed number. */ format.parse = function(x) { var re = pv.Format.re; /* Remove leading and trailing padding. Split on the decimal separator. */ var s = String(x) .replace(new RegExp("^(" + re(padi) + ")*"), "") .replace(new RegExp("(" + re(padf) + ")*$"), "") .split(decimal); /* Remove grouping and truncate the integral part. */ var i = s[0].replace(new RegExp(re(group), "g"), ""); if (i.length > maxi) i = i.substring(i.length - maxi); /* Round the fractional part. */ var f = s[1] ? Number("0." + s[1]) : 0; if (Infinity > maxf) f = Math.round(f * maxk) / maxk; return Math.round(i) + f; }; /** * Sets or gets the minimum and maximum number of integer digits. This * controls the number of decimal digits to display before the decimal * separator for the integral part of the number. If the number of digits is * smaller than the minimum, the digits are padded; if the number of digits is * larger, the digits are truncated, showing only the lower-order digits. The * default range is [0, Infinity]. * *

    If only one argument is specified to this method, this value is used as * both the minimum and maximum number. If no arguments are specified, a * two-element array is returned containing the minimum and the maximum. * * @function * @name pv.Format.number.prototype.integerDigits * @param {number} [min] the minimum integer digits. * @param {number} [max] the maximum integer digits. * @returns {pv.Format.number} this, or the current integer digits. */ format.integerDigits = function(min, max) { if (arguments.length) { mini = Number(min); maxi = (arguments.length > 1) ? Number(max) : mini; mins = mini + Math.floor(mini / 3) * group.length; return this; } return [mini, maxi]; }; /** * Sets or gets the minimum and maximum number of fraction digits. The * controls the number of decimal digits to display after the decimal * separator for the fractional part of the number. If the number of digits is * smaller than the minimum, the digits are padded; if the number of digits is * larger, the fractional part is rounded, showing only the higher-order * digits. The default range is [0, 0]. * *

    If only one argument is specified to this method, this value is used as * both the minimum and maximum number. If no arguments are specified, a * two-element array is returned containing the minimum and the maximum. * * @function * @name pv.Format.number.prototype.fractionDigits * @param {number} [min] the minimum fraction digits. * @param {number} [max] the maximum fraction digits. * @returns {pv.Format.number} this, or the current fraction digits. */ format.fractionDigits = function(min, max) { if (arguments.length) { minf = Number(min); maxf = (arguments.length > 1) ? Number(max) : minf; maxk = Math.pow(10, maxf); return this; } return [minf, maxf]; }; /** * Sets or gets the character used to pad the integer part. The integer pad is * used when the number of integer digits is smaller than the minimum. The * default pad character is "0" (zero). * * @param {string} [x] the new pad character. * @returns {pv.Format.number} this or the current pad character. */ format.integerPad = function(x) { if (arguments.length) { padi = String(x); padg = /\d/.test(padi); return this; } return padi; }; /** * Sets or gets the character used to pad the fration part. The fraction pad * is used when the number of fraction digits is smaller than the minimum. The * default pad character is "0" (zero). * * @param {string} [x] the new pad character. * @returns {pv.Format.number} this or the current pad character. */ format.fractionPad = function(x) { if (arguments.length) { padf = String(x); return this; } return padf; }; /** * Sets or gets the character used as the decimal point, separating the * integer and fraction parts of the number. The default decimal point is ".". * * @param {string} [x] the new decimal separator. * @returns {pv.Format.number} this or the current decimal separator. */ format.decimal = function(x) { if (arguments.length) { decimal = String(x); return this; } return decimal; }; /** * Sets or gets the character used as the group separator, grouping integer * digits by thousands. The default decimal point is ",". Grouping can be * disabled by using "" for the separator. * * @param {string} [x] the new group separator. * @returns {pv.Format.number} this or the current group separator. */ format.group = function(x) { if (arguments.length) { group = x ? String(x) : ""; mins = mini + Math.floor(mini / 3) * group.length; return this; } return group; }; return format; }; /** * @private A private variant of Array.prototype.map that supports the index * property. */ pv.map = function(array, f) { var o = {}; return f ? array.map(function(d, i) { o.index = i; return f.call(o, d); }) : array.slice(); }; /** * Concatenates the specified array with itself n times. For example, * pv.repeat([1, 2]) returns [1, 2, 1, 2]. * * @param {array} a an array. * @param {number} [n] the number of times to repeat; defaults to two. * @returns {array} an array that repeats the specified array. */ pv.repeat = function(array, n) { if (arguments.length == 1) n = 2; return pv.blend(pv.range(n).map(function() { return array; })); }; /** * Given two arrays a and b, returns an array of all possible * pairs of elements [ai, bj]. The outer loop is on array * a, while the inner loop is on b, such that the order of * returned elements is [a0, b0], [a0, * b1], ... [a0, bm], [a1, * b0], [a1, b1], ... [a1, * bm], ... [an, bm]. If either array is empty, * an empty array is returned. * * @param {array} a an array. * @param {array} b an array. * @returns {array} an array of pairs of elements in a and b. */ pv.cross = function(a, b) { var array = []; for (var i = 0, n = a.length, m = b.length; i < n; i++) { for (var j = 0, x = a[i]; j < m; j++) { array.push([x, b[j]]); } } return array; }; /** * Given the specified array of arrays, concatenates the arrays into a single * array. If the individual arrays are explicitly known, an alternative to blend * is to use JavaScript's concat method directly. These two equivalent * expressions:

      * *
    • pv.blend([[1, 2, 3], ["a", "b", "c"]]) *
    • [1, 2, 3].concat(["a", "b", "c"]) * *
    return [1, 2, 3, "a", "b", "c"]. * * @param {array[]} arrays an array of arrays. * @returns {array} an array containing all the elements of each array in * arrays. */ pv.blend = function(arrays) { return Array.prototype.concat.apply([], arrays); }; /** * Given the specified array of arrays, transposes each element * arrayij with arrayji. If the array has dimensions * n×m, it will have dimensions m×n * after this method returns. This method transposes the elements of the array * in place, mutating the array, and returning a reference to the array. * * @param {array[]} arrays an array of arrays. * @returns {array[]} the passed-in array, after transposing the elements. */ pv.transpose = function(arrays) { var n = arrays.length, m = pv.max(arrays, function(d) { return d.length; }); if (m > n) { arrays.length = m; for (var i = n; i < m; i++) { arrays[i] = new Array(n); } for (var i = 0; i < n; i++) { for (var j = i + 1; j < m; j++) { var t = arrays[i][j]; arrays[i][j] = arrays[j][i]; arrays[j][i] = t; } } } else { for (var i = 0; i < m; i++) { arrays[i].length = n; } for (var i = 0; i < n; i++) { for (var j = 0; j < i; j++) { var t = arrays[i][j]; arrays[i][j] = arrays[j][i]; arrays[j][i] = t; } } } arrays.length = m; for (var i = 0; i < m; i++) { arrays[i].length = n; } return arrays; }; /** * Returns a normalized copy of the specified array, such that the sum of the * returned elements sum to one. If the specified array is not an array of * numbers, an optional accessor function f can be specified to map the * elements to numbers. For example, if array is an array of objects, * and each object has a numeric property "foo", the expression * *
    pv.normalize(array, function(d) d.foo)
    * * returns a normalized array on the "foo" property. If an accessor function is * not specified, the identity function is used. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number[]} an array of numbers that sums to one. */ pv.normalize = function(array, f) { var norm = pv.map(array, f), sum = pv.sum(norm); for (var i = 0; i < norm.length; i++) norm[i] /= sum; return norm; }; /** * Returns a permutation of the specified array, using the specified array of * indexes. The returned array contains the corresponding element in * array for each index in indexes, in order. For example, * *
    pv.permute(["a", "b", "c"], [1, 2, 0])
    * * returns ["b", "c", "a"]. It is acceptable for the array of indexes * to be a different length from the array of elements, and for indexes to be * duplicated or omitted. The optional accessor function f can be used * to perform a simultaneous mapping of the array elements. Accessor functions * can refer to this.index. * * @param {array} array an array. * @param {number[]} indexes an array of indexes into array. * @param {function} [f] an optional accessor function. * @returns {array} an array of elements from array; a permutation. */ pv.permute = function(array, indexes, f) { if (!f) f = pv.identity; var p = new Array(indexes.length), o = {}; indexes.forEach(function(j, i) { o.index = j; p[i] = f.call(o, array[j]); }); return p; }; /** * Returns a map from key to index for the specified keys array. For * example, * *
    pv.numerate(["a", "b", "c"])
    * * returns {a: 0, b: 1, c: 2}. Note that since JavaScript maps only * support string keys, keys must contain strings, or other values that * naturally map to distinct string values. Alternatively, an optional accessor * function f can be specified to compute the string key for the given * element. Accessor functions can refer to this.index. * * @param {array} keys an array, usually of string keys. * @param {function} [f] an optional key function. * @returns a map from key to index. */ pv.numerate = function(keys, f) { if (!f) f = pv.identity; var map = {}, o = {}; keys.forEach(function(x, i) { o.index = i; map[f.call(o, x)] = i; }); return map; }; /** * Returns the unique elements in the specified array, in the order they appear. * Note that since JavaScript maps only support string keys, array must * contain strings, or other values that naturally map to distinct string * values. Alternatively, an optional accessor function f can be * specified to compute the string key for the given element. Accessor functions * can refer to this.index. * * @param {array} array an array, usually of string keys. * @param {function} [f] an optional key function. * @returns {array} the unique values. */ pv.uniq = function(array, f) { if (!f) f = pv.identity; var map = {}, keys = [], o = {}, y; array.forEach(function(x, i) { o.index = i; y = f.call(o, x); if (!(y in map)) map[y] = keys.push(y); }); return keys; }; /** * The comparator function for natural order. This can be used in conjunction with * the built-in array sort method to sort elements by their natural * order, ascending. Note that if no comparator function is specified to the * built-in sort method, the default order is lexicographic, not * natural! * * @see Array.sort. * @param a an element to compare. * @param b an element to compare. * @returns {number} negative if a < b; positive if a > b; otherwise 0. */ pv.naturalOrder = function(a, b) { return (a < b) ? -1 : ((a > b) ? 1 : 0); }; /** * The comparator function for reverse natural order. This can be used in * conjunction with the built-in array sort method to sort elements by * their natural order, descending. Note that if no comparator function is * specified to the built-in sort method, the default order is * lexicographic, not natural! * * @see #naturalOrder * @param a an element to compare. * @param b an element to compare. * @returns {number} negative if a < b; positive if a > b; otherwise 0. */ pv.reverseOrder = function(b, a) { return (a < b) ? -1 : ((a > b) ? 1 : 0); }; /** * Searches the specified array of numbers for the specified value using the * binary search algorithm. The array must be sorted (as by the sort * method) prior to making this call. If it is not sorted, the results are * undefined. If the array contains multiple elements with the specified value, * there is no guarantee which one will be found. * *

    The insertion point is defined as the point at which the value * would be inserted into the array: the index of the first element greater than * the value, or array.length, if all elements in the array are less * than the specified value. Note that this guarantees that the return value * will be nonnegative if and only if the value is found. * * @param {number[]} array the array to be searched. * @param {number} value the value to be searched for. * @returns the index of the search value, if it is contained in the array; * otherwise, (-(insertion point) - 1). * @param {function} [f] an optional key function. */ pv.search = function(array, value, f) { if (!f) f = pv.identity; var low = 0, high = array.length - 1; while (low <= high) { var mid = (low + high) >> 1, midValue = f(array[mid]); if (midValue < value) low = mid + 1; else if (midValue > value) high = mid - 1; else return mid; } return -low - 1; }; pv.search.index = function(array, value, f) { var i = pv.search(array, value, f); return (i < 0) ? (-i - 1) : i; }; /** * Returns an array of numbers, starting at start, incrementing by * step, until stop is reached. The stop value is * exclusive. If only a single argument is specified, this value is interpeted * as the stop value, with the start value as zero. If only two * arguments are specified, the step value is implied to be one. * *

    The method is modeled after the built-in range method from * Python. See the Python documentation for more details. * * @see Python range * @param {number} [start] the start value. * @param {number} stop the stop value. * @param {number} [step] the step value. * @returns {number[]} an array of numbers. */ pv.range = function(start, stop, step) { if (arguments.length == 1) { stop = start; start = 0; } if (step == undefined) step = 1; if ((stop - start) / step == Infinity) throw new Error("range must be finite"); var array = [], i = 0, j; if (step < 0) { while ((j = start + step * i++) > stop) { array.push(j); } } else { while ((j = start + step * i++) < stop) { array.push(j); } } return array; }; /** * Returns a random number in the range [start, stop) that is * a multiple of step. More specifically, the returned number is of the * form start + n * step, where n is a * nonnegative integer. If step is not specified, it defaults to 1, * returning a random integer if start is also an integer. * * @param {number} [start] the start value value. * @param {number} stop the stop value. * @param {number} [step] the step value. * @returns {number} a random number between start and stop. */ pv.random = function(start, stop, step) { if (arguments.length == 1) { stop = start; start = 0; } if (step == undefined) step = 1; return step ? (Math.floor(Math.random() * (stop - start) / step) * step + start) : (Math.random() * (stop - start) + start); }; /** * Returns the sum of the specified array. If the specified array is not an * array of numbers, an optional accessor function f can be specified * to map the elements to numbers. See {@link #normalize} for an example. * Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the sum of the specified array. */ pv.sum = function(array, f) { var o = {}; return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0); }; /** * Returns the maximum value of the specified array. If the specified array is * not an array of numbers, an optional accessor function f can be * specified to map the elements to numbers. See {@link #normalize} for an * example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the maximum value of the specified array. */ pv.max = function(array, f) { if (f == pv.index) return array.length - 1; return Math.max.apply(null, f ? pv.map(array, f) : array); }; /** * Returns the index of the maximum value of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the index of the maximum value of the specified array. */ pv.max.index = function(array, f) { if (!array.length) return -1; if (f == pv.index) return array.length - 1; if (!f) f = pv.identity; var maxi = 0, maxx = -Infinity, o = {}; for (var i = 0; i < array.length; i++) { o.index = i; var x = f.call(o, array[i]); if (x > maxx) { maxx = x; maxi = i; } } return maxi; } /** * Returns the minimum value of the specified array of numbers. If the specified * array is not an array of numbers, an optional accessor function f * can be specified to map the elements to numbers. See {@link #normalize} for * an example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the minimum value of the specified array. */ pv.min = function(array, f) { if (f == pv.index) return 0; return Math.min.apply(null, f ? pv.map(array, f) : array); }; /** * Returns the index of the minimum value of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the index of the minimum value of the specified array. */ pv.min.index = function(array, f) { if (!array.length) return -1; if (f == pv.index) return 0; if (!f) f = pv.identity; var mini = 0, minx = Infinity, o = {}; for (var i = 0; i < array.length; i++) { o.index = i; var x = f.call(o, array[i]); if (x < minx) { minx = x; mini = i; } } return mini; } /** * Returns the arithmetic mean, or average, of the specified array. If the * specified array is not an array of numbers, an optional accessor function * f can be specified to map the elements to numbers. See * {@link #normalize} for an example. Accessor functions can refer to * this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the mean of the specified array. */ pv.mean = function(array, f) { return pv.sum(array, f) / array.length; }; /** * Returns the median of the specified array. If the specified array is not an * array of numbers, an optional accessor function f can be specified * to map the elements to numbers. See {@link #normalize} for an example. * Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the median of the specified array. */ pv.median = function(array, f) { if (f == pv.index) return (array.length - 1) / 2; array = pv.map(array, f).sort(pv.naturalOrder); if (array.length % 2) return array[Math.floor(array.length / 2)]; var i = array.length / 2; return (array[i - 1] + array[i]) / 2; }; /** * Returns the unweighted variance of the specified array. If the specified * array is not an array of numbers, an optional accessor function f * can be specified to map the elements to numbers. See {@link #normalize} for * an example. Accessor functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the variance of the specified array. */ pv.variance = function(array, f) { if (array.length < 1) return NaN; if (array.length == 1) return 0; var mean = pv.mean(array, f), sum = 0, o = {}; if (!f) f = pv.identity; for (var i = 0; i < array.length; i++) { o.index = i; var d = f.call(o, array[i]) - mean; sum += d * d; } return sum; }; /** * Returns an unbiased estimation of the standard deviation of a population, * given the specified random sample. If the specified array is not an array of * numbers, an optional accessor function f can be specified to map the * elements to numbers. See {@link #normalize} for an example. Accessor * functions can refer to this.index. * * @param {array} array an array of objects, or numbers. * @param {function} [f] an optional accessor function. * @returns {number} the standard deviation of the specified array. */ pv.deviation = function(array, f) { return Math.sqrt(pv.variance(array, f) / (array.length - 1)); }; /** * Returns the logarithm with a given base value. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the logarithm value. */ pv.log = function(x, b) { return Math.log(x) / Math.log(b); }; /** * Computes a zero-symmetric logarithm. Computes the logarithm of the absolute * value of the input, and determines the sign of the output according to the * sign of the input value. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the symmetric log value. */ pv.logSymmetric = function(x, b) { return (x == 0) ? 0 : ((x < 0) ? -pv.log(-x, b) : pv.log(x, b)); }; /** * Computes a zero-symmetric logarithm, with adjustment to values between zero * and the logarithm base. This adjustment introduces distortion for values less * than the base number, but enables simultaneous plotting of log-transformed * data involving both positive and negative numbers. * * @param {number} x the number for which to compute the logarithm. * @param {number} b the base of the logarithm. * @returns {number} the adjusted, symmetric log value. */ pv.logAdjusted = function(x, b) { if (!isFinite(x)) return x; var negative = x < 0; if (x < b) x += (b - x) / b; return negative ? -pv.log(x, b) : pv.log(x, b); }; /** * Rounds an input value down according to its logarithm. The method takes the * floor of the logarithm of the value and then uses the resulting value as an * exponent for the base value. * * @param {number} x the number for which to compute the logarithm floor. * @param {number} b the base of the logarithm. * @returns {number} the rounded-by-logarithm value. */ pv.logFloor = function(x, b) { return (x > 0) ? Math.pow(b, Math.floor(pv.log(x, b))) : -Math.pow(b, -Math.floor(-pv.log(-x, b))); }; /** * Rounds an input value up according to its logarithm. The method takes the * ceiling of the logarithm of the value and then uses the resulting value as an * exponent for the base value. * * @param {number} x the number for which to compute the logarithm ceiling. * @param {number} b the base of the logarithm. * @returns {number} the rounded-by-logarithm value. */ pv.logCeil = function(x, b) { return (x > 0) ? Math.pow(b, Math.ceil(pv.log(x, b))) : -Math.pow(b, -Math.ceil(-pv.log(-x, b))); }; (function() { var radians = Math.PI / 180, degrees = 180 / Math.PI; /** Returns the number of radians corresponding to the specified degrees. */ pv.radians = function(degrees) { return radians * degrees; }; /** Returns the number of degrees corresponding to the specified radians. */ pv.degrees = function(radians) { return degrees * radians; }; })(); /** * Returns all of the property names (keys) of the specified object (a map). The * order of the returned array is not defined. * * @param map an object. * @returns {string[]} an array of strings corresponding to the keys. * @see #entries */ pv.keys = function(map) { var array = []; for (var key in map) { array.push(key); } return array; }; /** * Returns all of the entries (key-value pairs) of the specified object (a * map). The order of the returned array is not defined. Each key-value pair is * represented as an object with key and value attributes, * e.g., {key: "foo", value: 42}. * * @param map an object. * @returns {array} an array of key-value pairs corresponding to the keys. */ pv.entries = function(map) { var array = []; for (var key in map) { array.push({ key: key, value: map[key] }); } return array; }; /** * Returns all of the values (attribute values) of the specified object (a * map). The order of the returned array is not defined. * * @param map an object. * @returns {array} an array of objects corresponding to the values. * @see #entries */ pv.values = function(map) { var array = []; for (var key in map) { array.push(map[key]); } return array; }; /** * Returns a map constructed from the specified keys, using the * function f to compute the value for each key. The single argument to * the value function is the key. The callback is invoked only for indexes of * the array which have assigned values; it is not invoked for indexes which * have been deleted or which have never been assigned values. * *

    For example, this expression creates a map from strings to string length: * *

    pv.dict(["one", "three", "seventeen"], function(s) s.length)
    * * The returned value is {one: 3, three: 5, seventeen: 9}. Accessor * functions can refer to this.index. * * @param {array} keys an array. * @param {function} f a value function. * @returns a map from keys to values. */ pv.dict = function(keys, f) { var m = {}, o = {}; for (var i = 0; i < keys.length; i++) { if (i in keys) { var k = keys[i]; o.index = i; m[k] = f.call(o, k); } } return m; }; /** * Returns a {@link pv.Dom} operator for the given map. This is a convenience * factory method, equivalent to new pv.Dom(map). To apply the operator * and retrieve the root node, call {@link pv.Dom#root}; to retrieve all nodes * flattened, use {@link pv.Dom#nodes}. * * @see pv.Dom * @param map a map from which to construct a DOM. * @returns {pv.Dom} a DOM operator for the specified map. */ pv.dom = function(map) { return new pv.Dom(map); }; /** * Constructs a DOM operator for the specified map. This constructor should not * be invoked directly; use {@link pv.dom} instead. * * @class Represets a DOM operator for the specified map. This allows easy * transformation of a hierarchical JavaScript object (such as a JSON map) to a * W3C Document Object Model hierarchy. For more information on which attributes * and methods from the specification are supported, see {@link pv.Dom.Node}. * *

    Leaves in the map are determined using an associated leaf function; * see {@link #leaf}. By default, leaves are any value whose type is not * "object", such as numbers or strings. * * @param map a map from which to construct a DOM. */ pv.Dom = function(map) { this.$map = map; }; /** @private The default leaf function. */ pv.Dom.prototype.$leaf = function(n) { return typeof n != "object"; }; /** * Sets or gets the leaf function for this DOM operator. The leaf function * identifies which values in the map are leaves, and which are internal nodes. * By default, objects are considered internal nodes, and primitives (such as * numbers and strings) are considered leaves. * * @param {function} f the new leaf function. * @returns the current leaf function, or this. */ pv.Dom.prototype.leaf = function(f) { if (arguments.length) { this.$leaf = f; return this; } return this.$leaf; }; /** * Applies the DOM operator, returning the root node. * * @returns {pv.Dom.Node} the root node. * @param {string} [nodeName] optional node name for the root. */ pv.Dom.prototype.root = function(nodeName) { var leaf = this.$leaf, root = recurse(this.$map); /** @private */ function recurse(map) { var n = new pv.Dom.Node(); for (var k in map) { var v = map[k]; n.appendChild(leaf(v) ? new pv.Dom.Node(v) : recurse(v)).nodeName = k; } return n; } root.nodeName = nodeName; return root; }; /** * Applies the DOM operator, returning the array of all nodes in preorder * traversal. * * @returns {array} the array of nodes in preorder traversal. */ pv.Dom.prototype.nodes = function() { return this.root().nodes(); }; /** * Constructs a DOM node for the specified value. Instances of this class are * not typically created directly; instead they are generated from a JavaScript * map using the {@link pv.Dom} operator. * * @class Represents a Node in the W3C Document Object Model. */ pv.Dom.Node = function(value) { this.nodeValue = value; this.childNodes = []; }; /** * The node name. When generated from a map, the node name corresponds to the * key at the given level in the map. Note that the root node has no associated * key, and thus has an undefined node name (and no parentNode). * * @type string * @field pv.Dom.Node.prototype.nodeName */ /** * The node value. When generated from a map, node value corresponds to the leaf * value for leaf nodes, and is undefined for internal nodes. * * @field pv.Dom.Node.prototype.nodeValue */ /** * The array of child nodes. This array is empty for leaf nodes. An easy way to * check if child nodes exist is to query firstChild. * * @type array * @field pv.Dom.Node.prototype.childNodes */ /** * The parent node, which is null for root nodes. * * @type pv.Dom.Node */ pv.Dom.Node.prototype.parentNode = null; /** * The first child, which is null for leaf nodes. * * @type pv.Dom.Node */ pv.Dom.Node.prototype.firstChild = null; /** * The last child, which is null for leaf nodes. * * @type pv.Dom.Node */ pv.Dom.Node.prototype.lastChild = null; /** * The previous sibling node, which is null for the first child. * * @type pv.Dom.Node */ pv.Dom.Node.prototype.previousSibling = null; /** * The next sibling node, which is null for the last child. * * @type pv.Dom.Node */ pv.Dom.Node.prototype.nextSibling = null; /** * Removes the specified child node from this node. * * @throws Error if the specified child is not a child of this node. * @returns {pv.Dom.Node} the removed child. */ pv.Dom.Node.prototype.removeChild = function(n) { var i = this.childNodes.indexOf(n); if (i == -1) throw new Error("child not found"); this.childNodes.splice(i, 1); if (n.previousSibling) n.previousSibling.nextSibling = n.nextSibling; else this.firstChild = n.nextSibling; if (n.nextSibling) n.nextSibling.previousSibling = n.previousSibling; else this.lastChild = n.previousSibling; delete n.nextSibling; delete n.previousSibling; delete n.parentNode; return n; }; /** * Appends the specified child node to this node. If the specified child is * already part of the DOM, the child is first removed before being added to * this node. * * @returns {pv.Dom.Node} the appended child. */ pv.Dom.Node.prototype.appendChild = function(n) { if (n.parentNode) n.parentNode.removeChild(n); n.parentNode = this; n.previousSibling = this.lastChild; if (this.lastChild) this.lastChild.nextSibling = n; else this.firstChild = n; this.lastChild = n; this.childNodes.push(n); return n; }; /** * Inserts the specified child n before the given reference child * r of this node. If r is null, this method is equivalent to * {@link #appendChild}. If n is already part of the DOM, it is first * removed before being inserted. * * @throws Error if r is non-null and not a child of this node. * @returns {pv.Dom.Node} the inserted child. */ pv.Dom.Node.prototype.insertBefore = function(n, r) { if (!r) return this.appendChild(n); var i = this.childNodes.indexOf(r); if (i == -1) throw new Error("child not found"); if (n.parentNode) n.parentNode.removeChild(n); n.parentNode = this; n.nextSibling = r; n.previousSibling = r.previousSibling; if (r.previousSibling) { r.previousSibling.nextSibling = n; } else { if (r == this.lastChild) this.lastChild = n; this.firstChild = n; } this.childNodes.splice(i, 0, n); return n; }; /** * Replaces the specified child r of this node with the node n. If * n is already part of the DOM, it is first removed before being added. * * @throws Error if r is not a child of this node. */ pv.Dom.Node.prototype.replaceChild = function(n, r) { var i = this.childNodes.indexOf(r); if (i == -1) throw new Error("child not found"); if (n.parentNode) n.parentNode.removeChild(n); n.parentNode = this; n.nextSibling = r.nextSibling; n.previousSibling = r.previousSibling; if (r.previousSibling) r.previousSibling.nextSibling = n; else this.firstChild = n; if (r.nextSibling) r.nextSibling.previousSibling = n; else this.lastChild = n; this.childNodes[i] = n; return r; }; /** * Visits each node in the tree in preorder traversal, applying the specified * function f. The arguments to the function are:

      * *
    1. The current node. *
    2. The current depth, starting at 0 for the root node.
    * * @param {function} f a function to apply to each node. */ pv.Dom.Node.prototype.visitBefore = function(f) { function visit(n, i) { f(n, i); for (var c = n.firstChild; c; c = c.nextSibling) { visit(c, i + 1); } } visit(this, 0); }; /** * Visits each node in the tree in postorder traversal, applying the specified * function f. The arguments to the function are:
      * *
    1. The current node. *
    2. The current depth, starting at 0 for the root node.
    * * @param {function} f a function to apply to each node. */ pv.Dom.Node.prototype.visitAfter = function(f) { function visit(n, i) { for (var c = n.firstChild; c; c = c.nextSibling) { visit(c, i + 1); } f(n, i); } visit(this, 0); }; /** * Sorts child nodes of this node, and all descendent nodes recursively, using * the specified comparator function f. The comparator function is * passed two nodes to compare. * *

    Note: during the sort operation, the comparator function should not rely * on the tree being well-formed; the values of previousSibling and * nextSibling for the nodes being compared are not defined during the * sort operation. * * @param {function} f a comparator function. * @returns this. */ pv.Dom.Node.prototype.sort = function(f) { if (this.firstChild) { this.childNodes.sort(f); var p = this.firstChild = this.childNodes[0], c; delete p.previousSibling; for (var i = 1; i < this.childNodes.length; i++) { p.sort(f); c = this.childNodes[i]; c.previousSibling = p; p = p.nextSibling = c; } this.lastChild = p; delete p.nextSibling; p.sort(f); } return this; }; /** * Reverses all sibling nodes. * * @returns this. */ pv.Dom.Node.prototype.reverse = function() { var childNodes = []; this.visitAfter(function(n) { while (n.lastChild) childNodes.push(n.removeChild(n.lastChild)); for (var c; c = childNodes.pop();) n.insertBefore(c, n.firstChild); }); return this; }; /** Returns all descendants of this node in preorder traversal. */ pv.Dom.Node.prototype.nodes = function() { var array = []; /** @private */ function flatten(node) { array.push(node); node.childNodes.forEach(flatten); } flatten(this, array); return array; }; /** * Toggles the child nodes of this node. If this node is not yet toggled, this * method removes all child nodes and appends them to a new toggled * array attribute on this node. Otherwise, if this node is toggled, this method * re-adds all toggled child nodes and deletes the toggled attribute. * *

    This method has no effect if the node has no child nodes. * * @param {boolean} [recursive] whether the toggle should apply to descendants. */ pv.Dom.Node.prototype.toggle = function(recursive) { if (recursive) return this.toggled ? this.visitBefore(function(n) { if (n.toggled) n.toggle(); }) : this.visitAfter(function(n) { if (!n.toggled) n.toggle(); }); var n = this; if (n.toggled) { for (var c; c = n.toggled.pop();) n.appendChild(c); delete n.toggled; } else if (n.lastChild) { n.toggled = []; while (n.lastChild) n.toggled.push(n.removeChild(n.lastChild)); } }; /** * Given a flat array of values, returns a simple DOM with each value wrapped by * a node that is a child of the root node. * * @param {array} values. * @returns {array} nodes. */ pv.nodes = function(values) { var root = new pv.Dom.Node(); for (var i = 0; i < values.length; i++) { root.appendChild(new pv.Dom.Node(values[i])); } return root.nodes(); }; /** * Returns a {@link pv.Tree} operator for the specified array. This is a * convenience factory method, equivalent to new pv.Tree(array). * * @see pv.Tree * @param {array} array an array from which to construct a tree. * @returns {pv.Tree} a tree operator for the specified array. */ pv.tree = function(array) { return new pv.Tree(array); }; /** * Constructs a tree operator for the specified array. This constructor should * not be invoked directly; use {@link pv.tree} instead. * * @class Represents a tree operator for the specified array. The tree operator * allows a hierarchical map to be constructed from an array; it is similar to * the {@link pv.Nest} operator, except the hierarchy is derived dynamically * from the array elements. * *

    For example, given an array of size information for ActionScript classes: * *

    { name: "flare.flex.FlareVis", size: 4116 },
     * { name: "flare.physics.DragForce", size: 1082 },
     * { name: "flare.physics.GravityForce", size: 1336 }, ...
    * * To facilitate visualization, it may be useful to nest the elements by their * package hierarchy: * *
    var tree = pv.tree(classes)
     *     .keys(function(d) d.name.split("."))
     *     .map();
    * * The resulting tree is: * *
    { flare: {
     *     flex: {
     *       FlareVis: {
     *         name: "flare.flex.FlareVis",
     *         size: 4116 } },
     *     physics: {
     *       DragForce: {
     *         name: "flare.physics.DragForce",
     *         size: 1082 },
     *       GravityForce: {
     *         name: "flare.physics.GravityForce",
     *         size: 1336 } },
     *     ... } }
    * * By specifying a value function, * *
    var tree = pv.tree(classes)
     *     .keys(function(d) d.name.split("."))
     *     .value(function(d) d.size)
     *     .map();
    * * we can further eliminate redundant data: * *
    { flare: {
     *     flex: {
     *       FlareVis: 4116 },
     *     physics: {
     *       DragForce: 1082,
     *       GravityForce: 1336 },
     *   ... } }
    * * For visualizations with large data sets, performance improvements may be seen * by storing the data in a tree format, and then flattening it into an array at * runtime with {@link pv.Flatten}. * * @param {array} array an array from which to construct a tree. */ pv.Tree = function(array) { this.array = array; }; /** * Assigns a keys function to this operator; required. The keys function * returns an array of strings for each element in the associated * array; these keys determine how the elements are nested in the tree. The * returned keys should be unique for each element in the array; otherwise, the * behavior of this operator is undefined. * * @param {function} k the keys function. * @returns {pv.Tree} this. */ pv.Tree.prototype.keys = function(k) { this.k = k; return this; }; /** * Assigns a value function to this operator; optional. The value * function specifies an optional transformation of the element in the array * before it is inserted into the map. If no value function is specified, it is * equivalent to using the identity function. * * @param {function} k the value function. * @returns {pv.Tree} this. */ pv.Tree.prototype.value = function(v) { this.v = v; return this; }; /** * Returns a hierarchical map of values. The hierarchy is determined by the keys * function; the values in the map are determined by the value function. * * @returns a hierarchical map of values. */ pv.Tree.prototype.map = function() { var map = {}, o = {}; for (var i = 0; i < this.array.length; i++) { o.index = i; var value = this.array[i], keys = this.k.call(o, value), node = map; for (var j = 0; j < keys.length - 1; j++) { node = node[keys[j]] || (node[keys[j]] = {}); } node[keys[j]] = this.v ? this.v.call(o, value) : value; } return map; }; /** * Returns a {@link pv.Nest} operator for the specified array. This is a * convenience factory method, equivalent to new pv.Nest(array). * * @see pv.Nest * @param {array} array an array of elements to nest. * @returns {pv.Nest} a nest operator for the specified array. */ pv.nest = function(array) { return new pv.Nest(array); }; /** * Constructs a nest operator for the specified array. This constructor should * not be invoked directly; use {@link pv.nest} instead. * * @class Represents a {@link Nest} operator for the specified array. Nesting * allows elements in an array to be grouped into a hierarchical tree * structure. The levels in the tree are specified by key functions. The * leaf nodes of the tree can be sorted by value, while the internal nodes can * be sorted by key. Finally, the tree can be returned either has a * multidimensional array via {@link #entries}, or as a hierarchical map via * {@link #map}. The {@link #rollup} routine similarly returns a map, collapsing * the elements in each leaf node using a summary function. * *

    For example, consider the following tabular data structure of Barley * yields, from various sites in Minnesota during 1931-2: * *

    { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
     * { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
     * { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" }, ...
    * * To facilitate visualization, it may be useful to nest the elements first by * year, and then by variety, as follows: * *
    var nest = pv.nest(yields)
     *     .key(function(d) d.year)
     *     .key(function(d) d.variety)
     *     .entries();
    * * This returns a nested array. Each element of the outer array is a key-values * pair, listing the values for each distinct key: * *
    { key: 1931, values: [
     *   { key: "Manchuria", values: [
     *       { yield: 27.00, variety: "Manchuria", year: 1931, site: "University Farm" },
     *       { yield: 48.87, variety: "Manchuria", year: 1931, site: "Waseca" },
     *       { yield: 27.43, variety: "Manchuria", year: 1931, site: "Morris" },
     *       ...
     *     ] },
     *   { key: "Glabron", values: [
     *       { yield: 43.07, variety: "Glabron", year: 1931, site: "University Farm" },
     *       { yield: 55.20, variety: "Glabron", year: 1931, site: "Waseca" },
     *       ...
     *     ] },
     *   ] },
     * { key: 1932, values: ... }
    * * Further details, including sorting and rollup, is provided below on the * corresponding methods. * * @param {array} array an array of elements to nest. */ pv.Nest = function(array) { this.array = array; this.keys = []; }; /** * Nests using the specified key function. Multiple keys may be added to the * nest; the array elements will be nested in the order keys are specified. * * @param {function} key a key function; must return a string or suitable map * key. * @returns {pv.Nest} this. */ pv.Nest.prototype.key = function(key) { this.keys.push(key); return this; }; /** * Sorts the previously-added keys. The natural sort order is used by default * (see {@link pv.naturalOrder}); if an alternative order is desired, * order should be a comparator function. If this method is not called * (i.e., keys are unsorted), keys will appear in the order they appear * in the underlying elements array. For example, * *
    pv.nest(yields)
     *     .key(function(d) d.year)
     *     .key(function(d) d.variety)
     *     .sortKeys()
     *     .entries()
    * * groups yield data by year, then variety, and sorts the variety groups * lexicographically (since the variety attribute is a string). * *

    Key sort order is only used in conjunction with {@link #entries}, which * returns an array of key-values pairs. If the nest is used to construct a * {@link #map} instead, keys are unsorted. * * @param {function} [order] an optional comparator function. * @returns {pv.Nest} this. */ pv.Nest.prototype.sortKeys = function(order) { this.keys[this.keys.length - 1].order = order || pv.naturalOrder; return this; }; /** * Sorts the leaf values. The natural sort order is used by default (see * {@link pv.naturalOrder}); if an alternative order is desired, order * should be a comparator function. If this method is not called (i.e., values * are unsorted), values will appear in the order they appear in the * underlying elements array. For example, * *

    pv.nest(yields)
     *     .key(function(d) d.year)
     *     .key(function(d) d.variety)
     *     .sortValues(function(a, b) a.yield - b.yield)
     *     .entries()
    * * groups yield data by year, then variety, and sorts the values for each * variety group by yield. * *

    Value sort order, unlike keys, applies to both {@link #entries} and * {@link #map}. It has no effect on {@link #rollup}. * * @param {function} [order] an optional comparator function. * @returns {pv.Nest} this. */ pv.Nest.prototype.sortValues = function(order) { this.order = order || pv.naturalOrder; return this; }; /** * Returns a hierarchical map of values. Each key adds one level to the * hierarchy. With only a single key, the returned map will have a key for each * distinct value of the key function; the correspond value with be an array of * elements with that key value. If a second key is added, this will be a nested * map. For example: * *

    pv.nest(yields)
     *     .key(function(d) d.variety)
     *     .key(function(d) d.site)
     *     .map()
    * * returns a map m such that m[variety][site] is an array, a subset of * yields, with each element having the given variety and site. * * @returns a hierarchical map of values. */ pv.Nest.prototype.map = function() { var map = {}, values = []; /* Build the map. */ for (var i, j = 0; j < this.array.length; j++) { var x = this.array[j]; var m = map; for (i = 0; i < this.keys.length - 1; i++) { var k = this.keys[i](x); if (!m[k]) m[k] = {}; m = m[k]; } k = this.keys[i](x); if (!m[k]) { var a = []; values.push(a); m[k] = a; } m[k].push(x); } /* Sort each leaf array. */ if (this.order) { for (var i = 0; i < values.length; i++) { values[i].sort(this.order); } } return map; }; /** * Returns a hierarchical nested array. This method is similar to * {@link pv.entries}, but works recursively on the entire hierarchy. Rather * than returning a map like {@link #map}, this method returns a nested * array. Each element of the array has a key and values * field. For leaf nodes, the values array will be a subset of the * underlying elements array; for non-leaf nodes, the values array will * contain more key-values pairs. * *

    For an example usage, see the {@link Nest} constructor. * * @returns a hierarchical nested array. */ pv.Nest.prototype.entries = function() { /** Recursively extracts the entries for the given map. */ function entries(map) { var array = []; for (var k in map) { var v = map[k]; array.push({ key: k, values: (v instanceof Array) ? v : entries(v) }); }; return array; } /** Recursively sorts the values for the given key-values array. */ function sort(array, i) { var o = this.keys[i].order; if (o) array.sort(function(a, b) { return o(a.key, b.key); }); if (++i < this.keys.length) { for (var j = 0; j < array.length; j++) { sort.call(this, array[j].values, i); } } return array; } return sort.call(this, entries(this.map()), 0); }; /** * Returns a rollup map. The behavior of this method is the same as * {@link #map}, except that the leaf values are replaced with the return value * of the specified rollup function f. For example, * *

    pv.nest(yields)
     *      .key(function(d) d.site)
     *      .rollup(function(v) pv.median(v, function(d) d.yield))
    * * first groups yield data by site, and then returns a map from site to median * yield for the given site. * * @see #map * @param {function} f a rollup function. * @returns a hierarchical map, with the leaf values computed by f. */ pv.Nest.prototype.rollup = function(f) { /** Recursively descends to the leaf nodes (arrays) and does rollup. */ function rollup(map) { for (var key in map) { var value = map[key]; if (value instanceof Array) { map[key] = f(value); } else { rollup(value); } } return map; } return rollup(this.map()); }; /** * Returns a {@link pv.Flatten} operator for the specified map. This is a * convenience factory method, equivalent to new pv.Flatten(map). * * @see pv.Flatten * @param map a map to flatten. * @returns {pv.Flatten} a flatten operator for the specified map. */ pv.flatten = function(map) { return new pv.Flatten(map); }; /** * Constructs a flatten operator for the specified map. This constructor should * not be invoked directly; use {@link pv.flatten} instead. * * @class Represents a flatten operator for the specified array. Flattening * allows hierarchical maps to be flattened into an array. The levels in the * input tree are specified by key functions. * *

    For example, consider the following hierarchical data structure of Barley * yields, from various sites in Minnesota during 1931-2: * *

    { 1931: {
     *     Manchuria: {
     *       "University Farm": 27.00,
     *       "Waseca": 48.87,
     *       "Morris": 27.43,
     *       ... },
     *     Glabron: {
     *       "University Farm": 43.07,
     *       "Waseca": 55.20,
     *       ... } },
     *   1932: {
     *     ... } }
    * * To facilitate visualization, it may be useful to flatten the tree into a * tabular array: * *
    var array = pv.flatten(yields)
     *     .key("year")
     *     .key("variety")
     *     .key("site")
     *     .key("yield")
     *     .array();
    * * This returns an array of object elements. Each element in the array has * attributes corresponding to this flatten operator's keys: * *
    { site: "University Farm", variety: "Manchuria", year: 1931, yield: 27 },
     * { site: "Waseca", variety: "Manchuria", year: 1931, yield: 48.87 },
     * { site: "Morris", variety: "Manchuria", year: 1931, yield: 27.43 },
     * { site: "University Farm", variety: "Glabron", year: 1931, yield: 43.07 },
     * { site: "Waseca", variety: "Glabron", year: 1931, yield: 55.2 }, ...
    * *

    The flatten operator is roughly the inverse of the {@link pv.Nest} and * {@link pv.Tree} operators. * * @param map a map to flatten. */ pv.Flatten = function(map) { this.map = map; this.keys = []; }; /** * Flattens using the specified key function. Multiple keys may be added to the * flatten; the tiers of the underlying tree must correspond to the specified * keys, in order. The order of the returned array is undefined; however, you * can easily sort it. * * @param {string} key the key name. * @param {function} [f] an optional value map function. * @returns {pv.Nest} this. */ pv.Flatten.prototype.key = function(key, f) { this.keys.push({name: key, value: f}); delete this.$leaf; return this; }; /** * Flattens using the specified leaf function. This is an alternative to * specifying an explicit set of keys; the tiers of the underlying tree will be * determined dynamically by recursing on the values, and the resulting keys * will be stored in the entries keys attribute. The leaf function must * return true for leaves, and false for internal nodes. * * @param {function} f a leaf function. * @returns {pv.Nest} this. */ pv.Flatten.prototype.leaf = function(f) { this.keys.length = 0; this.$leaf = f; return this; }; /** * Returns the flattened array. Each entry in the array is an object; each * object has attributes corresponding to this flatten operator's keys. * * @returns an array of elements from the flattened map. */ pv.Flatten.prototype.array = function() { var entries = [], stack = [], keys = this.keys, leaf = this.$leaf; /* Recursively visit using the leaf function. */ if (leaf) { function recurse(value, i) { if (leaf(value)) { entries.push({keys: stack.slice(), value: value}); } else { for (var key in value) { stack.push(key); recurse(value[key], i + 1); stack.pop(); } } } recurse(this.map, 0); return entries; } /* Recursively visits the specified value. */ function visit(value, i) { if (i < keys.length - 1) { for (var key in value) { stack.push(key); visit(value[key], i + 1); stack.pop(); } } else { entries.push(stack.concat(value)); } } visit(this.map, 0); return entries.map(function(stack) { var m = {}; for (var i = 0; i < keys.length; i++) { var k = keys[i], v = stack[i]; m[k.name] = k.value ? k.value.call(null, v) : v; } return m; }); }; /** * Returns a {@link pv.Vector} for the specified x and y * coordinate. This is a convenience factory method, equivalent to new * pv.Vector(x, y). * * @see pv.Vector * @param {number} x the x coordinate. * @param {number} y the y coordinate. * @returns {pv.Vector} a vector for the specified coordinates. */ pv.vector = function(x, y) { return new pv.Vector(x, y); }; /** * Constructs a {@link pv.Vector} for the specified x and y * coordinate. This constructor should not be invoked directly; use * {@link pv.vector} instead. * * @class Represents a two-dimensional vector; a 2-tuple ⟨x, * y⟩. The intent of this class is to simplify vector math. Note that * in performance-sensitive cases it may be more efficient to represent 2D * vectors as simple objects with x and y attributes, rather * than using instances of this class. * * @param {number} x the x coordinate. * @param {number} y the y coordinate. */ pv.Vector = function(x, y) { this.x = x; this.y = y; }; /** * Returns a vector perpendicular to this vector: ⟨-y, x⟩. * * @returns {pv.Vector} a perpendicular vector. */ pv.Vector.prototype.perp = function() { return new pv.Vector(-this.y, this.x); }; /** * Returns a normalized copy of this vector: a vector with the same direction, * but unit length. If this vector has zero length this method returns a copy of * this vector. * * @returns {pv.Vector} a unit vector. */ pv.Vector.prototype.norm = function() { var l = this.length(); return this.times(l ? (1 / l) : 1); }; /** * Returns the magnitude of this vector, defined as sqrt(x * x + y * y). * * @returns {number} a length. */ pv.Vector.prototype.length = function() { return Math.sqrt(this.x * this.x + this.y * this.y); }; /** * Returns a scaled copy of this vector: ⟨x * k, y * k⟩. * To perform the equivalent divide operation, use 1 / k. * * @param {number} k the scale factor. * @returns {pv.Vector} a scaled vector. */ pv.Vector.prototype.times = function(k) { return new pv.Vector(this.x * k, this.y * k); }; /** * Returns this vector plus the vector v: ⟨x + v.x, y + * v.y⟩. If only one argument is specified, it is interpreted as the * vector v. * * @param {number} x the x coordinate to add. * @param {number} y the y coordinate to add. * @returns {pv.Vector} a new vector. */ pv.Vector.prototype.plus = function(x, y) { return (arguments.length == 1) ? new pv.Vector(this.x + x.x, this.y + x.y) : new pv.Vector(this.x + x, this.y + y); }; /** * Returns this vector minus the vector v: ⟨x - v.x, y - * v.y⟩. If only one argument is specified, it is interpreted as the * vector v. * * @param {number} x the x coordinate to subtract. * @param {number} y the y coordinate to subtract. * @returns {pv.Vector} a new vector. */ pv.Vector.prototype.minus = function(x, y) { return (arguments.length == 1) ? new pv.Vector(this.x - x.x, this.y - x.y) : new pv.Vector(this.x - x, this.y - y); }; /** * Returns the dot product of this vector and the vector v: x * v.x + * y * v.y. If only one argument is specified, it is interpreted as the * vector v. * * @param {number} x the x coordinate to dot. * @param {number} y the y coordinate to dot. * @returns {number} a dot product. */ pv.Vector.prototype.dot = function(x, y) { return (arguments.length == 1) ? this.x * x.x + this.y * x.y : this.x * x + this.y * y; }; /** * Returns a new identity transform. * * @class Represents a transformation matrix. The transformation matrix is * limited to expressing translate and uniform scale transforms only; shearing, * rotation, general affine, and other transforms are not supported. * *

    The methods on this class treat the transform as immutable, returning a * copy of the transformation matrix with the specified transform applied. Note, * alternatively, that the matrix fields can be get and set directly. */ pv.Transform = function() {}; pv.Transform.prototype = {k: 1, x: 0, y: 0}; /** * The scale magnitude; defaults to 1. * * @type number * @name pv.Transform.prototype.k */ /** * The x-offset; defaults to 0. * * @type number * @name pv.Transform.prototype.x */ /** * The y-offset; defaults to 0. * * @type number * @name pv.Transform.prototype.y */ /** * @private The identity transform. * * @type pv.Transform */ pv.Transform.identity = new pv.Transform(); // k 0 x 1 0 a k 0 ka+x // 0 k y * 0 1 b = 0 k kb+y // 0 0 1 0 0 1 0 0 1 /** * Returns a translated copy of this transformation matrix. * * @param {number} x the x-offset. * @param {number} y the y-offset. * @returns {pv.Transform} the translated transformation matrix. */ pv.Transform.prototype.translate = function(x, y) { var v = new pv.Transform(); v.k = this.k; v.x = this.k * x + this.x; v.y = this.k * y + this.y; return v; }; // k 0 x d 0 0 kd 0 x // 0 k y * 0 d 0 = 0 kd y // 0 0 1 0 0 1 0 0 1 /** * Returns a scaled copy of this transformation matrix. * * @param {number} k * @returns {pv.Transform} the scaled transformation matrix. */ pv.Transform.prototype.scale = function(k) { var v = new pv.Transform(); v.k = this.k * k; v.x = this.x; v.y = this.y; return v; }; /** * Returns the inverse of this transformation matrix. * * @returns {pv.Transform} the inverted transformation matrix. */ pv.Transform.prototype.invert = function() { var v = new pv.Transform(), k = 1 / this.k; v.k = k; v.x = -this.x * k; v.y = -this.y * k; return v; }; // k 0 x d 0 a kd 0 ka+x // 0 k y * 0 d b = 0 kd kb+y // 0 0 1 0 0 1 0 0 1 /** * Returns this matrix post-multiplied by the specified matrix m. * * @param {pv.Transform} m * @returns {pv.Transform} the post-multiplied transformation matrix. */ pv.Transform.prototype.times = function(m) { var v = new pv.Transform(); v.k = this.k * m.k; v.x = this.k * m.x + this.x; v.y = this.k * m.y + this.y; return v; }; /** * Abstract; see the various scale implementations. * * @class Represents a scale; a function that performs a transformation from * data domain to visual range. For quantitative and quantile scales, the domain * is expressed as numbers; for ordinal scales, the domain is expressed as * strings (or equivalently objects with unique string representations). The * "visual range" may correspond to pixel space, colors, font sizes, and the * like. * *

    Note that scales are functions, and thus can be used as properties * directly, assuming that the data associated with a mark is a number. While * this is convenient for single-use scales, frequently it is desirable to * define scales globally: * *

    var y = pv.Scale.linear(0, 100).range(0, 640);
    * * The y scale can now be equivalently referenced within a property: * *
        .height(function(d) y(d))
    * * Alternatively, if the data are not simple numbers, the appropriate value can * be passed to the y scale (e.g., d.foo). The {@link #by} * method similarly allows the data to be mapped to a numeric value before * performing the linear transformation. * * @see pv.Scale.quantitative * @see pv.Scale.quantile * @see pv.Scale.ordinal * @extends function */ pv.Scale = function() {}; /** * @private Returns a function that interpolators from the start value to the * end value, given a parameter t in [0, 1]. * * @param start the start value. * @param end the end value. */ pv.Scale.interpolator = function(start, end) { if (typeof start == "number") { return function(t) { return t * (end - start) + start; }; } /* For now, assume color. */ start = pv.color(start).rgb(); end = pv.color(end).rgb(); return function(t) { var a = start.a * (1 - t) + end.a * t; if (a < 1e-5) a = 0; // avoid scientific notation return (start.a == 0) ? pv.rgb(end.r, end.g, end.b, a) : ((end.a == 0) ? pv.rgb(start.r, start.g, start.b, a) : pv.rgb( Math.round(start.r * (1 - t) + end.r * t), Math.round(start.g * (1 - t) + end.g * t), Math.round(start.b * (1 - t) + end.b * t), a)); }; }; /** * Returns a view of this scale by the specified accessor function f. * Given a scale y, y.by(function(d) d.foo) is equivalent to * function(d) y(d.foo). * *

    This method is provided for convenience, such that scales can be * succinctly defined inline. For example, given an array of data elements that * have a score attribute with the domain [0, 1], the height property * could be specified as: * *

        .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))
    * * This is equivalent to: * *
        .height(function(d) d.score * 480)
    * * This method should be used judiciously; it is typically more clear to invoke * the scale directly, passing in the value to be scaled. * * @function * @name pv.Scale.prototype.by * @param {function} f an accessor function. * @returns {pv.Scale} a view of this scale by the specified accessor function. */ /** * Returns a default quantitative, linear, scale for the specified domain. The * arguments to this constructor are optional, and equivalent to calling * {@link #domain}. The default domain and range are [0,1]. * *

    This constructor is typically not used directly; see one of the * quantitative scale implementations instead. * * @class Represents an abstract quantitative scale; a function that performs a * numeric transformation. This class is typically not used directly; see one of * the quantitative scale implementations (linear, log, root, etc.) * instead. A quantitative * scale represents a 1-dimensional transformation from a numeric domain of * input data [d0, d1] to a numeric range of * pixels [r0, r1]. In addition to * readability, scales offer several useful features: * *

    1. The range can be expressed in colors, rather than pixels. For example: * *

        .fillStyle(pv.Scale.linear(0, 100).range("red", "green"))
    * * will fill the marks "red" on an input value of 0, "green" on an input value * of 100, and some color in-between for intermediate values. * *

    2. The domain and range can be subdivided for a non-uniform * transformation. For example, you may want a diverging color scale that is * increasingly red for negative values, and increasingly green for positive * values: * *

        .fillStyle(pv.Scale.linear(-1, 0, 1).range("red", "white", "green"))
    * * The domain can be specified as a series of n monotonically-increasing * values; the range must also be specified as n values, resulting in * n - 1 contiguous linear scales. * *

    3. Quantitative scales can be inverted for interaction. The * {@link #invert} method takes a value in the output range, and returns the * corresponding value in the input domain. This is frequently used to convert * the mouse location (see {@link pv.Mark#mouse}) to a value in the input * domain. Note that inversion is only supported for numeric ranges, and not * colors. * *

    4. A scale can be queried for reasonable "tick" values. The {@link #ticks} * method provides a convenient way to get a series of evenly-spaced rounded * values in the input domain. Frequently these are used in conjunction with * {@link pv.Rule} to display tick marks or grid lines. * *

    5. A scale can be "niced" to extend the domain to suitable rounded * numbers. If the minimum and maximum of the domain are messy because they are * derived from data, you can use {@link #nice} to round these values down and * up to even numbers. * * @param {number...} domain... optional domain values. * @see pv.Scale.linear * @see pv.Scale.log * @see pv.Scale.root * @extends pv.Scale */ pv.Scale.quantitative = function() { var d = [0, 1], // default domain l = [0, 1], // default transformed domain r = [0, 1], // default range i = [pv.identity], // default interpolators type = Number, // default type n = false, // whether the domain is negative f = pv.identity, // default forward transform g = pv.identity, // default inverse transform tickFormat = String; // default tick formatting function /** @private */ function newDate(x) { return new Date(x); } /** @private */ function scale(x) { var j = pv.search(d, x); if (j < 0) j = -j - 2; j = Math.max(0, Math.min(i.length - 1, j)); return i[j]((f(x) - l[j]) / (l[j + 1] - l[j])); } /** @private */ scale.transform = function(forward, inverse) { /** @ignore */ f = function(x) { return n ? -forward(-x) : forward(x); }; /** @ignore */ g = function(y) { return n ? -inverse(-y) : inverse(y); }; l = d.map(f); return this; }; /** * Sets or gets the input domain. This method can be invoked several ways: * *

    1. domain(min, ..., max) * *

    Specifying the domain as a series of numbers is the most explicit and * recommended approach. Most commonly, two numbers are specified: the minimum * and maximum value. However, for a diverging scale, or other subdivided * non-uniform scales, multiple values can be specified. Values can be derived * from data using {@link pv.min} and {@link pv.max}. For example: * *

        .domain(0, pv.max(array))
    * * An alternative method for deriving minimum and maximum values from data * follows. * *

    2. domain(array, minf, maxf) * *

    When both the minimum and maximum value are derived from data, the * arguments to the domain method can be specified as the array of * data, followed by zero, one or two accessor functions. For example, if the * array of data is just an array of numbers: * *

        .domain(array)
    * * On the other hand, if the array elements are objects representing stock * values per day, and the domain should consider the stock's daily low and * daily high: * *
        .domain(array, function(d) d.low, function(d) d.high)
    * * The first method of setting the domain is preferred because it is more * explicit; setting the domain using this second method should be used only * if brevity is required. * *

    3. domain() * *

    Invoking the domain method with no arguments returns the * current domain as an array of numbers. * * @function * @name pv.Scale.quantitative.prototype.domain * @param {number...} domain... domain values. * @returns {pv.Scale.quantitative} this, or the current domain. */ scale.domain = function(array, min, max) { if (arguments.length) { var o; // the object we use to infer the domain type if (array instanceof Array) { if (arguments.length < 2) min = pv.identity; if (arguments.length < 3) max = min; o = array.length && min(array[0]); d = array.length ? [pv.min(array, min), pv.max(array, max)] : []; } else { o = array; d = Array.prototype.slice.call(arguments).map(Number); } if (!d.length) d = [-Infinity, Infinity]; else if (d.length == 1) d = [d[0], d[0]]; n = (d[0] || d[d.length - 1]) < 0; l = d.map(f); type = (o instanceof Date) ? newDate : Number; return this; } return d.map(type); }; /** * Sets or gets the output range. This method can be invoked several ways: * *

    1. range(min, ..., max) * *

    The range may be specified as a series of numbers or colors. Most * commonly, two numbers are specified: the minimum and maximum pixel values. * For a color scale, values may be specified as {@link pv.Color}s or * equivalent strings. For a diverging scale, or other subdivided non-uniform * scales, multiple values can be specified. For example: * *

        .range("red", "white", "green")
    * *

    Currently, only numbers and colors are supported as range values. The * number of range values must exactly match the number of domain values, or * the behavior of the scale is undefined. * *

    2. range() * *

    Invoking the range method with no arguments returns the current * range as an array of numbers or colors. * * @function * @name pv.Scale.quantitative.prototype.range * @param {...} range... range values. * @returns {pv.Scale.quantitative} this, or the current range. */ scale.range = function() { if (arguments.length) { r = Array.prototype.slice.call(arguments); if (!r.length) r = [-Infinity, Infinity]; else if (r.length == 1) r = [r[0], r[0]]; i = []; for (var j = 0; j < r.length - 1; j++) { i.push(pv.Scale.interpolator(r[j], r[j + 1])); } return this; } return r; }; /** * Inverts the specified value in the output range, returning the * corresponding value in the input domain. This is frequently used to convert * the mouse location (see {@link pv.Mark#mouse}) to a value in the input * domain. Inversion is only supported for numeric ranges, and not colors. * *

    Note that this method does not do any rounding or bounds checking. If * the input domain is discrete (e.g., an array index), the returned value * should be rounded. If the specified y value is outside the range, * the returned value may be equivalently outside the input domain. * * @function * @name pv.Scale.quantitative.prototype.invert * @param {number} y a value in the output range (a pixel location). * @returns {number} a value in the input domain. */ scale.invert = function(y) { var j = pv.search(r, y); if (j < 0) j = -j - 2; j = Math.max(0, Math.min(i.length - 1, j)); return type(g(l[j] + (y - r[j]) / (r[j + 1] - r[j]) * (l[j + 1] - l[j]))); }; /** * Returns an array of evenly-spaced, suitably-rounded values in the input * domain. This method attempts to return between 5 and 10 tick values. These * values are frequently used in conjunction with {@link pv.Rule} to display * tick marks or grid lines. * * @function * @name pv.Scale.quantitative.prototype.ticks * @param {number} [m] optional number of desired ticks. * @returns {number[]} an array input domain values to use as ticks. */ scale.ticks = function(m) { var start = d[0], end = d[d.length - 1], reverse = end < start, min = reverse ? end : start, max = reverse ? start : end, span = max - min; /* Special case: empty, invalid or infinite span. */ if (!span || !isFinite(span)) { if (type == newDate) tickFormat = pv.Format.date("%x"); return [type(min)]; } /* Special case: dates. */ if (type == newDate) { /* Floor the date d given the precision p. */ function floor(d, p) { switch (p) { case 31536e6: d.setMonth(0); case 2592e6: d.setDate(1); case 6048e5: if (p == 6048e5) d.setDate(d.getDate() - d.getDay()); case 864e5: d.setHours(0); case 36e5: d.setMinutes(0); case 6e4: d.setSeconds(0); case 1e3: d.setMilliseconds(0); } } var precision, format, increment, step = 1; if (span >= 3 * 31536e6) { precision = 31536e6; format = "%Y"; /** @ignore */ increment = function(d) { d.setFullYear(d.getFullYear() + step); }; } else if (span >= 3 * 2592e6) { precision = 2592e6; format = "%m/%Y"; /** @ignore */ increment = function(d) { d.setMonth(d.getMonth() + step); }; } else if (span >= 3 * 6048e5) { precision = 6048e5; format = "%m/%d"; /** @ignore */ increment = function(d) { d.setDate(d.getDate() + 7 * step); }; } else if (span >= 3 * 864e5) { precision = 864e5; format = "%m/%d"; /** @ignore */ increment = function(d) { d.setDate(d.getDate() + step); }; } else if (span >= 3 * 36e5) { precision = 36e5; format = "%I:%M %p"; /** @ignore */ increment = function(d) { d.setHours(d.getHours() + step); }; } else if (span >= 3 * 6e4) { precision = 6e4; format = "%I:%M %p"; /** @ignore */ increment = function(d) { d.setMinutes(d.getMinutes() + step); }; } else if (span >= 3 * 1e3) { precision = 1e3; format = "%I:%M:%S"; /** @ignore */ increment = function(d) { d.setSeconds(d.getSeconds() + step); }; } else { precision = 1; format = "%S.%Qs"; /** @ignore */ increment = function(d) { d.setTime(d.getTime() + step); }; } tickFormat = pv.Format.date(format); var date = new Date(min), dates = []; floor(date, precision); /* If we'd generate too many ticks, skip some!. */ var n = span / precision; if (n > 10) { switch (precision) { case 36e5: { step = (n > 20) ? 6 : 3; date.setHours(Math.floor(date.getHours() / step) * step); break; } case 2592e6: { step = 3; // seasons date.setMonth(Math.floor(date.getMonth() / step) * step); break; } case 6e4: { step = (n > 30) ? 15 : ((n > 15) ? 10 : 5); date.setMinutes(Math.floor(date.getMinutes() / step) * step); break; } case 1e3: { step = (n > 90) ? 15 : ((n > 60) ? 10 : 5); date.setSeconds(Math.floor(date.getSeconds() / step) * step); break; } case 1: { step = (n > 1000) ? 250 : ((n > 200) ? 100 : ((n > 100) ? 50 : ((n > 50) ? 25 : 5))); date.setMilliseconds(Math.floor(date.getMilliseconds() / step) * step); break; } default: { step = pv.logCeil(n / 15, 10); if (n / step < 2) step /= 5; else if (n / step < 5) step /= 2; date.setFullYear(Math.floor(date.getFullYear() / step) * step); break; } } } while (true) { increment(date); if (date > max) break; dates.push(new Date(date)); } return reverse ? dates.reverse() : dates; } /* Normal case: numbers. */ if (!arguments.length) m = 10; var step = pv.logFloor(span / m, 10), err = m / (span / step); if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; var start = Math.ceil(min / step) * step, end = Math.floor(max / step) * step; tickFormat = pv.Format.number() .fractionDigits(Math.max(0, -Math.floor(pv.log(step, 10) + .01))); var ticks = pv.range(start, end + step, step); return reverse ? ticks.reverse() : ticks; }; /** * Formats the specified tick value using the appropriate precision, based on * the step interval between tick marks. If {@link #ticks} has not been called, * the argument is converted to a string, but no formatting is applied. * * @function * @name pv.Scale.quantitative.prototype.tickFormat * @param {number} t a tick value. * @returns {string} a formatted tick value. */ scale.tickFormat = function (t) { return tickFormat(t); }; /** * "Nices" this scale, extending the bounds of the input domain to * evenly-rounded values. Nicing is useful if the domain is computed * dynamically from data, and may be irregular. For example, given a domain of * [0.20147987687960267, 0.996679553296417], a call to nice() might * extend the domain to [0.2, 1]. * *

    This method must be invoked each time after setting the domain. * * @function * @name pv.Scale.quantitative.prototype.nice * @returns {pv.Scale.quantitative} this. */ scale.nice = function() { if (d.length != 2) return this; // TODO support non-uniform domains var start = d[0], end = d[d.length - 1], reverse = end < start, min = reverse ? end : start, max = reverse ? start : end, span = max - min; /* Special case: empty, invalid or infinite span. */ if (!span || !isFinite(span)) return this; var step = Math.pow(10, Math.round(Math.log(span) / Math.log(10)) - 1); d = [Math.floor(min / step) * step, Math.ceil(max / step) * step]; if (reverse) d.reverse(); l = d.map(f); return this; }; /** * Returns a view of this scale by the specified accessor function f. * Given a scale y, y.by(function(d) d.foo) is equivalent to * function(d) y(d.foo). * *

    This method is provided for convenience, such that scales can be * succinctly defined inline. For example, given an array of data elements * that have a score attribute with the domain [0, 1], the height * property could be specified as: * *

        .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))
    * * This is equivalent to: * *
        .height(function(d) d.score * 480)
    * * This method should be used judiciously; it is typically more clear to * invoke the scale directly, passing in the value to be scaled. * * @function * @name pv.Scale.quantitative.prototype.by * @param {function} f an accessor function. * @returns {pv.Scale.quantitative} a view of this scale by the specified * accessor function. */ scale.by = function(f) { function by() { return scale(f.apply(this, arguments)); } for (var method in scale) by[method] = scale[method]; return by; }; scale.domain.apply(scale, arguments); return scale; }; /** * Returns a linear scale for the specified domain. The arguments to this * constructor are optional, and equivalent to calling {@link #domain}. * The default domain and range are [0,1]. * * @class Represents a linear scale; a function that performs a linear * transformation. Most * commonly, a linear scale represents a 1-dimensional linear transformation * from a numeric domain of input data [d0, * d1] to a numeric range of pixels [r0, * r1]. The equation for such a scale is: * *
    f(x) = (x - d0) / (d1 - d0) * * (r1 - r0) + r0
    * * For example, a linear scale from the domain [0, 100] to range [0, 640]: * *
    f(x) = (x - 0) / (100 - 0) * (640 - 0) + 0
    * f(x) = x / 100 * 640
    * f(x) = x * 6.4
    *
    * * Thus, saying * *
        .height(function(d) d * 6.4)
    * * is identical to * *
        .height(pv.Scale.linear(0, 100).range(0, 640))
    * * Note that the scale is itself a function, and thus can be used as a property * directly, assuming that the data associated with a mark is a number. While * this is convenient for single-use scales, frequently it is desirable to * define scales globally: * *
    var y = pv.Scale.linear(0, 100).range(0, 640);
    * * The y scale can now be equivalently referenced within a property: * *
        .height(function(d) y(d))
    * * Alternatively, if the data are not simple numbers, the appropriate value can * be passed to the y scale (e.g., d.foo). The {@link #by} * method similarly allows the data to be mapped to a numeric value before * performing the linear transformation. * * @param {number...} domain... optional domain values. * @extends pv.Scale.quantitative */ pv.Scale.linear = function() { var scale = pv.Scale.quantitative(); scale.domain.apply(scale, arguments); return scale; }; /** * Returns a log scale for the specified domain. The arguments to this * constructor are optional, and equivalent to calling {@link #domain}. * The default domain is [1,10] and the default range is [0,1]. * * @class Represents a log scale. Most commonly, a log scale * represents a 1-dimensional log transformation from a numeric domain of input * data [d0, d1] to a numeric range of * pixels [r0, r1]. The equation for such a * scale is: * *
    f(x) = (log(x) - log(d0)) / (log(d1) - * log(d0)) * (r1 - r0) + * r0
    * * where log(x) represents the zero-symmetric logarthim of x using * the scale's associated base (default: 10, see {@link pv.logSymmetric}). For * example, a log scale from the domain [1, 100] to range [0, 640]: * *
    f(x) = (log(x) - log(1)) / (log(100) - log(1)) * (640 - 0) + 0
    * f(x) = log(x) / 2 * 640
    * f(x) = log(x) * 320
    *
    * * Thus, saying * *
        .height(function(d) Math.log(d) * 138.974)
    * * is equivalent to * *
        .height(pv.Scale.log(1, 100).range(0, 640))
    * * Note that the scale is itself a function, and thus can be used as a property * directly, assuming that the data associated with a mark is a number. While * this is convenient for single-use scales, frequently it is desirable to * define scales globally: * *
    var y = pv.Scale.log(1, 100).range(0, 640);
    * * The y scale can now be equivalently referenced within a property: * *
        .height(function(d) y(d))
    * * Alternatively, if the data are not simple numbers, the appropriate value can * be passed to the y scale (e.g., d.foo). The {@link #by} * method similarly allows the data to be mapped to a numeric value before * performing the log transformation. * * @param {number...} domain... optional domain values. * @extends pv.Scale.quantitative */ pv.Scale.log = function() { var scale = pv.Scale.quantitative(1, 10), b, // logarithm base p, // cached Math.log(b) /** @ignore */ log = function(x) { return Math.log(x) / p; }, /** @ignore */ pow = function(y) { return Math.pow(b, y); }; /** * Returns an array of evenly-spaced, suitably-rounded values in the input * domain. These values are frequently used in conjunction with * {@link pv.Rule} to display tick marks or grid lines. * * @function * @name pv.Scale.log.prototype.ticks * @returns {number[]} an array input domain values to use as ticks. */ scale.ticks = function() { // TODO support non-uniform domains var d = scale.domain(), n = d[0] < 0, i = Math.floor(n ? -log(-d[0]) : log(d[0])), j = Math.ceil(n ? -log(-d[1]) : log(d[1])), ticks = []; if (n) { ticks.push(-pow(-i)); for (; i++ < j;) for (var k = b - 1; k > 0; k--) ticks.push(-pow(-i) * k); } else { for (; i < j; i++) for (var k = 1; k < b; k++) ticks.push(pow(i) * k); ticks.push(pow(i)); } for (i = 0; ticks[i] < d[0]; i++); // strip small values for (j = ticks.length; ticks[j - 1] > d[1]; j--); // strip big values return ticks.slice(i, j); }; /** * Formats the specified tick value using the appropriate precision, assuming * base 10. * * @function * @name pv.Scale.log.prototype.tickFormat * @param {number} t a tick value. * @returns {string} a formatted tick value. */ scale.tickFormat = function(t) { return t.toPrecision(1); }; /** * "Nices" this scale, extending the bounds of the input domain to * evenly-rounded values. This method uses {@link pv.logFloor} and * {@link pv.logCeil}. Nicing is useful if the domain is computed dynamically * from data, and may be irregular. For example, given a domain of * [0.20147987687960267, 0.996679553296417], a call to nice() might * extend the domain to [0.1, 1]. * *

    This method must be invoked each time after setting the domain (and * base). * * @function * @name pv.Scale.log.prototype.nice * @returns {pv.Scale.log} this. */ scale.nice = function() { // TODO support non-uniform domains var d = scale.domain(); return scale.domain(pv.logFloor(d[0], b), pv.logCeil(d[1], b)); }; /** * Sets or gets the logarithm base. Defaults to 10. * * @function * @name pv.Scale.log.prototype.base * @param {number} [v] the new base. * @returns {pv.Scale.log} this, or the current base. */ scale.base = function(v) { if (arguments.length) { b = Number(v); p = Math.log(b); scale.transform(log, pow); // update transformed domain return this; } return b; }; scale.domain.apply(scale, arguments); return scale.base(10); }; /** * Returns a root scale for the specified domain. The arguments to this * constructor are optional, and equivalent to calling {@link #domain}. * The default domain and range are [0,1]. * * @class Represents a root scale; a function that performs a power * transformation. Most * commonly, a root scale represents a 1-dimensional root transformation from a * numeric domain of input data [d0, d1] to * a numeric range of pixels [r0, r1]. * *

    Note that the scale is itself a function, and thus can be used as a * property directly, assuming that the data associated with a mark is a * number. While this is convenient for single-use scales, frequently it is * desirable to define scales globally: * *

    var y = pv.Scale.root(0, 100).range(0, 640);
    * * The y scale can now be equivalently referenced within a property: * *
        .height(function(d) y(d))
    * * Alternatively, if the data are not simple numbers, the appropriate value can * be passed to the y scale (e.g., d.foo). The {@link #by} * method similarly allows the data to be mapped to a numeric value before * performing the root transformation. * * @param {number...} domain... optional domain values. * @extends pv.Scale.quantitative */ pv.Scale.root = function() { var scale = pv.Scale.quantitative(); /** * Sets or gets the exponent; defaults to 2. * * @function * @name pv.Scale.root.prototype.power * @param {number} [v] the new exponent. * @returns {pv.Scale.root} this, or the current base. */ scale.power = function(v) { if (arguments.length) { var b = Number(v), p = 1 / b; scale.transform( function(x) { return Math.pow(x, p); }, function(y) { return Math.pow(y, b); }); return this; } return b; }; scale.domain.apply(scale, arguments); return scale.power(2); }; /** * Returns an ordinal scale for the specified domain. The arguments to this * constructor are optional, and equivalent to calling {@link #domain}. * * @class Represents an ordinal scale. An ordinal scale represents a * pairwise mapping from n discrete values in the input domain to * n discrete values in the output range. For example, an ordinal scale * might map a domain of species ["setosa", "versicolor", "virginica"] to colors * ["red", "green", "blue"]. Thus, saying * *
        .fillStyle(function(d) {
     *         switch (d.species) {
     *           case "setosa": return "red";
     *           case "versicolor": return "green";
     *           case "virginica": return "blue";
     *         }
     *       })
    * * is equivalent to * *
        .fillStyle(pv.Scale.ordinal("setosa", "versicolor", "virginica")
     *         .range("red", "green", "blue")
     *         .by(function(d) d.species))
    * * If the mapping from species to color does not need to be specified * explicitly, the domain can be omitted. In this case it will be inferred * lazily from the data: * *
        .fillStyle(pv.colors("red", "green", "blue")
     *         .by(function(d) d.species))
    * * When the domain is inferred, the first time the scale is invoked, the first * element from the range will be returned. Subsequent calls with unique values * will return subsequent elements from the range. If the inferred domain grows * larger than the range, range values will be reused. However, it is strongly * recommended that the domain and the range contain the same number of * elements. * *

    A range can be discretized from a continuous interval (e.g., for pixel * positioning) by using {@link #split}, {@link #splitFlush} or * {@link #splitBanded} after the domain has been set. For example, if * states is an array of the fifty U.S. state names, the state name can * be encoded in the left position: * *

        .left(pv.Scale.ordinal(states)
     *         .split(0, 640)
     *         .by(function(d) d.state))
    * *

    N.B.: ordinal scales are not invertible (at least not yet), since the * domain and range and discontinuous. A workaround is to use a linear scale. * * @param {...} domain... optional domain values. * @extends pv.Scale * @see pv.colors */ pv.Scale.ordinal = function() { var d = [], i = {}, r = [], band = 0; /** @private */ function scale(x) { if (!(x in i)) i[x] = d.push(x) - 1; return r[i[x] % r.length]; } /** * Sets or gets the input domain. This method can be invoked several ways: * *

    1. domain(values...) * *

    Specifying the domain as a series of values is the most explicit and * recommended approach. However, if the domain values are derived from data, * you may find the second method more appropriate. * *

    2. domain(array, f) * *

    Rather than enumerating the domain values as explicit arguments to this * method, you can specify a single argument of an array. In addition, you can * specify an optional accessor function to extract the domain values from the * array. * *

    3. domain() * *

    Invoking the domain method with no arguments returns the * current domain as an array. * * @function * @name pv.Scale.ordinal.prototype.domain * @param {...} domain... domain values. * @returns {pv.Scale.ordinal} this, or the current domain. */ scale.domain = function(array, f) { if (arguments.length) { array = (array instanceof Array) ? ((arguments.length > 1) ? pv.map(array, f) : array) : Array.prototype.slice.call(arguments); /* Filter the specified ordinals to their unique values. */ d = []; var seen = {}; for (var j = 0; j < array.length; j++) { var o = array[j]; if (!(o in seen)) { seen[o] = true; d.push(o); } } i = pv.numerate(d); return this; } return d; }; /** * Sets or gets the output range. This method can be invoked several ways: * *

    1. range(values...) * *

    Specifying the range as a series of values is the most explicit and * recommended approach. However, if the range values are derived from data, * you may find the second method more appropriate. * *

    2. range(array, f) * *

    Rather than enumerating the range values as explicit arguments to this * method, you can specify a single argument of an array. In addition, you can * specify an optional accessor function to extract the range values from the * array. * *

    3. range() * *

    Invoking the range method with no arguments returns the * current range as an array. * * @function * @name pv.Scale.ordinal.prototype.range * @param {...} range... range values. * @returns {pv.Scale.ordinal} this, or the current range. */ scale.range = function(array, f) { if (arguments.length) { r = (array instanceof Array) ? ((arguments.length > 1) ? pv.map(array, f) : array) : Array.prototype.slice.call(arguments); if (typeof r[0] == "string") r = r.map(pv.color); return this; } return r; }; /** * Sets the range from the given continuous interval. The interval * [min, max] is subdivided into n equispaced points, * where n is the number of (unique) values in the domain. The first * and last point are offset from the edge of the range by half the distance * between points. * *

    This method must be called after the domain is set. * * @function * @name pv.Scale.ordinal.prototype.split * @param {number} min minimum value of the output range. * @param {number} max maximum value of the output range. * @returns {pv.Scale.ordinal} this. * @see #splitFlush * @see #splitBanded */ scale.split = function(min, max) { var step = (max - min) / this.domain().length; r = pv.range(min + step / 2, max, step); return this; }; /** * Sets the range from the given continuous interval. The interval * [min, max] is subdivided into n equispaced points, * where n is the number of (unique) values in the domain. The first * and last point are exactly on the edge of the range. * *

    This method must be called after the domain is set. * * @function * @name pv.Scale.ordinal.prototype.splitFlush * @param {number} min minimum value of the output range. * @param {number} max maximum value of the output range. * @returns {pv.Scale.ordinal} this. * @see #split */ scale.splitFlush = function(min, max) { var n = this.domain().length, step = (max - min) / (n - 1); r = (n == 1) ? [(min + max) / 2] : pv.range(min, max + step / 2, step); return this; }; /** * Sets the range from the given continuous interval. The interval * [min, max] is subdivided into n equispaced bands, * where n is the number of (unique) values in the domain. The first * and last band are offset from the edge of the range by the distance between * bands. * *

    The band width argument, band, is typically in the range [0, 1] * and defaults to 1. This fraction corresponds to the amount of space in the * range to allocate to the bands, as opposed to padding. A value of 0.5 means * that the band width will be equal to the padding width. The computed * absolute band width can be retrieved from the range as * scale.range().band. * *

    If the band width argument is negative, this method will allocate bands * of a fixed width -band, rather than a relative fraction of * the available space. * *

    Tip: to inset the bands by a fixed amount p, specify a minimum * value of min + p (or simply p, if min is * 0). Then set the mark width to scale.range().band - p. * *

    This method must be called after the domain is set. * * @function * @name pv.Scale.ordinal.prototype.splitBanded * @param {number} min minimum value of the output range. * @param {number} max maximum value of the output range. * @param {number} [band] the fractional band width in [0, 1]; defaults to 1. * @returns {pv.Scale.ordinal} this. * @see #split */ scale.splitBanded = function(min, max, band) { if (arguments.length < 3) band = 1; if (band < 0) { var n = this.domain().length, total = -band * n, remaining = max - min - total, padding = remaining / (n + 1); r = pv.range(min + padding, max, padding - band); r.band = -band; } else { var step = (max - min) / (this.domain().length + (1 - band)); r = pv.range(min + step * (1 - band), max, step); r.band = step * band; } return this; }; /** * Returns a view of this scale by the specified accessor function f. * Given a scale y, y.by(function(d) d.foo) is equivalent to * function(d) y(d.foo). This method should be used judiciously; it * is typically more clear to invoke the scale directly, passing in the value * to be scaled. * * @function * @name pv.Scale.ordinal.prototype.by * @param {function} f an accessor function. * @returns {pv.Scale.ordinal} a view of this scale by the specified accessor * function. */ scale.by = function(f) { function by() { return scale(f.apply(this, arguments)); } for (var method in scale) by[method] = scale[method]; return by; }; scale.domain.apply(scale, arguments); return scale; }; /** * Constructs a default quantile scale. The arguments to this constructor are * optional, and equivalent to calling {@link #domain}. The default domain is * the empty set, and the default range is [0,1]. * * @class Represents a quantile scale; a function that maps from a value within * a sortable domain to a quantized numeric range. Typically, the domain is a * set of numbers, but any sortable value (such as strings) can be used as the * domain of a quantile scale. The range defaults to [0,1], with 0 corresponding * to the smallest value in the domain, 1 the largest, .5 the median, etc. * *

    By default, the number of quantiles in the range corresponds to the number * of values in the domain. The {@link #quantiles} method can be used to specify * an explicit number of quantiles; for example, quantiles(4) produces * a standard quartile scale. A quartile scale's range is a set of four discrete * values, such as [0, 1/3, 2/3, 1]. Calling the {@link #range} method will * scale these discrete values accordingly, similar to {@link * pv.Scale.ordinal#splitFlush}. * *

    For example, given the strings ["c", "a", "b"], a default quantile scale: * *

    pv.Scale.quantile("c", "a", "b")
    * * will return 0 for "a", .5 for "b", and 1 for "c". * * @extends pv.Scale */ pv.Scale.quantile = function() { var n = -1, // number of quantiles j = -1, // max quantile index q = [], // quantile boundaries d = [], // domain y = pv.Scale.linear(); // range /** @private */ function scale(x) { return y(Math.max(0, Math.min(j, pv.search.index(q, x) - 1)) / j); } /** * Sets or gets the quantile boundaries. By default, each element in the * domain is in its own quantile. If the argument to this method is a number, * it specifies the number of equal-sized quantiles by which to divide the * domain. * *

    If no arguments are specified, this method returns the quantile * boundaries; the first element is always the minimum value of the domain, * and the last element is the maximum value of the domain. Thus, the length * of the returned array is always one greater than the number of quantiles. * * @function * @name pv.Scale.quantile.prototype.quantiles * @param {number} x the number of quantiles. */ scale.quantiles = function(x) { if (arguments.length) { n = Number(x); if (n < 0) { q = [d[0]].concat(d); j = d.length - 1; } else { q = []; q[0] = d[0]; for (var i = 1; i <= n; i++) { q[i] = d[~~(i * (d.length - 1) / n)]; } j = n - 1; } return this; } return q; }; /** * Sets or gets the input domain. This method can be invoked several ways: * *

    1. domain(values...) * *

    Specifying the domain as a series of values is the most explicit and * recommended approach. However, if the domain values are derived from data, * you may find the second method more appropriate. * *

    2. domain(array, f) * *

    Rather than enumerating the domain values as explicit arguments to this * method, you can specify a single argument of an array. In addition, you can * specify an optional accessor function to extract the domain values from the * array. * *

    3. domain() * *

    Invoking the domain method with no arguments returns the * current domain as an array. * * @function * @name pv.Scale.quantile.prototype.domain * @param {...} domain... domain values. * @returns {pv.Scale.quantile} this, or the current domain. */ scale.domain = function(array, f) { if (arguments.length) { d = (array instanceof Array) ? pv.map(array, f) : Array.prototype.slice.call(arguments); d.sort(pv.naturalOrder); scale.quantiles(n); // recompute quantiles return this; } return d; }; /** * Sets or gets the output range. This method can be invoked several ways: * *

    1. range(min, ..., max) * *

    The range may be specified as a series of numbers or colors. Most * commonly, two numbers are specified: the minimum and maximum pixel values. * For a color scale, values may be specified as {@link pv.Color}s or * equivalent strings. For a diverging scale, or other subdivided non-uniform * scales, multiple values can be specified. For example: * *

        .range("red", "white", "green")
    * *

    Currently, only numbers and colors are supported as range values. The * number of range values must exactly match the number of domain values, or * the behavior of the scale is undefined. * *

    2. range() * *

    Invoking the range method with no arguments returns the current * range as an array of numbers or colors. * * @function * @name pv.Scale.quantile.prototype.range * @param {...} range... range values. * @returns {pv.Scale.quantile} this, or the current range. */ scale.range = function() { if (arguments.length) { y.range.apply(y, arguments); return this; } return y.range(); }; /** * Returns a view of this scale by the specified accessor function f. * Given a scale y, y.by(function(d) d.foo) is equivalent to * function(d) y(d.foo). * *

    This method is provided for convenience, such that scales can be * succinctly defined inline. For example, given an array of data elements * that have a score attribute with the domain [0, 1], the height * property could be specified as: * *

    .height(pv.Scale.linear().range(0, 480).by(function(d) d.score))
    * * This is equivalent to: * *
    .height(function(d) d.score * 480)
    * * This method should be used judiciously; it is typically more clear to * invoke the scale directly, passing in the value to be scaled. * * @function * @name pv.Scale.quantile.prototype.by * @param {function} f an accessor function. * @returns {pv.Scale.quantile} a view of this scale by the specified * accessor function. */ scale.by = function(f) { function by() { return scale(f.apply(this, arguments)); } for (var method in scale) by[method] = scale[method]; return by; }; scale.domain.apply(scale, arguments); return scale; }; /** * Returns a histogram operator for the specified data, with an optional * accessor function. If the data specified is not an array of numbers, an * accessor function must be specified to map the data to numeric values. * * @class Represents a histogram operator. * * @param {array} data an array of numbers or objects. * @param {function} [f] an optional accessor function. */ pv.histogram = function(data, f) { var frequency = true; return { /** * Returns the computed histogram bins. An optional array of numbers, * ticks, may be specified as the break points. If the ticks are * not specified, default ticks will be computed using a linear scale on the * data domain. * *

    The returned array contains {@link pv.histogram.Bin}s. The x * attribute corresponds to the bin's start value (inclusive), while the * dx attribute stores the bin size (end - start). The y * attribute stores either the frequency count or probability, depending on * how the histogram operator has been configured. * *

    The {@link pv.histogram.Bin} objects are themselves arrays, containing * the data elements present in each bin, i.e., the elements in the * data array (prior to invoking the accessor function, if any). * For example, if the data represented countries, and the accessor function * returned the GDP of each country, the returned bins would be arrays of * countries (not GDPs). * * @function * @name pv.histogram.prototype.bins * @param {array} [ticks] * @returns {array} */ /** @private */ bins: function(ticks) { var x = pv.map(data, f), bins = []; /* Initialize default ticks. */ if (!arguments.length) ticks = pv.Scale.linear(x).ticks(); /* Initialize the bins. */ for (var i = 0; i < ticks.length - 1; i++) { var bin = bins[i] = []; bin.x = ticks[i]; bin.dx = ticks[i + 1] - ticks[i]; bin.y = 0; } /* Count the number of samples per bin. */ for (var i = 0; i < x.length; i++) { var j = pv.search.index(ticks, x[i]) - 1, bin = bins[Math.max(0, Math.min(bins.length - 1, j))]; bin.y++; bin.push(data[i]); } /* Convert frequencies to probabilities. */ if (!frequency) for (var i = 0; i < bins.length; i++) { bins[i].y /= x.length; } return bins; }, /** * Sets or gets whether this histogram operator returns frequencies or * probabilities. * * @function * @name pv.histogram.prototype.frequency * @param {boolean} [x] * @returns {pv.histogram} this. */ /** @private */ frequency: function(x) { if (arguments.length) { frequency = Boolean(x); return this; } return frequency; } }; }; /** * @class Represents a bin returned by the {@link pv.histogram} operator. Bins * are themselves arrays containing the data elements present in the given bin * (prior to the accessor function being invoked to convert the data object to a * numeric value). These bin arrays have additional attributes with meta * information about the bin. * * @name pv.histogram.Bin * @extends array * @see pv.histogram */ /** * The start value of the bin's range. * * @type number * @name pv.histogram.Bin.prototype.x */ /** * The magnitude value of the bin's range; end - start. * * @type number * @name pv.histogram.Bin.prototype.dx */ /** * The frequency or probability of the bin, depending on how the histogram * operator was configured. * * @type number * @name pv.histogram.Bin.prototype.y */ /** * Returns the {@link pv.Color} for the specified color format string. Colors * may have an associated opacity, or alpha channel. Color formats are specified * by CSS Color Modular Level 3, using either in RGB or HSL color space. For * example:

      * *
    • #f00 // #rgb *
    • #ff0000 // #rrggbb *
    • rgb(255, 0, 0) *
    • rgb(100%, 0%, 0%) *
    • hsl(0, 100%, 50%) *
    • rgba(0, 0, 255, 0.5) *
    • hsla(120, 100%, 50%, 1) * *
    The SVG 1.0 color keywords names are also supported, such as "aliceblue" * and "yellowgreen". The "transparent" keyword is supported for fully- * transparent black. * *

    If the format argument is already an instance of Color, * the argument is returned with no further processing. * * @param {string} format the color specification string, such as "#f00". * @returns {pv.Color} the corresponding Color. * @see SVG color * keywords * @see CSS3 color module */ pv.color = function(format) { if (format.rgb) return format.rgb(); /* Handle hsl, rgb. */ var m1 = /([a-z]+)\((.*)\)/i.exec(format); if (m1) { var m2 = m1[2].split(","), a = 1; switch (m1[1]) { case "hsla": case "rgba": { a = parseFloat(m2[3]); if (!a) return pv.Color.transparent; break; } } switch (m1[1]) { case "hsla": case "hsl": { var h = parseFloat(m2[0]), // degrees s = parseFloat(m2[1]) / 100, // percentage l = parseFloat(m2[2]) / 100; // percentage return (new pv.Color.Hsl(h, s, l, a)).rgb(); } case "rgba": case "rgb": { function parse(c) { // either integer or percentage var f = parseFloat(c); return (c[c.length - 1] == '%') ? Math.round(f * 2.55) : f; } var r = parse(m2[0]), g = parse(m2[1]), b = parse(m2[2]); return pv.rgb(r, g, b, a); } } } /* Named colors. */ var named = pv.Color.names[format]; if (named) return named; /* Hexadecimal colors: #rgb and #rrggbb. */ if (format.charAt(0) == "#") { var r, g, b; if (format.length == 4) { r = format.charAt(1); r += r; g = format.charAt(2); g += g; b = format.charAt(3); b += b; } else if (format.length == 7) { r = format.substring(1, 3); g = format.substring(3, 5); b = format.substring(5, 7); } return pv.rgb(parseInt(r, 16), parseInt(g, 16), parseInt(b, 16), 1); } /* Otherwise, pass-through unsupported colors. */ return new pv.Color(format, 1); }; /** * Constructs a color with the specified color format string and opacity. This * constructor should not be invoked directly; use {@link pv.color} instead. * * @class Represents an abstract (possibly translucent) color. The color is * divided into two parts: the color attribute, an opaque color format * string, and the opacity attribute, a float in [0, 1]. The color * space is dependent on the implementing class; all colors support the * {@link #rgb} method to convert to RGB color space for interpolation. * *

    See also the Color guide. * * @param {string} color an opaque color format string, such as "#f00". * @param {number} opacity the opacity, in [0,1]. * @see pv.color */ pv.Color = function(color, opacity) { /** * An opaque color format string, such as "#f00". * * @type string * @see SVG color * keywords * @see CSS3 color module */ this.color = color; /** * The opacity, a float in [0, 1]. * * @type number */ this.opacity = opacity; }; /** * Returns a new color that is a brighter version of this color. The behavior of * this method may vary slightly depending on the underlying color space. * Although brighter and darker are inverse operations, the results of a series * of invocations of these two methods might be inconsistent because of rounding * errors. * * @param [k] {number} an optional scale factor; defaults to 1. * @see #darker * @returns {pv.Color} a brighter color. */ pv.Color.prototype.brighter = function(k) { return this.rgb().brighter(k); }; /** * Returns a new color that is a brighter version of this color. The behavior of * this method may vary slightly depending on the underlying color space. * Although brighter and darker are inverse operations, the results of a series * of invocations of these two methods might be inconsistent because of rounding * errors. * * @param [k] {number} an optional scale factor; defaults to 1. * @see #brighter * @returns {pv.Color} a darker color. */ pv.Color.prototype.darker = function(k) { return this.rgb().darker(k); }; /** * Constructs a new RGB color with the specified channel values. * * @param {number} r the red channel, an integer in [0,255]. * @param {number} g the green channel, an integer in [0,255]. * @param {number} b the blue channel, an integer in [0,255]. * @param {number} [a] the alpha channel, a float in [0,1]. * @returns pv.Color.Rgb */ pv.rgb = function(r, g, b, a) { return new pv.Color.Rgb(r, g, b, (arguments.length == 4) ? a : 1); }; /** * Constructs a new RGB color with the specified channel values. * * @class Represents a color in RGB space. * * @param {number} r the red channel, an integer in [0,255]. * @param {number} g the green channel, an integer in [0,255]. * @param {number} b the blue channel, an integer in [0,255]. * @param {number} a the alpha channel, a float in [0,1]. * @extends pv.Color */ pv.Color.Rgb = function(r, g, b, a) { pv.Color.call(this, a ? ("rgb(" + r + "," + g + "," + b + ")") : "none", a); /** * The red channel, an integer in [0, 255]. * * @type number */ this.r = r; /** * The green channel, an integer in [0, 255]. * * @type number */ this.g = g; /** * The blue channel, an integer in [0, 255]. * * @type number */ this.b = b; /** * The alpha channel, a float in [0, 1]. * * @type number */ this.a = a; }; pv.Color.Rgb.prototype = pv.extend(pv.Color); /** * Constructs a new RGB color with the same green, blue and alpha channels as * this color, with the specified red channel. * * @param {number} r the red channel, an integer in [0,255]. */ pv.Color.Rgb.prototype.red = function(r) { return pv.rgb(r, this.g, this.b, this.a); }; /** * Constructs a new RGB color with the same red, blue and alpha channels as this * color, with the specified green channel. * * @param {number} g the green channel, an integer in [0,255]. */ pv.Color.Rgb.prototype.green = function(g) { return pv.rgb(this.r, g, this.b, this.a); }; /** * Constructs a new RGB color with the same red, green and alpha channels as * this color, with the specified blue channel. * * @param {number} b the blue channel, an integer in [0,255]. */ pv.Color.Rgb.prototype.blue = function(b) { return pv.rgb(this.r, this.g, b, this.a); }; /** * Constructs a new RGB color with the same red, green and blue channels as this * color, with the specified alpha channel. * * @param {number} a the alpha channel, a float in [0,1]. */ pv.Color.Rgb.prototype.alpha = function(a) { return pv.rgb(this.r, this.g, this.b, a); }; /** * Returns the RGB color equivalent to this color. This method is abstract and * must be implemented by subclasses. * * @returns {pv.Color.Rgb} an RGB color. * @function * @name pv.Color.prototype.rgb */ /** * Returns this. * * @returns {pv.Color.Rgb} this. */ pv.Color.Rgb.prototype.rgb = function() { return this; }; /** * Returns a new color that is a brighter version of this color. This method * applies an arbitrary scale factor to each of the three RGB components of this * color to create a brighter version of this color. Although brighter and * darker are inverse operations, the results of a series of invocations of * these two methods might be inconsistent because of rounding errors. * * @param [k] {number} an optional scale factor; defaults to 1. * @see #darker * @returns {pv.Color.Rgb} a brighter color. */ pv.Color.Rgb.prototype.brighter = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); var r = this.r, g = this.g, b = this.b, i = 30; if (!r && !g && !b) return pv.rgb(i, i, i, this.a); if (r && (r < i)) r = i; if (g && (g < i)) g = i; if (b && (b < i)) b = i; return pv.rgb( Math.min(255, Math.floor(r / k)), Math.min(255, Math.floor(g / k)), Math.min(255, Math.floor(b / k)), this.a); }; /** * Returns a new color that is a darker version of this color. This method * applies an arbitrary scale factor to each of the three RGB components of this * color to create a darker version of this color. Although brighter and darker * are inverse operations, the results of a series of invocations of these two * methods might be inconsistent because of rounding errors. * * @param [k] {number} an optional scale factor; defaults to 1. * @see #brighter * @returns {pv.Color.Rgb} a darker color. */ pv.Color.Rgb.prototype.darker = function(k) { k = Math.pow(0.7, arguments.length ? k : 1); return pv.rgb( Math.max(0, Math.floor(k * this.r)), Math.max(0, Math.floor(k * this.g)), Math.max(0, Math.floor(k * this.b)), this.a); }; /** * Constructs a new HSL color with the specified values. * * @param {number} h the hue, an integer in [0, 360]. * @param {number} s the saturation, a float in [0, 1]. * @param {number} l the lightness, a float in [0, 1]. * @param {number} [a] the opacity, a float in [0, 1]. * @returns pv.Color.Hsl */ pv.hsl = function(h, s, l, a) { return new pv.Color.Hsl(h, s, l, (arguments.length == 4) ? a : 1); }; /** * Constructs a new HSL color with the specified values. * * @class Represents a color in HSL space. * * @param {number} h the hue, an integer in [0, 360]. * @param {number} s the saturation, a float in [0, 1]. * @param {number} l the lightness, a float in [0, 1]. * @param {number} a the opacity, a float in [0, 1]. * @extends pv.Color */ pv.Color.Hsl = function(h, s, l, a) { pv.Color.call(this, "hsl(" + h + "," + (s * 100) + "%," + (l * 100) + "%)", a); /** * The hue, an integer in [0, 360]. * * @type number */ this.h = h; /** * The saturation, a float in [0, 1]. * * @type number */ this.s = s; /** * The lightness, a float in [0, 1]. * * @type number */ this.l = l; /** * The opacity, a float in [0, 1]. * * @type number */ this.a = a; }; pv.Color.Hsl.prototype = pv.extend(pv.Color); /** * Constructs a new HSL color with the same saturation, lightness and alpha as * this color, and the specified hue. * * @param {number} h the hue, an integer in [0, 360]. */ pv.Color.Hsl.prototype.hue = function(h) { return pv.hsl(h, this.s, this.l, this.a); }; /** * Constructs a new HSL color with the same hue, lightness and alpha as this * color, and the specified saturation. * * @param {number} s the saturation, a float in [0, 1]. */ pv.Color.Hsl.prototype.saturation = function(s) { return pv.hsl(this.h, s, this.l, this.a); }; /** * Constructs a new HSL color with the same hue, saturation and alpha as this * color, and the specified lightness. * * @param {number} l the lightness, a float in [0, 1]. */ pv.Color.Hsl.prototype.lightness = function(l) { return pv.hsl(this.h, this.s, l, this.a); }; /** * Constructs a new HSL color with the same hue, saturation and lightness as * this color, and the specified alpha. * * @param {number} a the opacity, a float in [0, 1]. */ pv.Color.Hsl.prototype.alpha = function(a) { return pv.hsl(this.h, this.s, this.l, a); }; /** * Returns the RGB color equivalent to this HSL color. * * @returns {pv.Color.Rgb} an RGB color. */ pv.Color.Hsl.prototype.rgb = function() { var h = this.h, s = this.s, l = this.l; /* Some simple corrections for h, s and l. */ h = h % 360; if (h < 0) h += 360; s = Math.max(0, Math.min(s, 1)); l = Math.max(0, Math.min(l, 1)); /* From FvD 13.37, CSS Color Module Level 3 */ var m2 = (l <= .5) ? (l * (1 + s)) : (l + s - l * s); var m1 = 2 * l - m2; function v(h) { if (h > 360) h -= 360; else if (h < 0) h += 360; if (h < 60) return m1 + (m2 - m1) * h / 60; if (h < 180) return m2; if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; return m1; } function vv(h) { return Math.round(v(h) * 255); } return pv.rgb(vv(h + 120), vv(h), vv(h - 120), this.a); }; /** * @private SVG color keywords, per CSS Color Module Level 3. * * @see SVG color * keywords */ pv.Color.names = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkgrey: "#a9a9a9", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", green: "#008000", greenyellow: "#adff2f", grey: "#808080", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32", transparent: pv.Color.transparent = pv.rgb(0, 0, 0, 0) }; /* Initialized named colors. */ (function() { var names = pv.Color.names; for (var name in names) names[name] = pv.color(names[name]); })(); /** * Returns a new categorical color encoding using the specified colors. The * arguments to this method are an array of colors; see {@link pv.color}. For * example, to create a categorical color encoding using the species * attribute: * *

    pv.colors("red", "green", "blue").by(function(d) d.species)
    * * The result of this expression can be used as a fill- or stroke-style * property. This assumes that the data's species attribute is a * string. * * @param {string} colors... categorical colors. * @see pv.Scale.ordinal * @returns {pv.Scale.ordinal} an ordinal color scale. */ pv.colors = function() { var scale = pv.Scale.ordinal(); scale.range.apply(scale, arguments); return scale; }; /** * A collection of standard color palettes for categorical encoding. * * @namespace A collection of standard color palettes for categorical encoding. */ pv.Colors = {}; /** * Returns a new 10-color scheme. The arguments to this constructor are * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The * following colors are used: * *
    #1f77b4
    *
    #ff7f0e
    *
    #2ca02c
    *
    #d62728
    *
    #9467bd
    *
    #8c564b
    *
    #e377c2
    *
    #7f7f7f
    *
    #bcbd22
    *
    #17becf
    * * @param {number...} domain... domain values. * @returns {pv.Scale.ordinal} a new ordinal color scale. * @see pv.color */ pv.Colors.category10 = function() { var scale = pv.colors( "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"); scale.domain.apply(scale, arguments); return scale; }; /** * Returns a new 20-color scheme. The arguments to this constructor are * optional, and equivalent to calling {@link pv.Scale.OrdinalScale#domain}. The * following colors are used: * *
    #1f77b4
    *
    #aec7e8
    *
    #ff7f0e
    *
    #ffbb78
    *
    #2ca02c
    *
    #98df8a
    *
    #d62728
    *
    #ff9896
    *
    #9467bd
    *
    #c5b0d5
    *
    #8c564b
    *
    #c49c94
    *
    #e377c2
    *
    #f7b6d2
    *
    #7f7f7f
    *
    #c7c7c7
    *
    #bcbd22
    *
    #dbdb8d
    *
    #17becf
    *
    #9edae5
    * * @param {number...} domain... domain values. * @returns {pv.Scale.ordinal} a new ordinal color scale. * @see pv.color */ pv.Colors.category20 = function() { var scale = pv.colors( "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5"); scale.domain.apply(scale, arguments); return scale; }; /** * Returns a new alternative 19-color scheme. The arguments to this constructor * are optional, and equivalent to calling * {@link pv.Scale.OrdinalScale#domain}. The following colors are used: * *
    #9c9ede
    *
    #7375b5
    *
    #4a5584
    *
    #cedb9c
    *
    #b5cf6b
    *
    #8ca252
    *
    #637939
    *
    #e7cb94
    *
    #e7ba52
    *
    #bd9e39
    *
    #8c6d31
    *
    #e7969c
    *
    #d6616b
    *
    #ad494a
    *
    #843c39
    *
    #de9ed6
    *
    #ce6dbd
    *
    #a55194
    *
    #7b4173
    * * @param {number...} domain... domain values. * @returns {pv.Scale.ordinal} a new ordinal color scale. * @see pv.color */ pv.Colors.category19 = function() { var scale = pv.colors( "#9c9ede", "#7375b5", "#4a5584", "#cedb9c", "#b5cf6b", "#8ca252", "#637939", "#e7cb94", "#e7ba52", "#bd9e39", "#8c6d31", "#e7969c", "#d6616b", "#ad494a", "#843c39", "#de9ed6", "#ce6dbd", "#a55194", "#7b4173"); scale.domain.apply(scale, arguments); return scale; }; /** * Returns a linear color ramp from the specified start color to the * specified end color. The color arguments may be specified either as * strings or as {@link pv.Color}s. This is equivalent to: * *
        pv.Scale.linear().domain(0, 1).range(...)
    * * @param {string} start the start color; may be a pv.Color. * @param {string} end the end color; may be a pv.Color. * @returns {Function} a color ramp from start to end. * @see pv.Scale.linear */ pv.ramp = function(start, end) { var scale = pv.Scale.linear(); scale.range.apply(scale, arguments); return scale; }; /** * @private * @namespace */ pv.Scene = pv.SvgScene = { /* Various namespaces. */ svg: "http://www.w3.org/2000/svg", xmlns: "http://www.w3.org/2000/xmlns", xlink: "http://www.w3.org/1999/xlink", xhtml: "http://www.w3.org/1999/xhtml", /** The pre-multipled scale, based on any enclosing transforms. */ scale: 1, /** The set of supported events. */ events: [ "DOMMouseScroll", // for Firefox "mousewheel", "mousedown", "mouseup", "mouseover", "mouseout", "mousemove", "click", "dblclick" ], /** Implicit values for SVG and CSS properties. */ implicit: { svg: { "shape-rendering": "auto", "pointer-events": "painted", "x": 0, "y": 0, "dy": 0, "text-anchor": "start", "transform": "translate(0,0)", "fill": "none", "fill-opacity": 1, "stroke": "none", "stroke-opacity": 1, "stroke-width": 1.5, "stroke-linejoin": "miter" }, css: { "font": "10px sans-serif" } } }; /** * Updates the display for the specified array of scene nodes. * * @param scenes {array} an array of scene nodes. */ pv.SvgScene.updateAll = function(scenes) { if (scenes.length && scenes[0].reverse && (scenes.type != "line") && (scenes.type != "area")) { var reversed = pv.extend(scenes); for (var i = 0, j = scenes.length - 1; j >= 0; i++, j--) { reversed[i] = scenes[j]; } scenes = reversed; } this.removeSiblings(this[scenes.type](scenes)); }; /** * Creates a new SVG element of the specified type. * * @param type {string} an SVG element type, such as "rect". * @returns a new SVG element. */ pv.SvgScene.create = function(type) { return document.createElementNS(this.svg, type); }; /** * Expects the element e to be the specified type. If the element does * not exist, a new one is created. If the element does exist but is the wrong * type, it is replaced with the specified element. * * @param e the current SVG element. * @param type {string} an SVG element type, such as "rect". * @param attributes an optional attribute map. * @param style an optional style map. * @returns a new SVG element. */ pv.SvgScene.expect = function(e, type, attributes, style) { if (e) { if (e.tagName == "a") e = e.firstChild; if (e.tagName != type) { var n = this.create(type); e.parentNode.replaceChild(n, e); e = n; } } else { e = this.create(type); } for (var name in attributes) { var value = attributes[name]; if (value == this.implicit.svg[name]) value = null; if (value == null) e.removeAttribute(name); else e.setAttribute(name, value); } for (var name in style) { var value = style[name]; if (value == this.implicit.css[name]) value = null; if (value == null) e.style.removeProperty(name); else e.style[name] = value; } return e; }; /** TODO */ pv.SvgScene.append = function(e, scenes, index) { e.$scene = {scenes:scenes, index:index}; e = this.title(e, scenes[index]); if (!e.parentNode) scenes.$g.appendChild(e); return e.nextSibling; }; /** * Applies a title tooltip to the specified element e, using the * title property of the specified scene node s. Note that * this implementation does not create an SVG title element as a child * of e; although this is the recommended standard, it is only * supported in Opera. Instead, an anchor element is created around the element * e, and the xlink:title attribute is set accordingly. * * @param e an SVG element. * @param s a scene node. */ pv.SvgScene.title = function(e, s) { var a = e.parentNode; if (a && (a.tagName != "a")) a = null; if (s.title) { if (!a) { a = this.create("a"); if (e.parentNode) e.parentNode.replaceChild(a, e); a.appendChild(e); } a.setAttributeNS(this.xlink, "title", s.title); return a; } if (a) a.parentNode.replaceChild(e, a); return e; }; /** TODO */ pv.SvgScene.dispatch = pv.listener(function(e) { var t = e.target.$scene; if (t) { var type = e.type; /* Fixes for mousewheel support on Firefox & Opera. */ switch (type) { case "DOMMouseScroll": { type = "mousewheel"; e.wheel = -480 * e.detail; break; } case "mousewheel": { e.wheel = (window.opera ? 12 : 1) * e.wheelDelta; break; } } if (pv.Mark.dispatch(type, t.scenes, t.index)) e.preventDefault(); } }); /** @private Remove siblings following element e. */ pv.SvgScene.removeSiblings = function(e) { while (e) { var n = e.nextSibling; e.parentNode.removeChild(e); e = n; } }; /** @private Do nothing when rendering undefined mark types. */ pv.SvgScene.undefined = function() {}; /** * @private Converts the specified b-spline curve segment to a bezier curve * compatible with SVG "C". * * @param p0 the first control point. * @param p1 the second control point. * @param p2 the third control point. * @param p3 the fourth control point. */ pv.SvgScene.pathBasis = (function() { /** * Matrix to transform basis (b-spline) control points to bezier control * points. Derived from FvD 11.2.8. */ var basis = [ [ 1/6, 2/3, 1/6, 0 ], [ 0, 2/3, 1/3, 0 ], [ 0, 1/3, 2/3, 0 ], [ 0, 1/6, 2/3, 1/6 ] ]; /** * Returns the point that is the weighted sum of the specified control points, * using the specified weights. This method requires that there are four * weights and four control points. */ function weight(w, p0, p1, p2, p3) { return { x: w[0] * p0.left + w[1] * p1.left + w[2] * p2.left + w[3] * p3.left, y: w[0] * p0.top + w[1] * p1.top + w[2] * p2.top + w[3] * p3.top }; } var convert = function(p0, p1, p2, p3) { var b1 = weight(basis[1], p0, p1, p2, p3), b2 = weight(basis[2], p0, p1, p2, p3), b3 = weight(basis[3], p0, p1, p2, p3); return "C" + b1.x + "," + b1.y + "," + b2.x + "," + b2.y + "," + b3.x + "," + b3.y; }; convert.segment = function(p0, p1, p2, p3) { var b0 = weight(basis[0], p0, p1, p2, p3), b1 = weight(basis[1], p0, p1, p2, p3), b2 = weight(basis[2], p0, p1, p2, p3), b3 = weight(basis[3], p0, p1, p2, p3); return "M" + b0.x + "," + b0.y + "C" + b1.x + "," + b1.y + "," + b2.x + "," + b2.y + "," + b3.x + "," + b3.y; }; return convert; })(); /** * @private Interpolates the given points using the basis spline interpolation. * Returns an SVG path without the leading M instruction to allow path * appending. * * @param points the array of points. */ pv.SvgScene.curveBasis = function(points) { if (points.length <= 2) return ""; var path = "", p0 = points[0], p1 = p0, p2 = p0, p3 = points[1]; path += this.pathBasis(p0, p1, p2, p3); for (var i = 2; i < points.length; i++) { p0 = p1; p1 = p2; p2 = p3; p3 = points[i]; path += this.pathBasis(p0, p1, p2, p3); } /* Cycle through to get the last point. */ path += this.pathBasis(p1, p2, p3, p3); path += this.pathBasis(p2, p3, p3, p3); return path; }; /** * @private Interpolates the given points using the basis spline interpolation. * If points.length == tangents.length then a regular Hermite interpolation is * performed, if points.length == tangents.length + 2 then the first and last * segments are filled in with cubic bazier segments. Returns an array of path * strings. * * @param points the array of points. */ pv.SvgScene.curveBasisSegments = function(points) { if (points.length <= 2) return ""; var paths = [], p0 = points[0], p1 = p0, p2 = p0, p3 = points[1], firstPath = this.pathBasis.segment(p0, p1, p2, p3); p0 = p1; p1 = p2; p2 = p3; p3 = points[2]; paths.push(firstPath + this.pathBasis(p0, p1, p2, p3)); // merge first & second path for (var i = 3; i < points.length; i++) { p0 = p1; p1 = p2; p2 = p3; p3 = points[i]; paths.push(this.pathBasis.segment(p0, p1, p2, p3)); } // merge last & second-to-last path paths.push(this.pathBasis.segment(p1, p2, p3, p3) + this.pathBasis(p2, p3, p3, p3)); return paths; }; /** * @private Interpolates the given points with respective tangents using the cubic * Hermite spline interpolation. If points.length == tangents.length then a regular * Hermite interpolation is performed, if points.length == tangents.length + 2 then * the first and last segments are filled in with cubic bazier segments. * Returns an SVG path without the leading M instruction to allow path appending. * * @param points the array of points. * @param tangents the array of tangent vectors. */ pv.SvgScene.curveHermite = function(points, tangents) { if (tangents.length < 1 || (points.length != tangents.length && points.length != tangents.length + 2)) return ""; var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; if (quad) { path += "Q" + (p.left - t0.x * 2 / 3) + "," + (p.top - t0.y * 2 / 3) + "," + p.left + "," + p.top; p0 = points[1]; pi = 2; } if (tangents.length > 1) { t = tangents[1]; p = points[pi]; pi++; path += "C" + (p0.left + t0.x) + "," + (p0.top + t0.y) + "," + (p.left - t.x) + "," + (p.top - t.y) + "," + p.left + "," + p.top; for (var i = 2; i < tangents.length; i++, pi++) { p = points[pi]; t = tangents[i]; path += "S" + (p.left - t.x) + "," + (p.top - t.y) + "," + p.left + "," + p.top; } } if (quad) { var lp = points[pi]; path += "Q" + (p.left + t.x * 2 / 3) + "," + (p.top + t.y * 2 / 3) + "," + lp.left + "," + lp.top; } return path; }; /** * @private Interpolates the given points with respective tangents using the * cubic Hermite spline interpolation. Returns an array of path strings. * * @param points the array of points. * @param tangents the array of tangent vectors. */ pv.SvgScene.curveHermiteSegments = function(points, tangents) { if (tangents.length < 1 || (points.length != tangents.length && points.length != tangents.length + 2)) return []; var quad = points.length != tangents.length, paths = [], p0 = points[0], p = p0, t0 = tangents[0], t = t0, pi = 1; if (quad) { p = points[1]; paths.push("M" + p0.left + "," + p0.top + "Q" + (p.left - t.x * 2 / 3) + "," + (p.top - t.y * 2 / 3) + "," + p.left + "," + p.top); pi = 2; } for (var i = 1; i < tangents.length; i++, pi++) { p0 = p; t0 = t; p = points[pi]; t = tangents[i]; paths.push("M" + p0.left + "," + p0.top + "C" + (p0.left + t0.x) + "," + (p0.top + t0.y) + "," + (p.left - t.x) + "," + (p.top - t.y) + "," + p.left + "," + p.top); } if (quad) { var lp = points[pi]; paths.push("M" + p.left + "," + p.top + "Q" + (p.left + t.x * 2 / 3) + "," + (p.top + t.y * 2 / 3) + "," + lp.left + "," + lp.top); } return paths; }; /** * @private Computes the tangents for the given points needed for cardinal * spline interpolation. Returns an array of tangent vectors. Note: that for n * points only the n-2 well defined tangents are returned. * * @param points the array of points. * @param tension the tension of hte cardinal spline. */ pv.SvgScene.cardinalTangents = function(points, tension) { var tangents = [], a = (1 - tension) / 2, p0 = points[0], p1 = points[1], p2 = points[2]; for (var i = 3; i < points.length; i++) { tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)}); p0 = p1; p1 = p2; p2 = points[i]; } tangents.push({x: a * (p2.left - p0.left), y: a * (p2.top - p0.top)}); return tangents; }; /** * @private Interpolates the given points using cardinal spline interpolation. * Returns an SVG path without the leading M instruction to allow path * appending. * * @param points the array of points. * @param tension the tension of hte cardinal spline. */ pv.SvgScene.curveCardinal = function(points, tension) { if (points.length <= 2) return ""; return this.curveHermite(points, this.cardinalTangents(points, tension)); }; /** * @private Interpolates the given points using cardinal spline interpolation. * Returns an array of path strings. * * @param points the array of points. * @param tension the tension of hte cardinal spline. */ pv.SvgScene.curveCardinalSegments = function(points, tension) { if (points.length <= 2) return ""; return this.curveHermiteSegments(points, this.cardinalTangents(points, tension)); }; /** * @private Interpolates the given points using Fritsch-Carlson Monotone cubic * Hermite interpolation. Returns an array of tangent vectors. * * @param points the array of points. */ pv.SvgScene.monotoneTangents = function(points) { var tangents = [], d = [], m = [], dx = [], k = 0; /* Compute the slopes of the secant lines between successive points. */ for (k = 0; k < points.length-1; k++) { d[k] = (points[k+1].top - points[k].top)/(points[k+1].left - points[k].left); } /* Initialize the tangents at every point as the average of the secants. */ m[0] = d[0]; dx[0] = points[1].left - points[0].left; for (k = 1; k < points.length - 1; k++) { m[k] = (d[k-1]+d[k])/2; dx[k] = (points[k+1].left - points[k-1].left)/2; } m[k] = d[k-1]; dx[k] = (points[k].left - points[k-1].left); /* Step 3. Very important, step 3. Yep. Wouldn't miss it. */ for (k = 0; k < points.length - 1; k++) { if (d[k] == 0) { m[ k ] = 0; m[k+1] = 0; } } /* Step 4 + 5. Out of 5 or more steps. */ for (k = 0; k < points.length - 1; k++) { if ((Math.abs(m[k]) < 1e-5) || (Math.abs(m[k+1]) < 1e-5)) continue; var ak = m[k] / d[k], bk = m[k + 1] / d[k], s = ak * ak + bk * bk; // monotone constant (?) if (s > 9) { var tk = 3 / Math.sqrt(s); m[k] = tk * ak * d[k]; m[k + 1] = tk * bk * d[k]; } } var len; for (var i = 0; i < points.length; i++) { len = 1 + m[i] * m[i]; // pv.vector(1, m[i]).norm().times(dx[i]/3) tangents.push({x: dx[i] / 3 / len, y: m[i] * dx[i] / 3 / len}); } return tangents; }; /** * @private Interpolates the given points using Fritsch-Carlson Monotone cubic * Hermite interpolation. Returns an SVG path without the leading M instruction * to allow path appending. * * @param points the array of points. */ pv.SvgScene.curveMonotone = function(points) { if (points.length <= 2) return ""; return this.curveHermite(points, this.monotoneTangents(points)); } /** * @private Interpolates the given points using Fritsch-Carlson Monotone cubic * Hermite interpolation. * Returns an array of path strings. * * @param points the array of points. */ pv.SvgScene.curveMonotoneSegments = function(points) { if (points.length <= 2) return ""; return this.curveHermiteSegments(points, this.monotoneTangents(points)); }; pv.SvgScene.area = function(scenes) { var e = scenes.$g.firstChild; if (!scenes.length) return e; var s = scenes[0]; /* segmented */ if (s.segmented) return this.areaSegment(scenes); /* visible */ if (!s.visible) return e; var fill = s.fillStyle, stroke = s.strokeStyle; if (!fill.opacity && !stroke.opacity) return e; /** @private Computes the straight path for the range [i, j]. */ function path(i, j) { var p1 = [], p2 = []; for (var k = j; i <= k; i++, j--) { var si = scenes[i], sj = scenes[j], pi = si.left + "," + si.top, pj = (sj.left + sj.width) + "," + (sj.top + sj.height); /* interpolate */ if (i < k) { var sk = scenes[i + 1], sl = scenes[j - 1]; switch (s.interpolate) { case "step-before": { pi += "V" + sk.top; pj += "H" + (sl.left + sl.width); break; } case "step-after": { pi += "H" + sk.left; pj += "V" + (sl.top + sl.height); break; } } } p1.push(pi); p2.push(pj); } return p1.concat(p2).join("L"); } /** @private Computes the curved path for the range [i, j]. */ function pathCurve(i, j) { var pointsT = [], pointsB = [], pathT, pathB; for (var k = j; i <= k; i++, j--) { var sj = scenes[j]; pointsT.push(scenes[i]); pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height}); } if (s.interpolate == "basis") { pathT = pv.SvgScene.curveBasis(pointsT); pathB = pv.SvgScene.curveBasis(pointsB); } else if (s.interpolate == "cardinal") { pathT = pv.SvgScene.curveCardinal(pointsT, s.tension); pathB = pv.SvgScene.curveCardinal(pointsB, s.tension); } else { // monotone pathT = pv.SvgScene.curveMonotone(pointsT); pathB = pv.SvgScene.curveMonotone(pointsB); } return pointsT[0].left + "," + pointsT[0].top + pathT + "L" + pointsB[0].left + "," + pointsB[0].top + pathB; } /* points */ var d = [], si, sj; for (var i = 0; i < scenes.length; i++) { si = scenes[i]; if (!si.width && !si.height) continue; for (var j = i + 1; j < scenes.length; j++) { sj = scenes[j]; if (!sj.width && !sj.height) break; } if (i && (s.interpolate != "step-after")) i--; if ((j < scenes.length) && (s.interpolate != "step-before")) j++; d.push(((j - i > 2 && (s.interpolate == "basis" || s.interpolate == "cardinal" || s.interpolate == "monotone")) ? pathCurve : path)(i, j - 1)); i = j - 1; } if (!d.length) return e; e = this.expect(e, "path", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "d": "M" + d.join("ZM") + "Z", "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null }); return this.append(e, scenes, 0); }; pv.SvgScene.areaSegment = function(scenes) { var e = scenes.$g.firstChild, s = scenes[0], pathsT, pathsB; if (s.interpolate == "basis" || s.interpolate == "cardinal" || s.interpolate == "monotone") { var pointsT = [], pointsB = []; for (var i = 0, n = scenes.length; i < n; i++) { var sj = scenes[n - i - 1]; pointsT.push(scenes[i]); pointsB.push({left: sj.left + sj.width, top: sj.top + sj.height}); } if (s.interpolate == "basis") { pathsT = this.curveBasisSegments(pointsT); pathsB = this.curveBasisSegments(pointsB); } else if (s.interpolate == "cardinal") { pathsT = this.curveCardinalSegments(pointsT, s.tension); pathsB = this.curveCardinalSegments(pointsB, s.tension); } else { // monotone pathsT = this.curveMonotoneSegments(pointsT); pathsB = this.curveMonotoneSegments(pointsB); } } for (var i = 0, n = scenes.length - 1; i < n; i++) { var s1 = scenes[i], s2 = scenes[i + 1]; /* visible */ if (!s1.visible || !s2.visible) continue; var fill = s1.fillStyle, stroke = s1.strokeStyle; if (!fill.opacity && !stroke.opacity) continue; var d; if (pathsT) { var pathT = pathsT[i], pathB = "L" + pathsB[n - i - 1].substr(1); d = pathT + pathB + "Z"; } else { /* interpolate */ var si = s1, sj = s2; switch (s1.interpolate) { case "step-before": si = s2; break; case "step-after": sj = s1; break; } /* path */ d = "M" + s1.left + "," + si.top + "L" + s2.left + "," + sj.top + "L" + (s2.left + s2.width) + "," + (sj.top + sj.height) + "L" + (s1.left + s1.width) + "," + (si.top + si.height) + "Z"; } e = this.expect(e, "path", { "shape-rendering": s1.antialias ? null : "crispEdges", "pointer-events": s1.events, "cursor": s1.cursor, "d": d, "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null }); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.bar = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; var fill = s.fillStyle, stroke = s.strokeStyle; if (!fill.opacity && !stroke.opacity) continue; e = this.expect(e, "rect", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "x": s.left, "y": s.top, "width": Math.max(1E-10, s.width), "height": Math.max(1E-10, s.height), "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null }); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.dot = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; var fill = s.fillStyle, stroke = s.strokeStyle; if (!fill.opacity && !stroke.opacity) continue; /* points */ var radius = s.radius, path = null; switch (s.shape) { case "cross": { path = "M" + -radius + "," + -radius + "L" + radius + "," + radius + "M" + radius + "," + -radius + "L" + -radius + "," + radius; break; } case "triangle": { var h = radius, w = radius * 1.1547; // 2 / Math.sqrt(3) path = "M0," + h + "L" + w +"," + -h + " " + -w + "," + -h + "Z"; break; } case "diamond": { radius *= Math.SQRT2; path = "M0," + -radius + "L" + radius + ",0" + " 0," + radius + " " + -radius + ",0" + "Z"; break; } case "square": { path = "M" + -radius + "," + -radius + "L" + radius + "," + -radius + " " + radius + "," + radius + " " + -radius + "," + radius + "Z"; break; } case "tick": { path = "M0,0L0," + -s.size; break; } case "bar": { path = "M0," + (s.size / 2) + "L0," + -(s.size / 2); break; } } /* Use for circles, for everything else. */ var svg = { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null }; if (path) { svg.transform = "translate(" + s.left + "," + s.top + ")"; if (s.angle) svg.transform += " rotate(" + 180 * s.angle / Math.PI + ")"; svg.d = path; e = this.expect(e, "path", svg); } else { svg.cx = s.left; svg.cy = s.top; svg.r = radius; e = this.expect(e, "circle", svg); } e = this.append(e, scenes, i); } return e; }; pv.SvgScene.image = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; /* fill */ e = this.fill(e, scenes, i); /* image */ if (s.image) { e = this.expect(e, "foreignObject", { "cursor": s.cursor, "x": s.left, "y": s.top, "width": s.width, "height": s.height }); var c = e.firstChild || e.appendChild(document.createElementNS(this.xhtml, "canvas")); c.$scene = {scenes:scenes, index:i}; c.style.width = s.width; c.style.height = s.height; c.width = s.imageWidth; c.height = s.imageHeight; c.getContext("2d").putImageData(s.image, 0, 0); } else { e = this.expect(e, "image", { "preserveAspectRatio": "none", "cursor": s.cursor, "x": s.left, "y": s.top, "width": s.width, "height": s.height }); e.setAttributeNS(this.xlink, "href", s.url); } e = this.append(e, scenes, i); /* stroke */ e = this.stroke(e, scenes, i); } return e; }; pv.SvgScene.label = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; var fill = s.textStyle; if (!fill.opacity || !s.text) continue; /* text-baseline, text-align */ var x = 0, y = 0, dy = 0, anchor = "start"; switch (s.textBaseline) { case "middle": dy = ".35em"; break; case "top": dy = ".71em"; y = s.textMargin; break; case "bottom": y = "-" + s.textMargin; break; } switch (s.textAlign) { case "right": anchor = "end"; x = "-" + s.textMargin; break; case "center": anchor = "middle"; break; case "left": x = s.textMargin; break; } e = this.expect(e, "text", { "pointer-events": s.events, "cursor": s.cursor, "x": x, "y": y, "dy": dy, "transform": "translate(" + s.left + "," + s.top + ")" + (s.textAngle ? " rotate(" + 180 * s.textAngle / Math.PI + ")" : "") + (this.scale != 1 ? " scale(" + 1 / this.scale + ")" : ""), "fill": fill.color, "fill-opacity": fill.opacity || null, "text-anchor": anchor }, { "font": s.font, "text-shadow": s.textShadow, "text-decoration": s.textDecoration }); if (e.firstChild) e.firstChild.nodeValue = s.text; else e.appendChild(document.createTextNode(s.text)); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.line = function(scenes) { var e = scenes.$g.firstChild; if (scenes.length < 2) return e; var s = scenes[0]; /* segmented */ if (s.segmented) return this.lineSegment(scenes); /* visible */ if (!s.visible) return e; var fill = s.fillStyle, stroke = s.strokeStyle; if (!fill.opacity && !stroke.opacity) return e; /* points */ var d = "M" + s.left + "," + s.top; if (scenes.length > 2 && (s.interpolate == "basis" || s.interpolate == "cardinal" || s.interpolate == "monotone")) { switch (s.interpolate) { case "basis": d += this.curveBasis(scenes); break; case "cardinal": d += this.curveCardinal(scenes, s.tension); break; case "monotone": d += this.curveMonotone(scenes); break; } } else { for (var i = 1; i < scenes.length; i++) { d += this.pathSegment(scenes[i - 1], scenes[i]); } } e = this.expect(e, "path", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "d": d, "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null, "stroke-linejoin": s.lineJoin }); return this.append(e, scenes, 0); }; pv.SvgScene.lineSegment = function(scenes) { var e = scenes.$g.firstChild; var s = scenes[0]; var paths; switch (s.interpolate) { case "basis": paths = this.curveBasisSegments(scenes); break; case "cardinal": paths = this.curveCardinalSegments(scenes, s.tension); break; case "monotone": paths = this.curveMonotoneSegments(scenes); break; } for (var i = 0, n = scenes.length - 1; i < n; i++) { var s1 = scenes[i], s2 = scenes[i + 1]; /* visible */ if (!s1.visible || !s2.visible) continue; var stroke = s1.strokeStyle, fill = pv.Color.transparent; if (!stroke.opacity) continue; /* interpolate */ var d; if ((s1.interpolate == "linear") && (s1.lineJoin == "miter")) { fill = stroke; stroke = pv.Color.transparent; d = this.pathJoin(scenes[i - 1], s1, s2, scenes[i + 2]); } else if(paths) { d = paths[i]; } else { d = "M" + s1.left + "," + s1.top + this.pathSegment(s1, s2); } e = this.expect(e, "path", { "shape-rendering": s1.antialias ? null : "crispEdges", "pointer-events": s1.events, "cursor": s1.cursor, "d": d, "fill": fill.color, "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s1.lineWidth / this.scale : null, "stroke-linejoin": s1.lineJoin }); e = this.append(e, scenes, i); } return e; }; /** @private Returns the path segment for the specified points. */ pv.SvgScene.pathSegment = function(s1, s2) { var l = 1; // sweep-flag switch (s1.interpolate) { case "polar-reverse": l = 0; case "polar": { var dx = s2.left - s1.left, dy = s2.top - s1.top, e = 1 - s1.eccentricity, r = Math.sqrt(dx * dx + dy * dy) / (2 * e); if ((e <= 0) || (e > 1)) break; // draw a straight line return "A" + r + "," + r + " 0 0," + l + " " + s2.left + "," + s2.top; } case "step-before": return "V" + s2.top + "H" + s2.left; case "step-after": return "H" + s2.left + "V" + s2.top; } return "L" + s2.left + "," + s2.top; }; /** @private Line-line intersection, per Akenine-Moller 16.16.1. */ pv.SvgScene.lineIntersect = function(o1, d1, o2, d2) { return o1.plus(d1.times(o2.minus(o1).dot(d2.perp()) / d1.dot(d2.perp()))); } /** @private Returns the miter join path for the specified points. */ pv.SvgScene.pathJoin = function(s0, s1, s2, s3) { /* * P1-P2 is the current line segment. V is a vector that is perpendicular to * the line segment, and has length lineWidth / 2. ABCD forms the initial * bounding box of the line segment (i.e., the line segment if we were to do * no joins). */ var p1 = pv.vector(s1.left, s1.top), p2 = pv.vector(s2.left, s2.top), p = p2.minus(p1), v = p.perp().norm(), w = v.times(s1.lineWidth / (2 * this.scale)), a = p1.plus(w), b = p2.plus(w), c = p2.minus(w), d = p1.minus(w); /* * Start join. P0 is the previous line segment's start point. We define the * cutting plane as the average of the vector perpendicular to P0-P1, and * the vector perpendicular to P1-P2. This insures that the cross-section of * the line on the cutting plane is equal if the line-width is unchanged. * Note that we don't implement miter limits, so these can get wild. */ if (s0 && s0.visible) { var v1 = p1.minus(s0.left, s0.top).perp().norm().plus(v); d = this.lineIntersect(p1, v1, d, p); a = this.lineIntersect(p1, v1, a, p); } /* Similarly, for end join. */ if (s3 && s3.visible) { var v2 = pv.vector(s3.left, s3.top).minus(p2).perp().norm().plus(v); c = this.lineIntersect(p2, v2, c, p); b = this.lineIntersect(p2, v2, b, p); } return "M" + a.x + "," + a.y + "L" + b.x + "," + b.y + " " + c.x + "," + c.y + " " + d.x + "," + d.y; }; pv.SvgScene.panel = function(scenes) { var g = scenes.$g, e = g && g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; /* svg */ if (!scenes.parent) { s.canvas.style.display = "inline-block"; if (g && (g.parentNode != s.canvas)) { g = s.canvas.firstChild; e = g && g.firstChild; } if (!g) { g = s.canvas.appendChild(this.create("svg")); g.setAttribute("font-size", "10px"); g.setAttribute("font-family", "sans-serif"); g.setAttribute("fill", "none"); g.setAttribute("stroke", "none"); g.setAttribute("stroke-width", 1.5); for (var j = 0; j < this.events.length; j++) { g.addEventListener(this.events[j], this.dispatch, false); } e = g.firstChild; } scenes.$g = g; g.setAttribute("width", s.width + s.left + s.right); g.setAttribute("height", s.height + s.top + s.bottom); } /* clip (nest children) */ if (s.overflow == "hidden") { var id = pv.id().toString(36), c = this.expect(e, "g", {"clip-path": "url(#" + id + ")"}); if (!c.parentNode) g.appendChild(c); scenes.$g = g = c; e = c.firstChild; e = this.expect(e, "clipPath", {"id": id}); var r = e.firstChild || e.appendChild(this.create("rect")); r.setAttribute("x", s.left); r.setAttribute("y", s.top); r.setAttribute("width", s.width); r.setAttribute("height", s.height); if (!e.parentNode) g.appendChild(e); e = e.nextSibling; } /* fill */ e = this.fill(e, scenes, i); /* transform (push) */ var k = this.scale, t = s.transform, x = s.left + t.x, y = s.top + t.y; this.scale *= t.k; /* children */ for (var j = 0; j < s.children.length; j++) { s.children[j].$g = e = this.expect(e, "g", { "transform": "translate(" + x + "," + y + ")" + (t.k != 1 ? " scale(" + t.k + ")" : "") }); this.updateAll(s.children[j]); if (!e.parentNode) g.appendChild(e); e = e.nextSibling; } /* transform (pop) */ this.scale = k; /* stroke */ e = this.stroke(e, scenes, i); /* clip (restore group) */ if (s.overflow == "hidden") { scenes.$g = g = c.parentNode; e = c.nextSibling; } } return e; }; pv.SvgScene.fill = function(e, scenes, i) { var s = scenes[i], fill = s.fillStyle; if (fill.opacity || s.events == "all") { e = this.expect(e, "rect", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "x": s.left, "y": s.top, "width": s.width, "height": s.height, "fill": fill.color, "fill-opacity": fill.opacity, "stroke": null }); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.stroke = function(e, scenes, i) { var s = scenes[i], stroke = s.strokeStyle; if (stroke.opacity || s.events == "all") { e = this.expect(e, "rect", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events == "all" ? "stroke" : s.events, "cursor": s.cursor, "x": s.left, "y": s.top, "width": Math.max(1E-10, s.width), "height": Math.max(1E-10, s.height), "fill": null, "stroke": stroke.color, "stroke-opacity": stroke.opacity, "stroke-width": s.lineWidth / this.scale }); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.rule = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; var stroke = s.strokeStyle; if (!stroke.opacity) continue; e = this.expect(e, "line", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "x1": s.left, "y1": s.top, "x2": s.left + s.width, "y2": s.top + s.height, "stroke": stroke.color, "stroke-opacity": stroke.opacity, "stroke-width": s.lineWidth / this.scale }); e = this.append(e, scenes, i); } return e; }; pv.SvgScene.wedge = function(scenes) { var e = scenes.$g.firstChild; for (var i = 0; i < scenes.length; i++) { var s = scenes[i]; /* visible */ if (!s.visible) continue; var fill = s.fillStyle, stroke = s.strokeStyle; if (!fill.opacity && !stroke.opacity) continue; /* points */ var r1 = s.innerRadius, r2 = s.outerRadius, a = Math.abs(s.angle), p; if (a >= 2 * Math.PI) { if (r1) { p = "M0," + r2 + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2) + "A" + r2 + "," + r2 + " 0 1,1 0," + r2 + "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + (-r1) + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z"; } else { p = "M0," + r2 + "A" + r2 + "," + r2 + " 0 1,1 0," + (-r2) + "A" + r2 + "," + r2 + " 0 1,1 0," + r2 + "Z"; } } else { var sa = Math.min(s.startAngle, s.endAngle), ea = Math.max(s.startAngle, s.endAngle), c1 = Math.cos(sa), c2 = Math.cos(ea), s1 = Math.sin(sa), s2 = Math.sin(ea); if (r1) { p = "M" + r2 * c1 + "," + r2 * s1 + "A" + r2 + "," + r2 + " 0 " + ((a < Math.PI) ? "0" : "1") + ",1 " + r2 * c2 + "," + r2 * s2 + "L" + r1 * c2 + "," + r1 * s2 + "A" + r1 + "," + r1 + " 0 " + ((a < Math.PI) ? "0" : "1") + ",0 " + r1 * c1 + "," + r1 * s1 + "Z"; } else { p = "M" + r2 * c1 + "," + r2 * s1 + "A" + r2 + "," + r2 + " 0 " + ((a < Math.PI) ? "0" : "1") + ",1 " + r2 * c2 + "," + r2 * s2 + "L0,0Z"; } } e = this.expect(e, "path", { "shape-rendering": s.antialias ? null : "crispEdges", "pointer-events": s.events, "cursor": s.cursor, "transform": "translate(" + s.left + "," + s.top + ")", "d": p, "fill": fill.color, "fill-rule": "evenodd", "fill-opacity": fill.opacity || null, "stroke": stroke.color, "stroke-opacity": stroke.opacity || null, "stroke-width": stroke.opacity ? s.lineWidth / this.scale : null }); e = this.append(e, scenes, i); } return e; }; /** * Constructs a new mark with default properties. Marks, with the exception of * the root panel, are not typically constructed directly; instead, they are * added to a panel or an existing mark via {@link pv.Mark#add}. * * @class Represents a data-driven graphical mark. The Mark class is * the base class for all graphical marks in Protovis; it does not provide any * specific rendering functionality, but together with {@link Panel} establishes * the core framework. * *

    Concrete mark types include familiar visual elements such as bars, lines * and labels. Although a bar mark may be used to construct a bar chart, marks * know nothing about charts; it is only through their specification and * composition that charts are produced. These building blocks permit many * combinatorial possibilities. * *

    Marks are associated with data: a mark is generated once per * associated datum, mapping the datum to visual properties such as * position and color. Thus, a single mark specification represents a set of * visual elements that share the same data and visual encoding. The type of * mark defines the names of properties and their meaning. A property may be * static, ignoring the associated datum and returning a constant; or, it may be * dynamic, derived from the associated datum or index. Such dynamic encodings * can be specified succinctly using anonymous functions. Special properties * called event handlers can be registered to add interactivity. * *

    Protovis uses inheritance to simplify the specification of related * marks: a new mark can be derived from an existing mark, inheriting its * properties. The new mark can then override properties to specify new * behavior, potentially in terms of the old behavior. In this way, the old mark * serves as the prototype for the new mark. Most mark types share the * same basic properties for consistency and to facilitate inheritance. * *

    The prioritization of redundant properties is as follows:

      * *
    1. If the width property is not specified (i.e., null), its value * is the width of the parent panel, minus this mark's left and right margins; * the left and right margins are zero if not specified. * *
    2. Otherwise, if the right margin is not specified, its value is * the width of the parent panel, minus this mark's width and left margin; the * left margin is zero if not specified. * *
    3. Otherwise, if the left property is not specified, its value is * the width of the parent panel, minus this mark's width and the right margin. * *
    This prioritization is then duplicated for the height, * bottom and top properties, respectively. * *

    While most properties are variable, some mark types, such as lines * and areas, generate a single visual element rather than a distinct visual * element per datum. With these marks, some properties may be fixed. * Fixed properties can vary per mark, but not per datum! These * properties are evaluated solely for the first (0-index) datum, and typically * are specified as a constant. However, it is valid to use a function if the * property varies between panels or is dynamically generated. * *

    See also the Protovis guide. */ pv.Mark = function() { /* * TYPE 0 constant defs * TYPE 1 function defs * TYPE 2 constant properties * TYPE 3 function properties * in order of evaluation! */ this.$properties = []; this.$handlers = {}; }; /** @private Records which properties are defined on this mark type. */ pv.Mark.prototype.properties = {}; /** @private Records the cast function for each property. */ pv.Mark.cast = {}; /** * @private Defines and registers a property method for the property with the * given name. This method should be called on a mark class prototype to define * each exposed property. (Note this refers to the JavaScript * prototype, not the Protovis mark prototype, which is the {@link * #proto} field.) * *

    The created property method supports several modes of invocation:

      * *
    1. If invoked with a Function argument, this function is evaluated * for each associated datum. The return value of the function is used as the * computed property value. The context of the function (this) is this * mark. The arguments to the function are the associated data of this mark and * any enclosing panels. For example, a linear encoding of numerical data to * height is specified as * *
      m.height(function(d) d * 100);
      * * The expression d * 100 will be evaluated for the height property of * each mark instance. The return value of the property method (e.g., * m.height) is this mark (m)).

      * *

    2. If invoked with a non-function argument, the property is treated as a * constant. The return value of the property method (e.g., m.height) * is this mark.

      * *

    3. If invoked with no arguments, the computed property value for the current * mark instance in the scene graph is returned. This facilitates property * chaining, where one mark's properties are defined in terms of another's. * For example, to offset a mark's location from its prototype, you might say * *
      m.top(function() this.proto.top() + 10);
      * * Note that the index of the mark being evaluated (in the above example, * this.proto) is inherited from the Mark class and set by * this mark. So, if the fifth element's top property is being evaluated, the * fifth instance of this.proto will similarly be queried for the value * of its top property. If the mark being evaluated has a different number of * instances, or its data is unrelated, the behavior of this method is * undefined. In these cases it may be better to index the scene * explicitly to specify the exact instance. * *

    Property names should follow standard JavaScript method naming * conventions, using lowerCamel-style capitalization. * *

    In addition to creating the property method, every property is registered * in the {@link #properties} map on the prototype. Although this is an * instance field, it is considered immutable and shared by all instances of a * given mark type. The properties map can be queried to see if a mark * type defines a particular property, such as width or height. * * @param {string} name the property name. * @param {function} [cast] the cast function for this property. */ pv.Mark.prototype.property = function(name, cast) { if (!this.hasOwnProperty("properties")) { this.properties = pv.extend(this.properties); } this.properties[name] = true; /* * Define the setter-getter globally, since the default behavior should be the * same for all properties, and since the Protovis inheritance chain is * independent of the JavaScript inheritance chain. For example, anchors * define a "name" property that is evaluated on derived marks, even though * those marks don't normally have a name. */ pv.Mark.prototype.propertyMethod(name, false, pv.Mark.cast[name] = cast); return this; }; /** * @private Defines a setter-getter for the specified property. * *

    If a cast function has been assigned to the specified property name, the * property function is wrapped by the cast function, or, if a constant is * specified, the constant is immediately cast. Note, however, that if the * property value is null, the cast function is not invoked. * * @param {string} name the property name. * @param {boolean} [def] whether is a property or a def. * @param {function} [cast] the cast function for this property. */ pv.Mark.prototype.propertyMethod = function(name, def, cast) { if (!cast) cast = pv.Mark.cast[name]; this[name] = function(v) { /* If this is a def, use it rather than property. */ if (def && this.scene) { var defs = this.scene.defs; if (arguments.length) { defs[name] = { id: (v == null) ? 0 : pv.id(), value: ((v != null) && cast) ? cast(v) : v }; return this; } return defs[name] ? defs[name].value : null; } /* If arguments are specified, set the property value. */ if (arguments.length) { var type = !def << 1 | (typeof v == "function"); this.propertyValue(name, (type & 1 && cast) ? function() { var x = v.apply(this, arguments); return (x != null) ? cast(x) : null; } : (((v != null) && cast) ? cast(v) : v)).type = type; return this; } return this.instance()[name]; }; }; /** @private Sets the value of the property name to v. */ pv.Mark.prototype.propertyValue = function(name, v) { var properties = this.$properties, p = {name: name, id: pv.id(), value: v}; for (var i = 0; i < properties.length; i++) { if (properties[i].name == name) { properties.splice(i, 1); break; } } properties.push(p); return p; }; /* Define all global properties. */ pv.Mark.prototype .property("data") .property("visible", Boolean) .property("left", Number) .property("right", Number) .property("top", Number) .property("bottom", Number) .property("cursor", String) .property("title", String) .property("reverse", Boolean) .property("antialias", Boolean) .property("events", String); /** * The mark type; a lower camelCase name. The type name controls rendering * behavior, and unless the rendering engine is extended, must be one of the * built-in concrete mark types: area, bar, dot, image, label, line, panel, * rule, or wedge. * * @type string * @name pv.Mark.prototype.type */ /** * The mark prototype, possibly undefined, from which to inherit property * functions. The mark prototype is not necessarily of the same type as this * mark. Any properties defined on this mark will override properties inherited * either from the prototype or from the type-specific defaults. * * @type pv.Mark * @name pv.Mark.prototype.proto */ /** * The enclosing parent panel. The parent panel is generally undefined only for * the root panel; however, it is possible to create "offscreen" marks that are * used only for inheritance purposes. * * @type pv.Panel * @name pv.Mark.prototype.parent */ /** * The child index. -1 if the enclosing parent panel is null; otherwise, the * zero-based index of this mark into the parent panel's children array. * * @type number */ pv.Mark.prototype.childIndex = -1; /** * The mark index. The value of this field depends on which instance (i.e., * which element of the data array) is currently being evaluated. During the * build phase, the index is incremented over each datum; when handling events, * the index is set to the instance that triggered the event. * * @type number */ pv.Mark.prototype.index = -1; /** * The current scale factor, based on any enclosing transforms. The current * scale can be used to create scale-independent graphics. For example, to * define a dot that has a radius of 10 irrespective of any zooming, say: * *

    dot.radius(function() 10 / this.scale)
    * * Note that the stroke width and font size are defined irrespective of scale * (i.e., in screen space) already. Also note that when a transform is applied * to a panel, the scale affects only the child marks, not the panel itself. * * @type number * @see pv.Panel#transform */ pv.Mark.prototype.scale = 1; /** * @private The scene graph. The scene graph is an array of objects; each object * (or "node") corresponds to an instance of this mark and an element in the * data array. The scene graph can be traversed to lookup previously-evaluated * properties. * * @name pv.Mark.prototype.scene */ /** * The root parent panel. This may be undefined for "offscreen" marks that are * created for inheritance purposes only. * * @type pv.Panel * @name pv.Mark.prototype.root */ /** * The data property; an array of objects. The size of the array determines the * number of marks that will be instantiated; each element in the array will be * passed to property functions to compute the property values. Typically, the * data property is specified as a constant array, such as * *
    m.data([1, 2, 3, 4, 5]);
    * * However, it is perfectly acceptable to define the data property as a * function. This function might compute the data dynamically, allowing * different data to be used per enclosing panel. For instance, in the stacked * area graph example (see {@link #scene}), the data function on the area mark * dereferences each series. * * @type array * @name pv.Mark.prototype.data */ /** * The visible property; a boolean determining whether or not the mark instance * is visible. If a mark instance is not visible, its other properties will not * be evaluated. Similarly, for panels no child marks will be rendered. * * @type boolean * @name pv.Mark.prototype.visible */ /** * The left margin; the distance, in pixels, between the left edge of the * enclosing panel and the left edge of this mark. Note that in some cases this * property may be redundant with the right property, or with the conjunction of * right and width. * * @type number * @name pv.Mark.prototype.left */ /** * The right margin; the distance, in pixels, between the right edge of the * enclosing panel and the right edge of this mark. Note that in some cases this * property may be redundant with the left property, or with the conjunction of * left and width. * * @type number * @name pv.Mark.prototype.right */ /** * The top margin; the distance, in pixels, between the top edge of the * enclosing panel and the top edge of this mark. Note that in some cases this * property may be redundant with the bottom property, or with the conjunction * of bottom and height. * * @type number * @name pv.Mark.prototype.top */ /** * The bottom margin; the distance, in pixels, between the bottom edge of the * enclosing panel and the bottom edge of this mark. Note that in some cases * this property may be redundant with the top property, or with the conjunction * of top and height. * * @type number * @name pv.Mark.prototype.bottom */ /** * The cursor property; corresponds to the CSS cursor property. This is * typically used in conjunction with event handlers to indicate interactivity. * * @type string * @name pv.Mark.prototype.cursor * @see CSS2 cursor */ /** * The title property; corresponds to the HTML/SVG title property, allowing the * general of simple plain text tooltips. * * @type string * @name pv.Mark.prototype.title */ /** * The events property; corresponds to the SVG pointer-events property, * specifying how the mark should participate in mouse events. The default value * is "painted". Supported values are: * *

    "painted": The given mark may receive events when the mouse is over a * "painted" area. The painted areas are the interior (i.e., fill) of the mark * if a 'fillStyle' is specified, and the perimeter (i.e., stroke) of the mark * if a 'strokeStyle' is specified. * *

    "all": The given mark may receive events when the mouse is over either the * interior (i.e., fill) or the perimeter (i.e., stroke) of the mark, regardless * of the specified fillStyle and strokeStyle. * *

    "none": The given mark may not receive events. * * @type string * @name pv.Mark.prototype.events */ /** * The reverse property; a boolean determining whether marks are ordered from * front-to-back or back-to-front. SVG does not support explicit z-ordering; * shapes are rendered in the order they appear. Thus, by default, marks are * rendered in data order. Setting the reverse property to false reverses the * order in which they are rendered; however, the properties are still evaluated * (i.e., built) in forward order. * * @type boolean * @name pv.Mark.prototype.reverse */ /** * Default properties for all mark types. By default, the data array is the * parent data as a single-element array; if the data property is not specified, * this causes each mark to be instantiated as a singleton with the parents * datum. The visible property is true by default, and the reverse property is * false. * * @type pv.Mark */ pv.Mark.prototype.defaults = new pv.Mark() .data(function(d) { return [d]; }) .visible(true) .antialias(true) .events("painted"); /** * Sets the prototype of this mark to the specified mark. Any properties not * defined on this mark may be inherited from the specified prototype mark, or * its prototype, and so on. The prototype mark need not be the same type of * mark as this mark. (Note that for inheritance to be useful, properties with * the same name on different mark types should have equivalent meaning.) * * @param {pv.Mark} proto the new prototype. * @returns {pv.Mark} this mark. * @see #add */ pv.Mark.prototype.extend = function(proto) { this.proto = proto; return this; }; /** * Adds a new mark of the specified type to the enclosing parent panel, whilst * simultaneously setting the prototype of the new mark to be this mark. * * @param {function} type the type of mark to add; a constructor, such as * pv.Bar. * @returns {pv.Mark} the new mark. * @see #extend */ pv.Mark.prototype.add = function(type) { return this.parent.add(type).extend(this); }; /** * Defines a custom property on this mark. Custom properties are currently * fixed, in that they are initialized once per mark set (i.e., per parent panel * instance). Custom properties can be used to store local state for the mark, * such as data needed by other properties (e.g., a custom scale) or interaction * state. * *

    WARNING We plan on changing this feature in a future release to define * standard properties, as opposed to fixed properties that behave * idiosyncratically within event handlers. Furthermore, we recommend storing * state in an external data structure, rather than tying it to the * visualization specification as with defs. * * @param {string} name the name of the local variable. * @param {function} [v] an optional initializer; may be a constant or a * function. */ pv.Mark.prototype.def = function(name, v) { this.propertyMethod(name, true); return this[name](arguments.length > 1 ? v : null); }; /** * Returns an anchor with the specified name. All marks support the five * standard anchor names:

      * *
    • top *
    • left *
    • center *
    • bottom *
    • right * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline). Text is * rendered to appear inside the mark by default. * *

    To facilitate stacking, anchors are defined in terms of their opposite * edge. For example, the top anchor defines the bottom property, such that the * mark extends upwards; the bottom anchor instead defines the top property, * such that the mark extends downwards. See also {@link pv.Layout.Stack}. * *

    While anchor names are typically constants, the anchor name is a true * property, which means you can specify a function to compute the anchor name * dynamically. See the {@link pv.Anchor#name} property for details. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} the new anchor. */ pv.Mark.prototype.anchor = function(name) { var target = this, scene; /* Default anchor name. */ if (!name) name = "center"; /** @private Find the instances of target that match source. */ function instances(source) { var mark = target, index = []; /* Mirrored descent. */ while (!(scene = mark.scene)) { source = source.parent; index.push({index: source.index, childIndex: mark.childIndex}); mark = mark.parent; } while (index.length) { var i = index.pop(); scene = scene[i.index].children[i.childIndex]; } /* * When the anchor target is also an ancestor, as in the case of adding * to a panel anchor, only generate one instance per panel. Also, set * the margins to zero, since they are offset by the enclosing panel. */ if (target.hasOwnProperty("index")) { var s = pv.extend(scene[target.index]); s.right = s.top = s.left = s.bottom = 0; return [s]; } return scene; } return new pv.Anchor(this) .name(name) .def("$mark.anchor", function() { scene = this.scene.target = instances(this); }) .data(function() { return scene.map(function(s) { return s.data; }); }) .visible(function() { return scene[this.index].visible; }) .left(function() { var s = scene[this.index], w = s.width || 0; switch (this.name()) { case "bottom": case "top": case "center": return s.left + w / 2; case "left": return null; } return s.left + w; }) .top(function() { var s = scene[this.index], h = s.height || 0; switch (this.name()) { case "left": case "right": case "center": return s.top + h / 2; case "top": return null; } return s.top + h; }) .right(function() { var s = scene[this.index]; return this.name() == "left" ? s.right + (s.width || 0) : null; }) .bottom(function() { var s = scene[this.index]; return this.name() == "top" ? s.bottom + (s.height || 0) : null; }) .textAlign(function() { switch (this.name()) { case "bottom": case "top": case "center": return "center"; case "right": return "right"; } return "left"; }) .textBaseline(function() { switch (this.name()) { case "right": case "left": case "center": return "middle"; case "top": return "top"; } return "bottom"; }); }; /** * Returns the anchor target of this mark, if it is derived from an anchor; * otherwise returns null. For example, if a label is derived from a bar anchor, * *

    bar.anchor("top").add(pv.Label);
    * * then property functions on the label can refer to the bar via the * anchorTarget method. This method is also useful for mark types * defining properties on custom anchors. * * @returns {pv.Mark} the anchor target of this mark; possibly null. */ pv.Mark.prototype.anchorTarget = function() { return this.proto.anchorTarget(); }; /** * Alias for setting the left, right, top and bottom properties simultaneously. * * @see #left * @see #right * @see #top * @see #bottom * @returns {pv.Mark} this. */ pv.Mark.prototype.margin = function(n) { return this.left(n).right(n).top(n).bottom(n); }; /** * @private Returns the current instance of this mark in the scene graph. This * is typically equivalent to this.scene[this.index], however if the * scene or index is unset, the default instance of the mark is returned. If no * default is set, the default is the last instance. Similarly, if the scene or * index of the parent panel is unset, the default instance of this mark in the * last instance of the enclosing panel is returned, and so on. * * @returns a node in the scene graph. */ pv.Mark.prototype.instance = function(defaultIndex) { var scene = this.scene || this.parent.instance(-1).children[this.childIndex], index = !arguments.length || this.hasOwnProperty("index") ? this.index : defaultIndex; return scene[index < 0 ? scene.length - 1 : index]; }; /** * @private Returns the first instance of this mark in the scene graph. This * method can only be called when the mark is bound to the scene graph (for * example, from an event handler, or within a property function). * * @returns a node in the scene graph. */ pv.Mark.prototype.first = function() { return this.scene[0]; }; /** * @private Returns the last instance of this mark in the scene graph. This * method can only be called when the mark is bound to the scene graph (for * example, from an event handler, or within a property function). In addition, * note that mark instances are built sequentially, so the last instance of this * mark may not yet be constructed. * * @returns a node in the scene graph. */ pv.Mark.prototype.last = function() { return this.scene[this.scene.length - 1]; }; /** * @private Returns the previous instance of this mark in the scene graph, or * null if this is the first instance. * * @returns a node in the scene graph, or null. */ pv.Mark.prototype.sibling = function() { return (this.index == 0) ? null : this.scene[this.index - 1]; }; /** * @private Returns the current instance in the scene graph of this mark, in the * previous instance of the enclosing parent panel. May return null if this * instance could not be found. * * @returns a node in the scene graph, or null. */ pv.Mark.prototype.cousin = function() { var p = this.parent, s = p && p.sibling(); return (s && s.children) ? s.children[this.childIndex][this.index] : null; }; /** * Renders this mark, including recursively rendering all child marks if this is * a panel. This method finds all instances of this mark and renders them. This * method descends recursively to the level of the mark to be rendered, finding * all visible instances of the mark. After the marks are rendered, the scene * and index attributes are removed from the mark to restore them to a clean * state. * *

    If an enclosing panel has an index property set (as is the case inside in * an event handler), then only instances of this mark inside the given instance * of the panel will be rendered; otherwise, all visible instances of the mark * will be rendered. */ pv.Mark.prototype.render = function() { var parent = this.parent, stack = pv.Mark.stack; /* For the first render, take it from the top. */ if (parent && !this.root.scene) { this.root.render(); return; } /* Record the path to this mark. */ var indexes = []; for (var mark = this; mark.parent; mark = mark.parent) { indexes.unshift(mark.childIndex); } /** @private */ function render(mark, depth, scale) { mark.scale = scale; if (depth < indexes.length) { stack.unshift(null); if (mark.hasOwnProperty("index")) { renderInstance(mark, depth, scale); } else { for (var i = 0, n = mark.scene.length; i < n; i++) { mark.index = i; renderInstance(mark, depth, scale); } delete mark.index; } stack.shift(); } else { mark.build(); /* * In the update phase, the scene is rendered by creating and updating * elements and attributes in the SVG image. No properties are evaluated * during the update phase; instead the values computed previously in the * build phase are simply translated into SVG. The update phase is * decoupled (see pv.Scene) to allow different rendering engines. */ pv.Scene.scale = scale; pv.Scene.updateAll(mark.scene); } delete mark.scale; } /** * @private Recursively renders the current instance of the specified mark. * This is slightly tricky because `index` and `scene` properties may or may * not already be set; if they are set, it means we are rendering only a * specific instance; if they are unset, we are rendering all instances. * Furthermore, we must preserve the original context of these properties when * rendering completes. * *

    Another tricky aspect is that the `scene` attribute should be set for * any preceding children, so as to allow property chaining. This is * consistent with first-pass rendering. */ function renderInstance(mark, depth, scale) { var s = mark.scene[mark.index], i; if (s.visible) { var childIndex = indexes[depth], child = mark.children[childIndex]; /* Set preceding child scenes. */ for (i = 0; i < childIndex; i++) { mark.children[i].scene = s.children[i]; } /* Set current child scene, if necessary. */ stack[0] = s.data; if (child.scene) { render(child, depth + 1, scale * s.transform.k); } else { child.scene = s.children[childIndex]; render(child, depth + 1, scale * s.transform.k); delete child.scene; } /* Clear preceding child scenes. */ for (i = 0; i < childIndex; i++) { delete mark.children[i].scene; } } } /* Bind this mark's property definitions. */ this.bind(); /* The render context is the first ancestor with an explicit index. */ while (parent && !parent.hasOwnProperty("index")) parent = parent.parent; /* Recursively render all instances of this mark. */ this.context( parent ? parent.scene : undefined, parent ? parent.index : -1, function() { render(this.root, 0, 1); }); }; /** @private Stores the current data stack. */ pv.Mark.stack = []; /** * @private In the bind phase, inherited property definitions are cached so they * do not need to be queried during build. */ pv.Mark.prototype.bind = function() { var seen = {}, types = [[], [], [], []], data, visible; /** Scans the proto chain for the specified mark. */ function bind(mark) { do { var properties = mark.$properties; for (var i = properties.length - 1; i >= 0 ; i--) { var p = properties[i]; if (!(p.name in seen)) { seen[p.name] = p; switch (p.name) { case "data": data = p; break; case "visible": visible = p; break; default: types[p.type].push(p); break; } } } } while (mark = mark.proto); } /* Scan the proto chain for all defined properties. */ bind(this); bind(this.defaults); types[1].reverse(); types[3].reverse(); /* Any undefined properties are null. */ var mark = this; do for (var name in mark.properties) { if (!(name in seen)) { types[2].push(seen[name] = {name: name, type: 2, value: null}); } } while (mark = mark.proto); /* Define setter-getter for inherited defs. */ var defs = types[0].concat(types[1]); for (var i = 0; i < defs.length; i++) { this.propertyMethod(defs[i].name, true); } /* Setup binds to evaluate constants before functions. */ this.binds = { properties: seen, data: data, defs: defs, required: [visible], optional: pv.blend(types) }; }; /** * @private Evaluates properties and computes implied properties. Properties are * stored in the {@link #scene} array for each instance of this mark. * *

    As marks are built recursively, the {@link #index} property is updated to * match the current index into the data array for each mark. Note that the * index property is only set for the mark currently being built and its * enclosing parent panels. The index property for other marks is unset, but is * inherited from the global Mark class prototype. This allows mark * properties to refer to properties on other marks in the same panel * conveniently; however, in general it is better to reference mark instances * specifically through the scene graph rather than depending on the magical * behavior of {@link #index}. * *

    The root scene array has a special property, data, which stores * the current data stack. The first element in this stack is the current datum, * followed by the datum of the enclosing parent panel, and so on. The data * stack should not be accessed directly; instead, property functions are passed * the current data stack as arguments. * *

    The evaluation of the data and visible properties is * special. The data property is evaluated first; unlike the other * properties, the data stack is from the parent panel, rather than the current * mark, since the data is not defined until the data property is evaluated. * The visisble property is subsequently evaluated for each instance; * only if true will the {@link #buildInstance} method be called, evaluating * other properties and recursively building the scene graph. * *

    If this mark is being re-built, any old instances of this mark that no * longer exist (because the new data array contains fewer elements) will be * cleared using {@link #clearInstance}. * * @param parent the instance of the parent panel from the scene graph. */ pv.Mark.prototype.build = function() { var scene = this.scene, stack = pv.Mark.stack; if (!scene) { scene = this.scene = []; scene.mark = this; scene.type = this.type; scene.childIndex = this.childIndex; if (this.parent) { scene.parent = this.parent.scene; scene.parentIndex = this.parent.index; } } /* Evaluate defs. */ if (this.binds.defs.length) { var defs = scene.defs; if (!defs) scene.defs = defs = {}; for (var i = 0; i < this.binds.defs.length; i++) { var p = this.binds.defs[i], d = defs[p.name]; if (!d || (p.id > d.id)) { defs[p.name] = { id: 0, // this def will be re-evaluated on next build value: (p.type & 1) ? p.value.apply(this, stack) : p.value }; } } } /* Evaluate special data property. */ var data = this.binds.data; data = data.type & 1 ? data.value.apply(this, stack) : data.value; /* Create, update and delete scene nodes. */ stack.unshift(null); scene.length = data.length; for (var i = 0; i < data.length; i++) { pv.Mark.prototype.index = this.index = i; var s = scene[i]; if (!s) scene[i] = s = {}; s.data = stack[0] = data[i]; this.buildInstance(s); } pv.Mark.prototype.index = -1; delete this.index; stack.shift(); return this; }; /** * @private Evaluates the specified array of properties for the specified * instance s in the scene graph. * * @param s a node in the scene graph; the instance of the mark to build. * @param properties an array of properties. */ pv.Mark.prototype.buildProperties = function(s, properties) { for (var i = 0, n = properties.length; i < n; i++) { var p = properties[i], v = p.value; // assume case 2 (constant) switch (p.type) { case 0: case 1: v = this.scene.defs[p.name].value; break; case 3: v = v.apply(this, pv.Mark.stack); break; } s[p.name] = v; } }; /** * @private Evaluates all of the properties for this mark for the specified * instance s in the scene graph. The set of properties to evaluate is * retrieved from the {@link #properties} array for this mark type (see {@link * #type}). After these properties are evaluated, any implied properties * may be computed by the mark and set on the scene graph; see * {@link #buildImplied}. * *

    For panels, this method recursively builds the scene graph for all child * marks as well. In general, this method should not need to be overridden by * concrete mark types. * * @param s a node in the scene graph; the instance of the mark to build. */ pv.Mark.prototype.buildInstance = function(s) { this.buildProperties(s, this.binds.required); if (s.visible) { this.buildProperties(s, this.binds.optional); this.buildImplied(s); } }; /** * @private Computes the implied properties for this mark for the specified * instance s in the scene graph. Implied properties are those with * dependencies on multiple other properties; for example, the width property * may be implied if the left and right properties are set. This method can be * overridden by concrete mark types to define new implied properties, if * necessary. * * @param s a node in the scene graph; the instance of the mark to build. */ pv.Mark.prototype.buildImplied = function(s) { var l = s.left; var r = s.right; var t = s.top; var b = s.bottom; /* Assume width and height are zero if not supported by this mark type. */ var p = this.properties; var w = p.width ? s.width : 0; var h = p.height ? s.height : 0; /* Compute implied width, right and left. */ var width = this.parent ? this.parent.width() : (w + l + r); if (w == null) { w = width - (r = r || 0) - (l = l || 0); } else if (r == null) { r = width - w - (l = l || 0); } else if (l == null) { l = width - w - (r = r || 0); } /* Compute implied height, bottom and top. */ var height = this.parent ? this.parent.height() : (h + t + b); if (h == null) { h = height - (t = t || 0) - (b = b || 0); } else if (b == null) { b = height - h - (t = t || 0); } else if (t == null) { t = height - h - (b = b || 0); } s.left = l; s.right = r; s.top = t; s.bottom = b; /* Only set width and height if they are supported by this mark type. */ if (p.width) s.width = w; if (p.height) s.height = h; /* Set any null colors to pv.Color.transparent. */ if (p.textStyle && !s.textStyle) s.textStyle = pv.Color.transparent; if (p.fillStyle && !s.fillStyle) s.fillStyle = pv.Color.transparent; if (p.strokeStyle && !s.strokeStyle) s.strokeStyle = pv.Color.transparent; }; /** * Returns the current location of the mouse (cursor) relative to this mark's * parent. The x coordinate corresponds to the left margin, while the * y coordinate corresponds to the top margin. * * @returns {pv.Vector} the mouse location. */ pv.Mark.prototype.mouse = function() { /* Compute xy-coordinates relative to the panel. */ var x = pv.event.pageX || 0, y = pv.event.pageY || 0, n = this.root.canvas(); do { x -= n.offsetLeft; y -= n.offsetTop; } while (n = n.offsetParent); /* Compute the inverse transform of all enclosing panels. */ var t = pv.Transform.identity, p = this.properties.transform ? this : this.parent, pz = []; do { pz.push(p); } while (p = p.parent); while (p = pz.pop()) t = t.translate(p.left(), p.top()).times(p.transform()); t = t.invert(); return pv.vector(x * t.k + t.x, y * t.k + t.y); }; /** * Registers an event handler for the specified event type with this mark. When * an event of the specified type is triggered, the specified handler will be * invoked. The handler is invoked in a similar method to property functions: * the context is this mark instance, and the arguments are the full * data stack. Event handlers can use property methods to manipulate the display * properties of the mark: * *

    m.event("click", function() this.fillStyle("red"));
    * * Alternatively, the external data can be manipulated and the visualization * redrawn: * *
    m.event("click", function(d) {
     *     data = all.filter(function(k) k.name == d);
     *     vis.render();
     *   });
    * * The return value of the event handler determines which mark gets re-rendered. * Use defs ({@link #def}) to set temporary state from event handlers. * *

    The complete set of event types is defined by SVG; see the reference * below. The set of supported event types is:

      * *
    • click *
    • mousedown *
    • mouseup *
    • mouseover *
    • mousemove *
    • mouseout * *
    Since Protovis does not specify any concept of focus, it does not * support key events; these should be handled outside the visualization using * standard JavaScript. In the future, support for interaction may be extended * to support additional event types, particularly those most relevant to * interactive visualization, such as selection. * *

    TODO In the current implementation, event handlers are not inherited from * prototype marks. They must be defined explicitly on each interactive mark. In * addition, only one event handler for a given event type can be defined; when * specifying multiple event handlers for the same type, only the last one will * be used. * * @see SVG events * @param {string} type the event type. * @param {function} handler the event handler. * @returns {pv.Mark} this. */ pv.Mark.prototype.event = function(type, handler) { this.$handlers[type] = pv.functor(handler); return this; }; /** @private Evaluates the function f with the specified context. */ pv.Mark.prototype.context = function(scene, index, f) { var proto = pv.Mark.prototype, stack = pv.Mark.stack, oscene = pv.Mark.scene, oindex = proto.index; /** @private Sets the context. */ function apply(scene, index) { pv.Mark.scene = scene; proto.index = index; if (!scene) return; var that = scene.mark, mark = that, ancestors = []; /* Set ancestors' scene and index; populate data stack. */ do { ancestors.push(mark); stack.push(scene[index].data); mark.index = index; mark.scene = scene; index = scene.parentIndex; scene = scene.parent; } while (mark = mark.parent); /* Set ancestors' scale; requires top-down. */ for (var i = ancestors.length - 1, k = 1; i > 0; i--) { mark = ancestors[i]; mark.scale = k; k *= mark.scene[mark.index].transform.k; } /* Set children's scene and scale. */ if (that.children) for (var i = 0, n = that.children.length; i < n; i++) { mark = that.children[i]; mark.scene = that.scene[that.index].children[i]; mark.scale = k; } } /** @private Clears the context. */ function clear(scene, index) { if (!scene) return; var that = scene.mark, mark; /* Reset children. */ if (that.children) for (var i = 0, n = that.children.length; i < n; i++) { mark = that.children[i]; delete mark.scene; delete mark.scale; } /* Reset ancestors. */ mark = that; do { stack.pop(); if (mark.parent) { delete mark.scene; delete mark.scale; } delete mark.index; } while (mark = mark.parent); } /* Context switch, invoke the function, then switch back. */ clear(oscene, oindex); apply(scene, index); try { f.apply(this, stack); } finally { clear(scene, index); apply(oscene, oindex); } }; /** @private Execute the event listener, then re-render. */ pv.Mark.dispatch = function(type, scene, index) { var m = scene.mark, p = scene.parent, l = m.$handlers[type]; if (!l) return p && pv.Mark.dispatch(type, p, scene.parentIndex); m.context(scene, index, function() { m = l.apply(m, pv.Mark.stack); if (m && m.render) m.render(); }); return true; }; /** * Constructs a new mark anchor with default properties. * * @class Represents an anchor on a given mark. An anchor is itself a mark, but * without a visual representation. It serves only to provide useful default * properties that can be inherited by other marks. Each type of mark can define * any number of named anchors for convenience. If the concrete mark type does * not define an anchor implementation specifically, one will be inherited from * the mark's parent class. * *

    For example, the bar mark provides anchors for its four sides: left, * right, top and bottom. Adding a label to the top anchor of a bar, * *

    bar.anchor("top").add(pv.Label);
    * * will render a text label on the top edge of the bar; the top anchor defines * the appropriate position properties (top and left), as well as text-rendering * properties for convenience (textAlign and textBaseline). * *

    Note that anchors do not inherit from their targets; the positional * properties are copied from the scene graph, which guarantees that the anchors * are positioned correctly, even if the positional properties are not defined * deterministically. (In addition, it also improves performance by avoiding * re-evaluating expensive properties.) If you want the anchor to inherit from * the target, use {@link pv.Mark#extend} before adding. For example: * *

    bar.anchor("top").extend(bar).add(pv.Label);
    * * The anchor defines it's own positional properties, but other properties (such * as the title property, say) can be inherited using the above idiom. Also note * that you can override positional properties in the anchor for custom * behavior. * * @extends pv.Mark * @param {pv.Mark} target the anchor target. */ pv.Anchor = function(target) { pv.Mark.call(this); this.target = target; this.parent = target.parent; }; pv.Anchor.prototype = pv.extend(pv.Mark) .property("name", String); /** * The anchor name. The set of supported anchor names is dependent on the * concrete mark type; see the mark type for details. For example, bars support * left, right, top and bottom anchors. * *

    While anchor names are typically constants, the anchor name is a true * property, which means you can specify a function to compute the anchor name * dynamically. For instance, if you wanted to alternate top and bottom anchors, * saying * *

    m.anchor(function() (this.index % 2) ? "top" : "bottom").add(pv.Dot);
    * * would have the desired effect. * * @type string * @name pv.Anchor.prototype.name */ /** * Returns the anchor target of this mark, if it is derived from an anchor; * otherwise returns null. For example, if a label is derived from a bar anchor, * *
    bar.anchor("top").add(pv.Label);
    * * then property functions on the label can refer to the bar via the * anchorTarget method. This method is also useful for mark types * defining properties on custom anchors. * * @returns {pv.Mark} the anchor target of this mark; possibly null. */ pv.Anchor.prototype.anchorTarget = function() { return this.target; }; /** * Constructs a new area mark with default properties. Areas are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents an area mark: the solid area between two series of * connected line segments. Unsurprisingly, areas are used most frequently for * area charts. * *

    Just as a line represents a polyline, the Area mark type * represents a polygon. However, an area is not an arbitrary polygon; * vertices are paired either horizontally or vertically into parallel * spans, and each span corresponds to an associated datum. Either the * width or the height must be specified, but not both; this determines whether * the area is horizontally-oriented or vertically-oriented. Like lines, areas * can be stroked and filled with arbitrary colors. * *

    See also the Area guide. * * @extends pv.Mark */ pv.Area = function() { pv.Mark.call(this); }; pv.Area.prototype = pv.extend(pv.Mark) .property("width", Number) .property("height", Number) .property("lineWidth", Number) .property("strokeStyle", pv.color) .property("fillStyle", pv.color) .property("segmented", Boolean) .property("interpolate", String) .property("tension", Number); pv.Area.prototype.type = "area"; /** * The width of a given span, in pixels; used for horizontal spans. If the width * is specified, the height property should be 0 (the default). Either the top * or bottom property should be used to space the spans vertically, typically as * a multiple of the index. * * @type number * @name pv.Area.prototype.width */ /** * The height of a given span, in pixels; used for vertical spans. If the height * is specified, the width property should be 0 (the default). Either the left * or right property should be used to space the spans horizontally, typically * as a multiple of the index. * * @type number * @name pv.Area.prototype.height */ /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the perimeter of the area. Unlike the * {@link Line} mark type, the entire perimeter is stroked, rather than just one * edge. The default value of this property is 1.5, but since the default stroke * style is null, area marks are not stroked by default. * *

    This property is fixed for non-segmented areas. See * {@link pv.Mark}. * * @type number * @name pv.Area.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the perimeter of the area. Unlike the {@link Line} mark type, the * entire perimeter is stroked, rather than just one edge. The default value of * this property is null, meaning areas are not stroked by default. * *

    This property is fixed for non-segmented areas. See * {@link pv.Mark}. * * @type string * @name pv.Area.prototype.strokeStyle * @see pv.color */ /** * The area fill style; if non-null, the interior of the polygon forming the * area is filled with the specified color. The default value of this property * is a categorical color. * *

    This property is fixed for non-segmented areas. See * {@link pv.Mark}. * * @type string * @name pv.Area.prototype.fillStyle * @see pv.color */ /** * Whether the area is segmented; whether variations in fill style, stroke * style, and the other properties are treated as fixed. Rendering segmented * areas is noticeably slower than non-segmented areas. * *

    This property is fixed. See {@link pv.Mark}. * * @type boolean * @name pv.Area.prototype.segmented */ /** * How to interpolate between values. Linear interpolation ("linear") is the * default, producing a straight line between points. For piecewise constant * functions (i.e., step functions), either "step-before" or "step-after" can be * specified. To draw open uniform b-splines, specify "basis". To draw cardinal * splines, specify "cardinal"; see also {@link #tension}. * *

    This property is fixed. See {@link pv.Mark}. * * @type string * @name pv.Area.prototype.interpolate */ /** * The tension of cardinal splines; used in conjunction with * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with * the given tension. In some sense, the tension can be interpreted as the * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e., * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The * default value is 0.7. * *

    This property is fixed. See {@link pv.Mark}. * * @type number * @name pv.Area.prototype.tension */ /** * Default properties for areas. By default, there is no stroke and the fill * style is a categorical color. * * @type pv.Area */ pv.Area.prototype.defaults = new pv.Area() .extend(pv.Mark.prototype.defaults) .lineWidth(1.5) .fillStyle(pv.Colors.category20().by(pv.parent)) .interpolate("linear") .tension(.7); /** @private Sets width and height to zero if null. */ pv.Area.prototype.buildImplied = function(s) { if (s.height == null) s.height = 0; if (s.width == null) s.width = 0; pv.Mark.prototype.buildImplied.call(this, s); }; /** @private Records which properties may be fixed. */ pv.Area.fixed = { lineWidth: 1, lineJoin: 1, strokeStyle: 1, fillStyle: 1, segmented: 1, interpolate: 1, tension: 1 }; /** * @private Make segmented required, such that this fixed property is always * evaluated, even if the first segment is not visible. Also cache which * properties are normally fixed. */ pv.Area.prototype.bind = function() { pv.Mark.prototype.bind.call(this); var binds = this.binds, required = binds.required, optional = binds.optional; for (var i = 0, n = optional.length; i < n; i++) { var p = optional[i]; p.fixed = p.name in pv.Area.fixed; if (p.name == "segmented") { required.push(p); optional.splice(i, 1); i--; n--; } } /* Cache the original arrays so they can be restored on build. */ this.binds.$required = required; this.binds.$optional = optional; }; /** * @private Override the default build behavior such that fixed properties are * determined dynamically, based on the value of the (always) fixed segmented * property. Any fixed properties are only evaluated on the first instance, * although their values are propagated to subsequent instances, so that they * are available for property chaining and the like. */ pv.Area.prototype.buildInstance = function(s) { var binds = this.binds; /* Handle fixed properties on secondary instances. */ if (this.index) { var fixed = binds.fixed; /* Determine which properties are fixed. */ if (!fixed) { fixed = binds.fixed = []; function f(p) { return !p.fixed || (fixed.push(p), false); } binds.required = binds.required.filter(f); if (!this.scene[0].segmented) binds.optional = binds.optional.filter(f); } /* Copy fixed property values from the first instance. */ for (var i = 0, n = fixed.length; i < n; i++) { var p = fixed[i].name; s[p] = this.scene[0][p]; } } /* Evaluate all properties on the first instance. */ else { binds.required = binds.$required; binds.optional = binds.$optional; binds.fixed = null; } pv.Mark.prototype.buildInstance.call(this, s); }; /** * Constructs a new area anchor with default properties. Areas support five * different anchors:

      * *
    • top *
    • left *
    • center *
    • bottom *
    • right * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline). Text * is rendered to appear inside the area. The area anchor also propagates the * interpolate, eccentricity, and tension properties such that an anchored area * or line will match positions between control points. * *

    For consistency with the other mark types, the anchor positions are * defined in terms of their opposite edge. For example, the top anchor defines * the bottom property, such that an area added to the top anchor grows upward. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} */ pv.Area.prototype.anchor = function(name) { var scene; return pv.Mark.prototype.anchor.call(this, name) .def("$area.anchor", function() { scene = this.scene.target; }) .interpolate(function() { return scene[this.index].interpolate; }) .eccentricity(function() { return scene[this.index].eccentricity; }) .tension(function() { return scene[this.index].tension; }); }; /** * Constructs a new bar mark with default properties. Bars are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a bar: an axis-aligned rectangle that can be stroked and * filled. Bars are used for many chart types, including bar charts, histograms * and Gantt charts. Bars can also be used as decorations, for example to draw a * frame border around a panel; in fact, a panel is a special type (a subclass) * of bar. * *

    Bars can be positioned in several ways. Most commonly, one of the four * corners is fixed using two margins, and then the width and height properties * determine the extent of the bar relative to this fixed location. For example, * using the bottom and left properties fixes the bottom-left corner; the width * then extends to the right, while the height extends to the top. As an * alternative to the four corners, a bar can be positioned exclusively using * margins; this is convenient as an inset from the containing panel, for * example. See {@link pv.Mark} for details on the prioritization of redundant * positioning properties. * *

    See also the Bar guide. * * @extends pv.Mark */ pv.Bar = function() { pv.Mark.call(this); }; pv.Bar.prototype = pv.extend(pv.Mark) .property("width", Number) .property("height", Number) .property("lineWidth", Number) .property("strokeStyle", pv.color) .property("fillStyle", pv.color); pv.Bar.prototype.type = "bar"; /** * The width of the bar, in pixels. If the left position is specified, the bar * extends rightward from the left edge; if the right position is specified, the * bar extends leftward from the right edge. * * @type number * @name pv.Bar.prototype.width */ /** * The height of the bar, in pixels. If the bottom position is specified, the * bar extends upward from the bottom edge; if the top position is specified, * the bar extends downward from the top edge. * * @type number * @name pv.Bar.prototype.height */ /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the bar's border. * * @type number * @name pv.Bar.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the bar's border. The default value of this property is null, meaning * bars are not stroked by default. * * @type string * @name pv.Bar.prototype.strokeStyle * @see pv.color */ /** * The bar fill style; if non-null, the interior of the bar is filled with the * specified color. The default value of this property is a categorical color. * * @type string * @name pv.Bar.prototype.fillStyle * @see pv.color */ /** * Default properties for bars. By default, there is no stroke and the fill * style is a categorical color. * * @type pv.Bar */ pv.Bar.prototype.defaults = new pv.Bar() .extend(pv.Mark.prototype.defaults) .lineWidth(1.5) .fillStyle(pv.Colors.category20().by(pv.parent)); /** * Constructs a new dot mark with default properties. Dots are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a dot; a dot is simply a sized glyph centered at a given * point that can also be stroked and filled. The size property is * proportional to the area of the rendered glyph to encourage meaningful visual * encodings. Dots can visually encode up to eight dimensions of data, though * this may be unwise due to integrality. See {@link pv.Mark} for details on the * prioritization of redundant positioning properties. * *

    See also the Dot guide. * * @extends pv.Mark */ pv.Dot = function() { pv.Mark.call(this); }; pv.Dot.prototype = pv.extend(pv.Mark) .property("size", Number) .property("radius", Number) .property("shape", String) .property("angle", Number) .property("lineWidth", Number) .property("strokeStyle", pv.color) .property("fillStyle", pv.color); pv.Dot.prototype.type = "dot"; /** * The size of the dot, in square pixels. Square pixels are used such that the * area of the dot is linearly proportional to the value of the size property, * facilitating representative encodings. * * @see #radius * @type number * @name pv.Dot.prototype.size */ /** * The radius of the dot, in pixels. This is an alternative to using * {@link #size}. * * @see #size * @type number * @name pv.Dot.prototype.radius */ /** * The shape name. Several shapes are supported:

      * *
    • cross *
    • triangle *
    • diamond *
    • square *
    • circle *
    • tick *
    • bar * *
    These shapes can be further changed using the {@link #angle} property; * for instance, a cross can be turned into a plus by rotating. Similarly, the * tick, which is vertical by default, can be rotated horizontally. Note that * some shapes (cross and tick) do not have interior areas, and thus do not * support fill style meaningfully. * *

    Note: it may be more natural to use the {@link pv.Rule} mark for * horizontal and vertical ticks. The tick shape is only necessary if angled * ticks are needed. * * @type string * @name pv.Dot.prototype.shape */ /** * The rotation angle, in radians. Used to rotate shapes, such as to turn a * cross into a plus. * * @type number * @name pv.Dot.prototype.angle */ /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the dot's shape. * * @type number * @name pv.Dot.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the dot's shape. The default value of this property is a categorical * color. * * @type string * @name pv.Dot.prototype.strokeStyle * @see pv.color */ /** * The fill style; if non-null, the interior of the dot is filled with the * specified color. The default value of this property is null, meaning dots are * not filled by default. * * @type string * @name pv.Dot.prototype.fillStyle * @see pv.color */ /** * Default properties for dots. By default, there is no fill and the stroke * style is a categorical color. The default shape is "circle" with size 20. * * @type pv.Dot */ pv.Dot.prototype.defaults = new pv.Dot() .extend(pv.Mark.prototype.defaults) .size(20) .shape("circle") .lineWidth(1.5) .strokeStyle(pv.Colors.category10().by(pv.parent)); /** * Constructs a new dot anchor with default properties. Dots support five * different anchors:

      * *
    • top *
    • left *
    • center *
    • bottom *
    • right * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline). Text is * rendered to appear outside the dot. Note that this behavior is different from * other mark anchors, which default to rendering text inside the mark. * *

    For consistency with the other mark types, the anchor positions are * defined in terms of their opposite edge. For example, the top anchor defines * the bottom property, such that a bar added to the top anchor grows upward. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} */ pv.Dot.prototype.anchor = function(name) { var scene; return pv.Mark.prototype.anchor.call(this, name) .def("$wedge.anchor", function() { scene = this.scene.target; }) .left(function() { var s = scene[this.index]; switch (this.name()) { case "bottom": case "top": case "center": return s.left; case "left": return null; } return s.left + s.radius; }) .right(function() { var s = scene[this.index]; return this.name() == "left" ? s.right + s.radius : null; }) .top(function() { var s = scene[this.index]; switch (this.name()) { case "left": case "right": case "center": return s.top; case "top": return null; } return s.top + s.radius; }) .bottom(function() { var s = scene[this.index]; return this.name() == "top" ? s.bottom + s.radius : null; }) .textAlign(function() { switch (this.name()) { case "left": return "right"; case "bottom": case "top": case "center": return "center"; } return "left"; }) .textBaseline(function() { switch (this.name()) { case "right": case "left": case "center": return "middle"; case "bottom": return "top"; } return "bottom"; }); }; /** @private Sets radius based on size or vice versa. */ pv.Dot.prototype.buildImplied = function(s) { if (s.radius == null) s.radius = Math.sqrt(s.size); else if (s.size == null) s.size = s.radius * s.radius; pv.Mark.prototype.buildImplied.call(this, s); }; /** * Constructs a new label mark with default properties. Labels are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a text label, allowing textual annotation of other marks or * arbitrary text within the visualization. The character data must be plain * text (unicode), though the text can be styled using the {@link #font} * property. If rich text is needed, external HTML elements can be overlaid on * the canvas by hand. * *

    Labels are positioned using the box model, similarly to {@link Dot}. Thus, * a label has no width or height, but merely a text anchor location. The text * is positioned relative to this anchor location based on the * {@link #textAlign}, {@link #textBaseline} and {@link #textMargin} properties. * Furthermore, the text may be rotated using {@link #textAngle}. * *

    Labels ignore events, so as to not interfere with event handlers on * underlying marks, such as bars. In the future, we may support event handlers * on labels. * *

    See also the Label guide. * * @extends pv.Mark */ pv.Label = function() { pv.Mark.call(this); }; pv.Label.prototype = pv.extend(pv.Mark) .property("text", String) .property("font", String) .property("textAngle", Number) .property("textStyle", pv.color) .property("textAlign", String) .property("textBaseline", String) .property("textMargin", Number) .property("textDecoration", String) .property("textShadow", String); pv.Label.prototype.type = "label"; /** * The character data to render; a string. The default value of the text * property is the identity function, meaning the label's associated datum will * be rendered using its toString. * * @type string * @name pv.Label.prototype.text */ /** * The font format, per the CSS Level 2 specification. The default font is "10px * sans-serif", for consistency with the HTML 5 canvas element specification. * Note that since text is not wrapped, any line-height property will be * ignored. The other font-style, font-variant, font-weight, font-size and * font-family properties are supported. * * @see CSS2 fonts * @type string * @name pv.Label.prototype.font */ /** * The rotation angle, in radians. Text is rotated clockwise relative to the * anchor location. For example, with the default left alignment, an angle of * Math.PI / 2 causes text to proceed downwards. The default angle is zero. * * @type number * @name pv.Label.prototype.textAngle */ /** * The text color. The name "textStyle" is used for consistency with "fillStyle" * and "strokeStyle", although it might be better to rename this property (and * perhaps use the same name as "strokeStyle"). The default color is black. * * @type string * @name pv.Label.prototype.textStyle * @see pv.color */ /** * The horizontal text alignment. One of:

      * *
    • left *
    • center *
    • right * *
    The default horizontal alignment is left. * * @type string * @name pv.Label.prototype.textAlign */ /** * The vertical text alignment. One of:
      * *
    • top *
    • middle *
    • bottom * *
    The default vertical alignment is bottom. * * @type string * @name pv.Label.prototype.textBaseline */ /** * The text margin; may be specified in pixels, or in font-dependent units (such * as ".1ex"). The margin can be used to pad text away from its anchor location, * in a direction dependent on the horizontal and vertical alignment * properties. For example, if the text is left- and middle-aligned, the margin * shifts the text to the right. The default margin is 3 pixels. * * @type number * @name pv.Label.prototype.textMargin */ /** * A list of shadow effects to be applied to text, per the CSS Text Level 3 * text-shadow property. An example specification is "0.1em 0.1em 0.1em * rgba(0,0,0,.5)"; the first length is the horizontal offset, the second the * vertical offset, and the third the blur radius. * * @see CSS3 text * @type string * @name pv.Label.prototype.textShadow */ /** * A list of decoration to be applied to text, per the CSS Text Level 3 * text-decoration property. An example specification is "underline". * * @see CSS3 text * @type string * @name pv.Label.prototype.textDecoration */ /** * Default properties for labels. See the individual properties for the default * values. * * @type pv.Label */ pv.Label.prototype.defaults = new pv.Label() .extend(pv.Mark.prototype.defaults) .events("none") .text(pv.identity) .font("10px sans-serif") .textAngle(0) .textStyle("black") .textAlign("left") .textBaseline("bottom") .textMargin(3); /** * Constructs a new line mark with default properties. Lines are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a series of connected line segments, or polyline, * that can be stroked with a configurable color and thickness. Each * articulation point in the line corresponds to a datum; for n points, * n-1 connected line segments are drawn. The point is positioned using * the box model. Arbitrary paths are also possible, allowing radar plots and * other custom visualizations. * *

    Like areas, lines can be stroked and filled with arbitrary colors. In most * cases, lines are only stroked, but the fill style can be used to construct * arbitrary polygons. * *

    See also the Line guide. * * @extends pv.Mark */ pv.Line = function() { pv.Mark.call(this); }; pv.Line.prototype = pv.extend(pv.Mark) .property("lineWidth", Number) .property("lineJoin", String) .property("strokeStyle", pv.color) .property("fillStyle", pv.color) .property("segmented", Boolean) .property("interpolate", String) .property("eccentricity", Number) .property("tension", Number); pv.Line.prototype.type = "line"; /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the line. * * @type number * @name pv.Line.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the line. The default value of this property is a categorical color. * * @type string * @name pv.Line.prototype.strokeStyle * @see pv.color */ /** * The type of corners where two lines meet. Accepted values are "bevel", * "round" and "miter". The default value is "miter". * *

    For segmented lines, only "miter" joins and "linear" interpolation are * currently supported. Any other value, including null, will disable joins, * producing disjoint line segments. Note that the miter joins must be computed * manually (at least in the current SVG renderer); since this calculation may * be expensive and unnecessary for small lines, specifying null can improve * performance significantly. * *

    This property is fixed. See {@link pv.Mark}. * * @type string * @name pv.Line.prototype.lineJoin */ /** * The line fill style; if non-null, the interior of the line is closed and * filled with the specified color. The default value of this property is a * null, meaning that lines are not filled by default. * *

    This property is fixed. See {@link pv.Mark}. * * @type string * @name pv.Line.prototype.fillStyle * @see pv.color */ /** * Whether the line is segmented; whether variations in stroke style, line width * and the other properties are treated as fixed. Rendering segmented lines is * noticeably slower than non-segmented lines. * *

    This property is fixed. See {@link pv.Mark}. * * @type boolean * @name pv.Line.prototype.segmented */ /** * How to interpolate between values. Linear interpolation ("linear") is the * default, producing a straight line between points. For piecewise constant * functions (i.e., step functions), either "step-before" or "step-after" can be * specified. To draw a clockwise circular arc between points, specify "polar"; * to draw a counterclockwise circular arc between points, specify * "polar-reverse". To draw open uniform b-splines, specify "basis". To draw * cardinal splines, specify "cardinal"; see also {@link #tension}. * *

    This property is fixed. See {@link pv.Mark}. * * @type string * @name pv.Line.prototype.interpolate */ /** * The eccentricity of polar line segments; used in conjunction with * interpolate("polar"). The default value of 0 means that line segments are * drawn as circular arcs. A value of 1 draws a straight line. A value between 0 * and 1 draws an elliptical arc with the given eccentricity. * * @type number * @name pv.Line.prototype.eccentricity */ /** * The tension of cardinal splines; used in conjunction with * interpolate("cardinal"). A value between 0 and 1 draws cardinal splines with * the given tension. In some sense, the tension can be interpreted as the * "length" of the tangent; a tension of 1 will yield all zero tangents (i.e., * linear interpolation), and a tension of 0 yields a Catmull-Rom spline. The * default value is 0.7. * *

    This property is fixed. See {@link pv.Mark}. * * @type number * @name pv.Line.prototype.tension */ /** * Default properties for lines. By default, there is no fill and the stroke * style is a categorical color. The default interpolation is linear. * * @type pv.Line */ pv.Line.prototype.defaults = new pv.Line() .extend(pv.Mark.prototype.defaults) .lineJoin("miter") .lineWidth(1.5) .strokeStyle(pv.Colors.category10().by(pv.parent)) .interpolate("linear") .eccentricity(0) .tension(.7); /** @private Reuse Area's implementation for segmented bind & build. */ pv.Line.prototype.bind = pv.Area.prototype.bind; pv.Line.prototype.buildInstance = pv.Area.prototype.buildInstance; /** * Constructs a new line anchor with default properties. Lines support five * different anchors:

      * *
    • top *
    • left *
    • center *
    • bottom *
    • right * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline). Text is * rendered to appear outside the line. Note that this behavior is different * from other mark anchors, which default to rendering text inside the * mark. * *

    For consistency with the other mark types, the anchor positions are * defined in terms of their opposite edge. For example, the top anchor defines * the bottom property, such that a bar added to the top anchor grows upward. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} */ pv.Line.prototype.anchor = function(name) { return pv.Area.prototype.anchor.call(this, name) .textAlign(function(d) { switch (this.name()) { case "left": return "right"; case "bottom": case "top": case "center": return "center"; case "right": return "left"; } }) .textBaseline(function(d) { switch (this.name()) { case "right": case "left": case "center": return "middle"; case "top": return "bottom"; case "bottom": return "top"; } }); }; /** * Constructs a new rule with default properties. Rules are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a horizontal or vertical rule. Rules are frequently used * for axes and grid lines. For example, specifying only the bottom property * draws horizontal rules, while specifying only the left draws vertical * rules. Rules can also be used as thin bars. The visual style is controlled in * the same manner as lines. * *

    Rules are positioned exclusively the standard box model properties. The * following combinations of properties are supported: * *

    * * * * * * * * * * * * * * * * * * * *
    PropertiesOrientation
    leftvertical
    rightvertical
    left, bottom, topvertical
    right, bottom, topvertical
    tophorizontal
    bottomhorizontal
    top, left, righthorizontal
    bottom, left, righthorizontal
    left, top, heightvertical
    left, bottom, heightvertical
    right, top, heightvertical
    right, bottom, heightvertical
    left, top, widthhorizontal
    left, bottom, widthhorizontal
    right, top, widthhorizontal
    right, bottom, widthhorizontal
    * *

    Small rules can be used as tick marks; alternatively, a {@link Dot} with * the "tick" shape can be used. * *

    See also the Rule guide. * * @see pv.Line * @extends pv.Mark */ pv.Rule = function() { pv.Mark.call(this); }; pv.Rule.prototype = pv.extend(pv.Mark) .property("width", Number) .property("height", Number) .property("lineWidth", Number) .property("strokeStyle", pv.color); pv.Rule.prototype.type = "rule"; /** * The width of the rule, in pixels. If the left position is specified, the rule * extends rightward from the left edge; if the right position is specified, the * rule extends leftward from the right edge. * * @type number * @name pv.Rule.prototype.width */ /** * The height of the rule, in pixels. If the bottom position is specified, the * rule extends upward from the bottom edge; if the top position is specified, * the rule extends downward from the top edge. * * @type number * @name pv.Rule.prototype.height */ /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the rule. The default value is 1 pixel. * * @type number * @name pv.Rule.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the rule. The default value of this property is black. * * @type string * @name pv.Rule.prototype.strokeStyle * @see pv.color */ /** * Default properties for rules. By default, a single-pixel black line is * stroked. * * @type pv.Rule */ pv.Rule.prototype.defaults = new pv.Rule() .extend(pv.Mark.prototype.defaults) .lineWidth(1) .strokeStyle("black") .antialias(false); /** * Constructs a new rule anchor with default properties. Rules support five * different anchors:

      * *
    • top *
    • left *
    • center *
    • bottom *
    • right * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline). Text is * rendered to appear outside the rule. Note that this behavior is different * from other mark anchors, which default to rendering text inside the * mark. * *

    For consistency with the other mark types, the anchor positions are * defined in terms of their opposite edge. For example, the top anchor defines * the bottom property, such that a bar added to the top anchor grows upward. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} */ pv.Rule.prototype.anchor = pv.Line.prototype.anchor; /** @private Sets width or height based on orientation. */ pv.Rule.prototype.buildImplied = function(s) { var l = s.left, r = s.right, t = s.top, b = s.bottom; /* Determine horizontal or vertical orientation. */ if ((s.width != null) || ((l == null) && (r == null)) || ((r != null) && (l != null))) { s.height = 0; } else { s.width = 0; } pv.Mark.prototype.buildImplied.call(this, s); }; /** * Constructs a new, empty panel with default properties. Panels, with the * exception of the root panel, are not typically constructed directly; instead, * they are added to an existing panel or mark via {@link pv.Mark#add}. * * @class Represents a container mark. Panels allow repeated or nested * structures, commonly used in small multiple displays where a small * visualization is tiled to facilitate comparison across one or more * dimensions. Other types of visualizations may benefit from repeated and * possibly overlapping structure as well, such as stacked area charts. Panels * can also offset the position of marks to provide padding from surrounding * content. * *

    All Protovis displays have at least one panel; this is the root panel to * which marks are rendered. The box model properties (four margins, width and * height) are used to offset the positions of contained marks. The data * property determines the panel count: a panel is generated once per associated * datum. When nested panels are used, property functions can declare additional * arguments to access the data associated with enclosing panels. * *

    Panels can be rendered inline, facilitating the creation of sparklines. * This allows designers to reuse browser layout features, such as text flow and * tables; designers can also overlay HTML elements such as rich text and * images. * *

    All panels have a children array (possibly empty) containing the * child marks in the order they were added. Panels also have a root * field which points to the root (outermost) panel; the root panel's root field * points to itself. * *

    See also the Protovis guide. * * @extends pv.Bar */ pv.Panel = function() { pv.Bar.call(this); /** * The child marks; zero or more {@link pv.Mark}s in the order they were * added. * * @see #add * @type pv.Mark[] */ this.children = []; this.root = this; /** * The internal $dom field is set by the Protovis loader; see lang/init.js. It * refers to the script element that contains the Protovis specification, so * that the panel knows where in the DOM to insert the generated SVG element. * * @private */ this.$dom = pv.$ && pv.$.s; }; pv.Panel.prototype = pv.extend(pv.Bar) .property("transform") .property("overflow", String) .property("canvas", function(c) { return (typeof c == "string") ? document.getElementById(c) : c; // assume that c is the passed-in element }); pv.Panel.prototype.type = "panel"; /** * The canvas element; either the string ID of the canvas element in the current * document, or a reference to the canvas element itself. If null, a canvas * element will be created and inserted into the document at the location of the * script element containing the current Protovis specification. This property * only applies to root panels and is ignored on nested panels. * *

    Note: the "canvas" element here refers to a div (or other suitable * HTML container element), not a canvas element. The name of * this property is a historical anachronism from the first implementation that * used HTML 5 canvas, rather than SVG. * * @type string * @name pv.Panel.prototype.canvas */ /** * Specifies whether child marks are clipped when they overflow this panel. * This affects the clipping of all this panel's descendant marks. * * @type string * @name pv.Panel.prototype.overflow * @see CSS2 */ /** * The transform to be applied to child marks. The default transform is * identity, which has no effect. Note that the panel's own fill and stroke are * not affected by the transform, and panel's transform only affects the * scale of child marks, not the panel itself. * * @type pv.Transform * @name pv.Panel.prototype.transform * @see pv.Mark#scale */ /** * Default properties for panels. By default, the margins are zero, the fill * style is transparent. * * @type pv.Panel */ pv.Panel.prototype.defaults = new pv.Panel() .extend(pv.Bar.prototype.defaults) .fillStyle(null) // override Bar default .overflow("visible"); /** * Returns an anchor with the specified name. This method is overridden such * that adding to a panel's anchor adds to the panel, rather than to the panel's * parent. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} the new anchor. */ pv.Panel.prototype.anchor = function(name) { var anchor = pv.Bar.prototype.anchor.call(this, name); anchor.parent = this; return anchor; }; /** * Adds a new mark of the specified type to this panel. Unlike the normal * {@link Mark#add} behavior, adding a mark to a panel does not cause the mark * to inherit from the panel. Since the contained marks are offset by the panel * margins already, inheriting properties is generally undesirable; of course, * it is always possible to change this behavior by calling {@link Mark#extend} * explicitly. * * @param {function} type the type of the new mark to add. * @returns {pv.Mark} the new mark. */ pv.Panel.prototype.add = function(type) { var child = new type(); child.parent = this; child.root = this.root; child.childIndex = this.children.length; this.children.push(child); return child; }; /** @private Bind this panel, then any child marks recursively. */ pv.Panel.prototype.bind = function() { pv.Mark.prototype.bind.call(this); for (var i = 0; i < this.children.length; i++) { this.children[i].bind(); } }; /** * @private Evaluates all of the properties for this panel for the specified * instance s in the scene graph, including recursively building the * scene graph for child marks. * * @param s a node in the scene graph; the instance of the panel to build. * @see Mark#scene */ pv.Panel.prototype.buildInstance = function(s) { pv.Bar.prototype.buildInstance.call(this, s); if (!s.visible) return; if (!s.children) s.children = []; /* * Multiply the current scale factor by this panel's transform. Also clear the * default index as we recurse into child marks; it will be reset to the * current index when the next panel instance is built. */ var scale = this.scale * s.transform.k, child, n = this.children.length; pv.Mark.prototype.index = -1; /* * Build each child, passing in the parent (this panel) scene graph node. The * child mark's scene is initialized from the corresponding entry in the * existing scene graph, such that properties from the previous build can be * reused; this is largely to facilitate the recycling of SVG elements. */ for (var i = 0; i < n; i++) { child = this.children[i]; child.scene = s.children[i]; // possibly undefined child.scale = scale; child.build(); } /* * Once the child marks have been built, the new scene graph nodes are removed * from the child marks and placed into the scene graph. The nodes cannot * remain on the child nodes because this panel (or a parent panel) may be * instantiated multiple times! */ for (var i = 0; i < n; i++) { child = this.children[i]; s.children[i] = child.scene; delete child.scene; delete child.scale; } /* Delete any expired child scenes. */ s.children.length = n; }; /** * @private Computes the implied properties for this panel for the specified * instance s in the scene graph. Panels have two implied * properties:

      * *
    • The canvas property references the DOM element, typically a DIV, * that contains the SVG element that is used to display the visualization. This * property may be specified as a string, referring to the unique ID of the * element in the DOM. The string is converted to a reference to the DOM * element. The width and height of the SVG element is inferred from this DOM * element. If no canvas property is specified, a new SVG element is created and * inserted into the document, using the panel dimensions; see * {@link #createCanvas}. * *
    • The children array, while not a property per se, contains the * scene graph for each child mark. This array is initialized to be empty, and * is populated above in {@link #buildInstance}. * *
    The current implementation creates the SVG element, if necessary, during * the build phase; in the future, it may be preferrable to move this to the * update phase, although then the canvas property would be undefined. In * addition, DOM inspection is necessary to define the implied width and height * properties that may be inferred from the DOM. * * @param s a node in the scene graph; the instance of the panel to build. */ pv.Panel.prototype.buildImplied = function(s) { if (!this.parent) { var c = s.canvas; if (c) { /* Clear the container if it's not associated with this panel. */ if (c.$panel != this) { c.$panel = this; while (c.lastChild) c.removeChild(c.lastChild); } /* If width and height weren't specified, inspect the container. */ var w, h; if (s.width == null) { w = parseFloat(pv.css(c, "width")); s.width = w - s.left - s.right; } if (s.height == null) { h = parseFloat(pv.css(c, "height")); s.height = h - s.top - s.bottom; } } else { var cache = this.$canvas || (this.$canvas = []); if (!(c = cache[this.index])) { c = cache[this.index] = document.createElement("span"); if (this.$dom) { // script element for text/javascript+protovis this.$dom.parentNode.insertBefore(c, this.$dom); } else { // find the last element in the body var n = document.body; while (n.lastChild && n.lastChild.tagName) n = n.lastChild; if (n != document.body) n = n.parentNode; n.appendChild(c); } } } s.canvas = c; } if (!s.transform) s.transform = pv.Transform.identity; pv.Mark.prototype.buildImplied.call(this, s); }; /** * Constructs a new image with default properties. Images are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents an image, either a static resource or a dynamically- * generated pixel buffer. Images share the same layout and style properties as * bars. The external image resource is specified via the {@link #url} * property. The optional fill, if specified, appears beneath the image, while * the optional stroke appears above the image. * *

    Dynamic images such as heatmaps are supported using the {@link #image} * psuedo-property. This function is passed the x and y index, in * addition to the current data stack. The return value is a {@link pv.Color}, * or null for transparent. A string can also be returned, which will be parsed * into a color; however, it is typically much faster to return an object with * r, g, b and a attributes, to avoid the * cost of parsing and object instantiation. * *

    See {@link pv.Bar} for details on positioning properties. * * @extends pv.Bar */ pv.Image = function() { pv.Bar.call(this); }; pv.Image.prototype = pv.extend(pv.Bar) .property("url", String) .property("imageWidth", Number) .property("imageHeight", Number); pv.Image.prototype.type = "image"; /** * The URL of the image to display. The set of supported image types is * browser-dependent; PNG and JPEG are recommended. * * @type string * @name pv.Image.prototype.url */ /** * The width of the image in pixels. For static images, this property is * computed implicitly from the loaded image resources. For dynamic images, this * property can be used to specify the width of the pixel buffer; otherwise, the * value is derived from the width property. * * @type number * @name pv.Image.prototype.imageWidth */ /** * The height of the image in pixels. For static images, this property is * computed implicitly from the loaded image resources. For dynamic images, this * property can be used to specify the height of the pixel buffer; otherwise, the * value is derived from the height property. * * @type number * @name pv.Image.prototype.imageHeight */ /** * Default properties for images. By default, there is no stroke or fill style. * * @type pv.Image */ pv.Image.prototype.defaults = new pv.Image() .extend(pv.Bar.prototype.defaults) .fillStyle(null); /** * Specifies the dynamic image function. By default, no image function is * specified and the url property is used to load a static image * resource. If an image function is specified, it will be invoked for each * pixel in the image, based on the related imageWidth and * imageHeight properties. * *

    For example, given a two-dimensional array heatmap, containing * numbers in the range [0, 1] in row-major order, a simple monochrome heatmap * image can be specified as: * *

    vis.add(pv.Image)
     *     .imageWidth(heatmap[0].length)
     *     .imageHeight(heatmap.length)
     *     .image(pv.ramp("white", "black").by(function(x, y) heatmap[y][x]));
    * * For fastest performance, use an ordinal scale which caches the fixed color * palette, or return an object literal with r, g, b * and a attributes. A {@link pv.Color} or string can also be returned, * though this typically results in slower performance. * * @param {function} f the new sizing function. * @returns {pv.Layout.Pack} this. */ pv.Image.prototype.image = function(f) { /** @private */ this.$image = function() { var c = f.apply(this, arguments); return c == null ? pv.Color.transparent : typeof c == "string" ? pv.color(c) : c; }; return this; }; /** @private Scan the proto chain for an image function. */ pv.Image.prototype.bind = function() { pv.Bar.prototype.bind.call(this); var binds = this.binds, mark = this; do { binds.image = mark.$image; } while (!binds.image && (mark = mark.proto)); }; /** @private */ pv.Image.prototype.buildImplied = function(s) { pv.Bar.prototype.buildImplied.call(this, s); if (!s.visible) return; /* Compute the implied image dimensions. */ if (s.imageWidth == null) s.imageWidth = s.width; if (s.imageHeight == null) s.imageHeight = s.height; /* Compute the pixel values. */ if ((s.url == null) && this.binds.image) { /* Cache the canvas element to reuse across renders. */ var canvas = this.$canvas || (this.$canvas = document.createElement("canvas")), context = canvas.getContext("2d"), w = s.imageWidth, h = s.imageHeight, stack = pv.Mark.stack, data; /* Evaluate the image function, storing into a CanvasPixelArray. */ canvas.width = w; canvas.height = h; data = (s.image = context.createImageData(w, h)).data; stack.unshift(null, null); for (var y = 0, p = 0; y < h; y++) { stack[1] = y; for (var x = 0; x < w; x++) { stack[0] = x; var color = this.binds.image.apply(this, stack); data[p++] = color.r; data[p++] = color.g; data[p++] = color.b; data[p++] = 255 * color.a; } } stack.splice(0, 2); } }; /** * Constructs a new wedge with default properties. Wedges are not typically * constructed directly, but by adding to a panel or an existing mark via * {@link pv.Mark#add}. * * @class Represents a wedge, or pie slice. Specified in terms of start and end * angle, inner and outer radius, wedges can be used to construct donut charts * and polar bar charts as well. If the {@link #angle} property is used, the end * angle is implied by adding this value to start angle. By default, the start * angle is the previously-generated wedge's end angle. This design allows * explicit control over the wedge placement if desired, while offering * convenient defaults for the construction of radial graphs. * *

    The center point of the circle is positioned using the standard box model. * The wedge can be stroked and filled, similar to {@link pv.Bar}. * *

    See also the Wedge guide. * * @extends pv.Mark */ pv.Wedge = function() { pv.Mark.call(this); }; pv.Wedge.prototype = pv.extend(pv.Mark) .property("startAngle", Number) .property("endAngle", Number) .property("angle", Number) .property("innerRadius", Number) .property("outerRadius", Number) .property("lineWidth", Number) .property("strokeStyle", pv.color) .property("fillStyle", pv.color); pv.Wedge.prototype.type = "wedge"; /** * The start angle of the wedge, in radians. The start angle is measured * clockwise from the 3 o'clock position. The default value of this property is * the end angle of the previous instance (the {@link Mark#sibling}), or -PI / 2 * for the first wedge; for pie and donut charts, typically only the * {@link #angle} property needs to be specified. * * @type number * @name pv.Wedge.prototype.startAngle */ /** * The end angle of the wedge, in radians. If not specified, the end angle is * implied as the start angle plus the {@link #angle}. * * @type number * @name pv.Wedge.prototype.endAngle */ /** * The angular span of the wedge, in radians. This property is used if end angle * is not specified. * * @type number * @name pv.Wedge.prototype.angle */ /** * The inner radius of the wedge, in pixels. The default value of this property * is zero; a positive value will produce a donut slice rather than a pie slice. * The inner radius can vary per-wedge. * * @type number * @name pv.Wedge.prototype.innerRadius */ /** * The outer radius of the wedge, in pixels. This property is required. For * pies, only this radius is required; for donuts, the inner radius must be * specified as well. The outer radius can vary per-wedge. * * @type number * @name pv.Wedge.prototype.outerRadius */ /** * The width of stroked lines, in pixels; used in conjunction with * strokeStyle to stroke the wedge's border. * * @type number * @name pv.Wedge.prototype.lineWidth */ /** * The style of stroked lines; used in conjunction with lineWidth to * stroke the wedge's border. The default value of this property is null, * meaning wedges are not stroked by default. * * @type string * @name pv.Wedge.prototype.strokeStyle * @see pv.color */ /** * The wedge fill style; if non-null, the interior of the wedge is filled with * the specified color. The default value of this property is a categorical * color. * * @type string * @name pv.Wedge.prototype.fillStyle * @see pv.color */ /** * Default properties for wedges. By default, there is no stroke and the fill * style is a categorical color. * * @type pv.Wedge */ pv.Wedge.prototype.defaults = new pv.Wedge() .extend(pv.Mark.prototype.defaults) .startAngle(function() { var s = this.sibling(); return s ? s.endAngle : -Math.PI / 2; }) .innerRadius(0) .lineWidth(1.5) .strokeStyle(null) .fillStyle(pv.Colors.category20().by(pv.index)); /** * Returns the mid-radius of the wedge, which is defined as half-way between the * inner and outer radii. * * @see #innerRadius * @see #outerRadius * @returns {number} the mid-radius, in pixels. */ pv.Wedge.prototype.midRadius = function() { return (this.innerRadius() + this.outerRadius()) / 2; }; /** * Returns the mid-angle of the wedge, which is defined as half-way between the * start and end angles. * * @see #startAngle * @see #endAngle * @returns {number} the mid-angle, in radians. */ pv.Wedge.prototype.midAngle = function() { return (this.startAngle() + this.endAngle()) / 2; }; /** * Constructs a new wedge anchor with default properties. Wedges support five * different anchors:

      * *
    • outer *
    • inner *
    • center *
    • start *
    • end * *
    In addition to positioning properties (left, right, top bottom), the * anchors support text rendering properties (text-align, text-baseline, * textAngle). Text is rendered to appear inside the wedge. * * @param {string} name the anchor name; either a string or a property function. * @returns {pv.Anchor} */ pv.Wedge.prototype.anchor = function(name) { function partial(s) { return s.innerRadius || s.angle < 2 * Math.PI; } function midRadius(s) { return (s.innerRadius + s.outerRadius) / 2; } function midAngle(s) { return (s.startAngle + s.endAngle) / 2; } var scene; return pv.Mark.prototype.anchor.call(this, name) .def("$wedge.anchor", function() { scene = this.scene.target; }) .left(function() { var s = scene[this.index]; if (partial(s)) switch (this.name()) { case "outer": return s.left + s.outerRadius * Math.cos(midAngle(s)); case "inner": return s.left + s.innerRadius * Math.cos(midAngle(s)); case "start": return s.left + midRadius(s) * Math.cos(s.startAngle); case "center": return s.left + midRadius(s) * Math.cos(midAngle(s)); case "end": return s.left + midRadius(s) * Math.cos(s.endAngle); } return s.left; }) .top(function() { var s = scene[this.index]; if (partial(s)) switch (this.name()) { case "outer": return s.top + s.outerRadius * Math.sin(midAngle(s)); case "inner": return s.top + s.innerRadius * Math.sin(midAngle(s)); case "start": return s.top + midRadius(s) * Math.sin(s.startAngle); case "center": return s.top + midRadius(s) * Math.sin(midAngle(s)); case "end": return s.top + midRadius(s) * Math.sin(s.endAngle); } return s.top; }) .textAlign(function() { var s = scene[this.index]; if (partial(s)) switch (this.name()) { case "outer": return pv.Wedge.upright(midAngle(s)) ? "right" : "left"; case "inner": return pv.Wedge.upright(midAngle(s)) ? "left" : "right"; } return "center"; }) .textBaseline(function() { var s = scene[this.index]; if (partial(s)) switch (this.name()) { case "start": return pv.Wedge.upright(s.startAngle) ? "top" : "bottom"; case "end": return pv.Wedge.upright(s.endAngle) ? "bottom" : "top"; } return "middle"; }) .textAngle(function() { var s = scene[this.index], a = 0; if (partial(s)) switch (this.name()) { case "center": case "inner": case "outer": a = midAngle(s); break; case "start": a = s.startAngle; break; case "end": a = s.endAngle; break; } return pv.Wedge.upright(a) ? a : (a + Math.PI); }); }; /** * Returns true if the specified angle is considered "upright", as in, text * rendered at that angle would appear upright. If the angle is not upright, * text is rotated 180 degrees to be upright, and the text alignment properties * are correspondingly changed. * * @param {number} angle an angle, in radius. * @returns {boolean} true if the specified angle is upright. */ pv.Wedge.upright = function(angle) { angle = angle % (2 * Math.PI); angle = (angle < 0) ? (2 * Math.PI + angle) : angle; return (angle < Math.PI / 2) || (angle >= 3 * Math.PI / 2); }; /** @private Sets angle based on endAngle or vice versa. */ pv.Wedge.prototype.buildImplied = function(s) { if (s.angle == null) s.angle = s.endAngle - s.startAngle; else if (s.endAngle == null) s.endAngle = s.startAngle + s.angle; pv.Mark.prototype.buildImplied.call(this, s); }; /** * Abstract; not implemented. There is no explicit constructor; this class * merely serves to document the attributes that are used on particles in * physics simulations. * * @class A weighted particle that can participate in a force simulation. * * @name pv.Particle */ /** * The next particle in the simulation. Particles form a singly-linked list. * * @field * @type pv.Particle * @name pv.Particle.prototype.next */ /** * The x-position of the particle. * * @field * @type number * @name pv.Particle.prototype.x */ /** * The y-position of the particle. * * @field * @type number * @name pv.Particle.prototype.y */ /** * The x-velocity of the particle. * * @field * @type number * @name pv.Particle.prototype.vx */ /** * The y-velocity of the particle. * * @field * @type number * @name pv.Particle.prototype.vy */ /** * The x-position of the particle at -dt. * * @field * @type number * @name pv.Particle.prototype.px */ /** * The y-position of the particle at -dt. * * @field * @type number * @name pv.Particle.prototype.py */ /** * The x-force on the particle. * * @field * @type number * @name pv.Particle.prototype.fx */ /** * The y-force on the particle. * * @field * @type number * @name pv.Particle.prototype.fy */ /** * Constructs a new empty simulation. * * @param {array} particles * @returns {pv.Simulation} a new simulation for the specified particles. * @see pv.Simulation */ pv.simulation = function(particles) { return new pv.Simulation(particles); }; /** * Constructs a new simulation for the specified particles. * * @class Represents a particle simulation. Particles are massive points in * two-dimensional space. Forces can be applied to these particles, causing them * to move. Constraints can also be applied to restrict particle movement, for * example, constraining particles to a fixed position, or simulating collision * between circular particles with area. * *

    The simulation uses Position Verlet * integration, due to the ease with which geometric constraints can be * implemented. For each time step, Verlet integration is performed, new forces * are accumulated, and then constraints are applied. * *

    The simulation makes two simplifying assumptions: all particles are * equal-mass, and the time step of the simulation is fixed. It would be easy to * incorporate variable-mass particles as a future enhancement. Variable time * steps are also possible, but are likely to introduce instability in the * simulation. * *

    This class can be used directly to simulate particle interaction. * Alternatively, for network diagrams, see {@link pv.Layout.Force}. * * @param {array} particles an array of {@link pv.Particle}s to simulate. * @see pv.Layout.Force * @see pv.Force * @see pv.Constraint */ pv.Simulation = function(particles) { for (var i = 0; i < particles.length; i++) this.particle(particles[i]); }; /** * The particles in the simulation. Particles are stored as a linked list; this * field represents the first particle in the simulation. * * @field * @type pv.Particle * @name pv.Simulation.prototype.particles */ /** * The forces in the simulation. Forces are stored as a linked list; this field * represents the first force in the simulation. * * @field * @type pv.Force * @name pv.Simulation.prototype.forces */ /** * The constraints in the simulation. Constraints are stored as a linked list; * this field represents the first constraint in the simulation. * * @field * @type pv.Constraint * @name pv.Simulation.prototype.constraints */ /** * Adds the specified particle to the simulation. * * @param {pv.Particle} p the new particle. * @returns {pv.Simulation} this. */ pv.Simulation.prototype.particle = function(p) { p.next = this.particles; /* Default velocities and forces to zero if unset. */ if (isNaN(p.px)) p.px = p.x; if (isNaN(p.py)) p.py = p.y; if (isNaN(p.fx)) p.fx = 0; if (isNaN(p.fy)) p.fy = 0; this.particles = p; return this; }; /** * Adds the specified force to the simulation. * * @param {pv.Force} f the new force. * @returns {pv.Simulation} this. */ pv.Simulation.prototype.force = function(f) { f.next = this.forces; this.forces = f; return this; }; /** * Adds the specified constraint to the simulation. * * @param {pv.Constraint} c the new constraint. * @returns {pv.Simulation} this. */ pv.Simulation.prototype.constraint = function(c) { c.next = this.constraints; this.constraints = c; return this; }; /** * Apply constraints, and then set the velocities to zero. * * @returns {pv.Simulation} this. */ pv.Simulation.prototype.stabilize = function(n) { var c; if (!arguments.length) n = 3; // TODO use cooling schedule for (var i = 0; i < n; i++) { var q = new pv.Quadtree(this.particles); for (c = this.constraints; c; c = c.next) c.apply(this.particles, q); } for (var p = this.particles; p; p = p.next) { p.px = p.x; p.py = p.y; } return this; }; /** * Advances the simulation one time-step. */ pv.Simulation.prototype.step = function() { var p, f, c; /* * Assumptions: * - The mass (m) of every particles is 1. * - The time step (dt) is 1. */ /* Position Verlet integration. */ for (p = this.particles; p; p = p.next) { var px = p.px, py = p.py; p.px = p.x; p.py = p.y; p.x += p.vx = ((p.x - px) + p.fx); p.y += p.vy = ((p.y - py) + p.fy); } /* Apply constraints, then accumulate new forces. */ var q = new pv.Quadtree(this.particles); for (c = this.constraints; c; c = c.next) c.apply(this.particles, q); for (p = this.particles; p; p = p.next) p.fx = p.fy = 0; for (f = this.forces; f; f = f.next) f.apply(this.particles, q); }; /** * Constructs a new quadtree for the specified array of particles. * * @class Represents a quadtree: a two-dimensional recursive spatial * subdivision. This particular implementation uses square partitions, dividing * each square into four equally-sized squares. Each particle exists in a unique * node; if multiple particles are in the same position, some particles may be * stored on internal nodes rather than leaf nodes. * *

    This quadtree can be used to accelerate various spatial operations, such * as the Barnes-Hut approximation for computing n-body forces, or collision * detection. * * @see pv.Force.charge * @see pv.Constraint.collision * @param {pv.Particle} particles the linked list of particles. */ pv.Quadtree = function(particles) { var p; /* Compute bounds. */ var x1 = Number.POSITIVE_INFINITY, y1 = x1, x2 = Number.NEGATIVE_INFINITY, y2 = x2; for (p = particles; p; p = p.next) { if (p.x < x1) x1 = p.x; if (p.y < y1) y1 = p.y; if (p.x > x2) x2 = p.x; if (p.y > y2) y2 = p.y; } /* Squarify the bounds. */ var dx = x2 - x1, dy = y2 - y1; if (dx > dy) y2 = y1 + dx; else x2 = x1 + dy; this.xMin = x1; this.yMin = y1; this.xMax = x2; this.yMax = y2; /** * @ignore Recursively inserts the specified particle p at the node * n or one of its descendants. The bounds are defined by [x1, * x2] and [y1, y2]. */ function insert(n, p, x1, y1, x2, y2) { if (isNaN(p.x) || isNaN(p.y)) return; // ignore invalid particles if (n.leaf) { if (n.p) { /* * If the particle at this leaf node is at the same position as the new * particle we are adding, we leave the particle associated with the * internal node while adding the new particle to a child node. This * avoids infinite recursion. */ if ((Math.abs(n.p.x - p.x) + Math.abs(n.p.y - p.y)) < .01) { insertChild(n, p, x1, y1, x2, y2); } else { var v = n.p; n.p = null; insertChild(n, v, x1, y1, x2, y2); insertChild(n, p, x1, y1, x2, y2); } } else { n.p = p; } } else { insertChild(n, p, x1, y1, x2, y2); } } /** * @ignore Recursively inserts the specified particle p into a * descendant of node n. The bounds are defined by [x1, * x2] and [y1, y2]. */ function insertChild(n, p, x1, y1, x2, y2) { /* Compute the split point, and the quadrant in which to insert p. */ var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = p.x >= sx, bottom = p.y >= sy; /* Recursively insert into the child node. */ n.leaf = false; switch ((bottom << 1) + right) { case 0: n = n.c1 || (n.c1 = new pv.Quadtree.Node()); break; case 1: n = n.c2 || (n.c2 = new pv.Quadtree.Node()); break; case 2: n = n.c3 || (n.c3 = new pv.Quadtree.Node()); break; case 3: n = n.c4 || (n.c4 = new pv.Quadtree.Node()); break; } /* Update the bounds as we recurse. */ if (right) x1 = sx; else x2 = sx; if (bottom) y1 = sy; else y2 = sy; insert(n, p, x1, y1, x2, y2); } /* Insert all particles. */ this.root = new pv.Quadtree.Node(); for (p = particles; p; p = p.next) insert(this.root, p, x1, y1, x2, y2); }; /** * The root node of the quadtree. * * @type pv.Quadtree.Node * @name pv.Quadtree.prototype.root */ /** * The minimum x-coordinate value of all contained particles. * * @type number * @name pv.Quadtree.prototype.xMin */ /** * The maximum x-coordinate value of all contained particles. * * @type number * @name pv.Quadtree.prototype.xMax */ /** * The minimum y-coordinate value of all contained particles. * * @type number * @name pv.Quadtree.prototype.yMin */ /** * The maximum y-coordinate value of all contained particles. * * @type number * @name pv.Quadtree.prototype.yMax */ /** * Constructs a new node. * * @class A node in a quadtree. * * @see pv.Quadtree */ pv.Quadtree.Node = function() { /* * Prepopulating all attributes significantly increases performance! Also, * letting the language interpreter manage garbage collection was moderately * faster than creating a cache pool. */ this.leaf = true; this.c1 = null; this.c2 = null; this.c3 = null; this.c4 = null; this.p = null; }; /** * True if this node is a leaf node; i.e., it has no children. Note that both * leaf nodes and non-leaf (internal) nodes may have associated particles. If * this is a non-leaf node, then at least one of {@link #c1}, {@link #c2}, * {@link #c3} or {@link #c4} is guaranteed to be non-null. * * @type boolean * @name pv.Quadtree.Node.prototype.leaf */ /** * The particle associated with this node, if any. * * @type pv.Particle * @name pv.Quadtree.Node.prototype.p */ /** * The child node for the second quadrant, if any. * * @type pv.Quadtree.Node * @name pv.Quadtree.Node.prototype.c2 */ /** * The child node for the third quadrant, if any. * * @type pv.Quadtree.Node * @name pv.Quadtree.Node.prototype.c3 */ /** * The child node for the fourth quadrant, if any. * * @type pv.Quadtree.Node * @name pv.Quadtree.Node.prototype.c4 */ /** * Abstract; see an implementing class. * * @class Represents a force that acts on particles. Note that this interface * does not specify how to bind a force to specific particles; in general, * forces are applied globally to all particles. However, some forces may be * applied to specific particles or between particles, such as spring forces, * through additional specialization. * * @see pv.Simulation * @see pv.Particle * @see pv.Force.charge * @see pv.Force.drag * @see pv.Force.spring */ pv.Force = {}; /** * Applies this force to the specified particles. * * @function * @name pv.Force.prototype.apply * @param {pv.Particle} particles particles to which to apply this force. * @param {pv.Quadtree} q a quadtree for spatial acceleration. */ /** * Constructs a new charge force, with an optional charge constant. The charge * constant can be negative for repulsion (e.g., particles with electrical * charge of equal sign), or positive for attraction (e.g., massive particles * with mutual gravity). The default charge constant is -40. * * @class An n-body force, as defined by Coulomb's law or Newton's law of * gravitation, inversely proportional to the square of the distance between * particles. Note that the force is independent of the mass of the * associated particles, and that the particles do not have charges of varying * magnitude; instead, the attraction or repulsion of all particles is globally * specified as the charge {@link #constant}. * *

    This particular implementation uses the Barnes-Hut algorithm. For details, * see "A * hierarchical O(N log N) force-calculation algorithm", J. Barnes & * P. Hut, Nature 1986. * * @name pv.Force.charge * @param {number} [k] the charge constant. */ pv.Force.charge = function(k) { var min = 2, // minimum distance at which to observe forces min1 = 1 / min, max = 500, // maximum distance at which to observe forces max1 = 1 / max, theta = .9, // Barnes-Hut theta approximation constant force = {}; if (!arguments.length) k = -40; // default charge constant (repulsion) /** * Sets or gets the charge constant. If an argument is specified, it is the * new charge constant. The charge constant can be negative for repulsion * (e.g., particles with electrical charge of equal sign), or positive for * attraction (e.g., massive particles with mutual gravity). The default * charge constant is -40. * * @function * @name pv.Force.charge.prototype.constant * @param {number} x the charge constant. * @returns {pv.Force.charge} this. */ force.constant = function(x) { if (arguments.length) { k = Number(x); return force; } return k; }; /** * Sets or gets the domain; specifies the minimum and maximum domain within * which charge forces are applied. A minimum distance threshold avoids * applying forces that are two strong (due to granularity of the simulation's * numeric integration). A maximum distance threshold improves performance by * skipping force calculations for particles that are far apart. * *

    The default domain is [2, 500]. * * @function * @name pv.Force.charge.prototype.domain * @param {number} a * @param {number} b * @returns {pv.Force.charge} this. */ force.domain = function(a, b) { if (arguments.length) { min = Number(a); min1 = 1 / min; max = Number(b); max1 = 1 / max; return force; } return [min, max]; }; /** * Sets or gets the Barnes-Hut approximation factor. The Barnes-Hut * approximation criterion is the ratio of the size of the quadtree node to * the distance from the point to the node's center of mass is beneath some * threshold. * * @function * @name pv.Force.charge.prototype.theta * @param {number} x the new Barnes-Hut approximation factor. * @returns {pv.Force.charge} this. */ force.theta = function(x) { if (arguments.length) { theta = Number(x); return force; } return theta; }; /** * @ignore Recursively computes the center of charge for each node in the * quadtree. This is equivalent to the center of mass, assuming that all * particles have unit weight. */ function accumulate(n) { var cx = 0, cy = 0; n.cn = 0; function accumulateChild(c) { accumulate(c); n.cn += c.cn; cx += c.cn * c.cx; cy += c.cn * c.cy; } if (!n.leaf) { if (n.c1) accumulateChild(n.c1); if (n.c2) accumulateChild(n.c2); if (n.c3) accumulateChild(n.c3); if (n.c4) accumulateChild(n.c4); } if (n.p) { n.cn += k; cx += k * n.p.x; cy += k * n.p.y; } n.cx = cx / n.cn; n.cy = cy / n.cn; } /** * @ignore Recursively computes forces on the given particle using the given * quadtree node. The Barnes-Hut approximation criterion is the ratio of the * size of the quadtree node to the distance from the point to the node's * center of mass is beneath some threshold. */ function forces(n, p, x1, y1, x2, y2) { var dx = n.cx - p.x, dy = n.cy - p.y, dn = 1 / Math.sqrt(dx * dx + dy * dy); /* Barnes-Hut criterion. */ if ((n.leaf && (n.p != p)) || ((x2 - x1) * dn < theta)) { if (dn < max1) return; if (dn > min1) dn = min1; var kc = n.cn * dn * dn * dn, fx = dx * kc, fy = dy * kc; p.fx += fx; p.fy += fy; } else if (!n.leaf) { var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5; if (n.c1) forces(n.c1, p, x1, y1, sx, sy); if (n.c2) forces(n.c2, p, sx, y1, x2, sy); if (n.c3) forces(n.c3, p, x1, sy, sx, y2); if (n.c4) forces(n.c4, p, sx, sy, x2, y2); if (dn < max1) return; if (dn > min1) dn = min1; if (n.p && (n.p != p)) { var kc = k * dn * dn * dn, fx = dx * kc, fy = dy * kc; p.fx += fx; p.fy += fy; } } } /** * Applies this force to the specified particles. The force is applied between * all pairs of particles within the domain, using the specified quadtree to * accelerate n-body force calculation using the Barnes-Hut approximation * criterion. * * @function * @name pv.Force.charge.prototype.apply * @param {pv.Particle} particles particles to which to apply this force. * @param {pv.Quadtree} q a quadtree for spatial acceleration. */ force.apply = function(particles, q) { accumulate(q.root); for (var p = particles; p; p = p.next) { forces(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax); } }; return force; }; /** * Constructs a new drag force with the specified constant. * * @class Implements a drag force, simulating friction. The drag force is * applied in the opposite direction of the particle's velocity. Since Position * Verlet integration does not track velocities explicitly, the error term with * this estimate of velocity is fairly high, so the drag force may be * inaccurate. * * @extends pv.Force * @param {number} k the drag constant. * @see #constant */ pv.Force.drag = function(k) { var force = {}; if (!arguments.length) k = .1; // default drag constant /** * Sets or gets the drag constant, in the range [0,1]. The default drag * constant is 0.1. The drag forces scales linearly with the particle's * velocity based on the given drag constant. * * @function * @name pv.Force.drag.prototype.constant * @param {number} x the new drag constant. * @returns {pv.Force.drag} this, or the current drag constant. */ force.constant = function(x) { if (arguments.length) { k = x; return force; } return k; }; /** * Applies this force to the specified particles. * * @function * @name pv.Force.drag.prototype.apply * @param {pv.Particle} particles particles to which to apply this force. */ force.apply = function(particles) { if (k) for (var p = particles; p; p = p.next) { p.fx -= k * p.vx; p.fy -= k * p.vy; } }; return force; }; /** * Constructs a new spring force with the specified constant. The links * associated with this spring force must be specified before the spring force * can be applied. * * @class Implements a spring force, per Hooke's law. The spring force can be * configured with a tension constant, rest length, and damping factor. The * tension and damping will automatically be normalized using the inverse square * root of the maximum link degree of attached nodes; this makes springs weaker * between nodes of high link degree. * *

    Unlike other forces (such as charge and drag forces) which may be applied * globally, spring forces are only applied between linked particles. Therefore, * an array of links must be specified before this force can be applied; the * links should be an array of {@link pv.Layout.Network.Link}s. See also * {@link pv.Layout.Force} for an example of using spring and charge forces for * network layout. * * @extends pv.Force * @param {number} k the spring constant. * @see #constant * @see #links */ pv.Force.spring = function(k) { var d = .1, // default damping factor l = 20, // default rest length links, // links on which to apply spring forces kl, // per-spring normalization force = {}; if (!arguments.length) k = .1; // default spring constant (tension) /** * Sets or gets the links associated with this spring force. Unlike other * forces (such as charge and drag forces) which may be applied globally, * spring forces are only applied between linked particles. Therefore, an * array of links must be specified before this force can be applied; the * links should be an array of {@link pv.Layout.Network.Link}s. * * @function * @name pv.Force.spring.prototype.links * @param {array} x the new array of links. * @returns {pv.Force.spring} this, or the current array of links. */ force.links = function(x) { if (arguments.length) { links = x; kl = x.map(function(l) { return 1 / Math.sqrt(Math.max( l.sourceNode.linkDegree, l.targetNode.linkDegree)); }); return force; } return links; }; /** * Sets or gets the spring constant. The default value is 0.1; greater values * will result in stronger tension. The spring tension is automatically * normalized using the inverse square root of the maximum link degree of * attached nodes. * * @function * @name pv.Force.spring.prototype.constant * @param {number} x the new spring constant. * @returns {pv.Force.spring} this, or the current spring constant. */ force.constant = function(x) { if (arguments.length) { k = Number(x); return force; } return k; }; /** * The spring damping factor, in the range [0,1]. Damping functions * identically to drag forces, damping spring bounciness by applying a force * in the opposite direction of attached nodes' velocities. The default value * is 0.1. The spring damping is automatically normalized using the inverse * square root of the maximum link degree of attached nodes. * * @function * @name pv.Force.spring.prototype.damping * @param {number} x the new spring damping factor. * @returns {pv.Force.spring} this, or the current spring damping factor. */ force.damping = function(x) { if (arguments.length) { d = Number(x); return force; } return d; }; /** * The spring rest length. The default value is 20 pixels. * * @function * @name pv.Force.spring.prototype.length * @param {number} x the new spring rest length. * @returns {pv.Force.spring} this, or the current spring rest length. */ force.length = function(x) { if (arguments.length) { l = Number(x); return force; } return l; }; /** * Applies this force to the specified particles. * * @function * @name pv.Force.spring.prototype.apply * @param {pv.Particle} particles particles to which to apply this force. */ force.apply = function(particles) { for (var i = 0; i < links.length; i++) { var a = links[i].sourceNode, b = links[i].targetNode, dx = a.x - b.x, dy = a.y - b.y, dn = Math.sqrt(dx * dx + dy * dy), dd = dn ? (1 / dn) : 1, ks = k * kl[i], // normalized tension kd = d * kl[i], // normalized damping kk = (ks * (dn - l) + kd * (dx * (a.vx - b.vx) + dy * (a.vy - b.vy)) * dd) * dd, fx = -kk * (dn ? dx : (.01 * (.5 - Math.random()))), fy = -kk * (dn ? dy : (.01 * (.5 - Math.random()))); a.fx += fx; a.fy += fy; b.fx -= fx; b.fy -= fy; } }; return force; }; /** * Abstract; see an implementing class. * * @class Represents a constraint that acts on particles. Note that this * interface does not specify how to bind a constraint to specific particles; in * general, constraints are applied globally to all particles. However, some * constraints may be applied to specific particles or between particles, such * as position constraints, through additional specialization. * * @see pv.Simulation * @see pv.Particle * @see pv.Constraint.bound * @see pv.Constraint.collision * @see pv.Constraint.position */ pv.Constraint = {}; /** * Applies this constraint to the specified particles. * * @function * @name pv.Constraint.prototype.apply * @param {pv.Particle} particles particles to which to apply this constraint. * @param {pv.Quadtree} q a quadtree for spatial acceleration. * @returns {pv.Constraint} this. */ /** * Constructs a new collision constraint. The default search radius is 10, and * the default repeat count is 1. A radius function must be specified to compute * the radius of particles. * * @class Constraints circles to avoid overlap. Each particle is treated as a * circle, with the radius of the particle computed using a specified function. * For example, if the particle has an r attribute storing the radius, * the radius function(d) d.r specifies a collision constraint using * this radius. The radius function is passed each {@link pv.Particle} as the * first argument. * *

    To accelerate collision detection, this implementation uses a quadtree and * a search radius. The search radius is computed as the maximum radius of all * particles in the simulation. * * @see pv.Constraint * @param {function} radius the radius function. */ pv.Constraint.collision = function(radius) { var n = 1, // number of times to repeat the constraint r1, px1, py1, px2, py2, constraint = {}; if (!arguments.length) r1 = 10; // default search radius /** * Sets or gets the repeat count. If the repeat count is greater than 1, the * constraint will be applied repeatedly; this is a form of the Gauss-Seidel * method for constraints relaxation. Repeating the collision constraint makes * the constraint have more of an effect when there is a potential for many * co-occurring collisions. * * @function * @name pv.Constraint.collision.prototype.repeat * @param {number} x the number of times to repeat this constraint. * @returns {pv.Constraint.collision} this. */ constraint.repeat = function(x) { if (arguments.length) { n = Number(x); return constraint; } return n; }; /** @private */ function constrain(n, p, x1, y1, x2, y2) { if (!n.leaf) { var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, top = sy > py1, bottom = sy < py2, left = sx > px1, right = sx < px2; if (top) { if (n.c1 && left) constrain(n.c1, p, x1, y1, sx, sy); if (n.c2 && right) constrain(n.c2, p, sx, y1, x2, sy); } if (bottom) { if (n.c3 && left) constrain(n.c3, p, x1, sy, sx, y2); if (n.c4 && right) constrain(n.c4, p, sx, sy, x2, y2); } } if (n.p && (n.p != p)) { var dx = p.x - n.p.x, dy = p.y - n.p.y, l = Math.sqrt(dx * dx + dy * dy), d = r1 + radius(n.p); if (l < d) { var k = (l - d) / l * .5; dx *= k; dy *= k; p.x -= dx; p.y -= dy; n.p.x += dx; n.p.y += dy; } } } /** * Applies this constraint to the specified particles. * * @function * @name pv.Constraint.collision.prototype.apply * @param {pv.Particle} particles particles to which to apply this constraint. * @param {pv.Quadtree} q a quadtree for spatial acceleration. */ constraint.apply = function(particles, q) { var p, r, max = -Infinity; for (p = particles; p; p = p.next) { r = radius(p); if (r > max) max = r; } for (var i = 0; i < n; i++) { for (p = particles; p; p = p.next) { r = (r1 = radius(p)) + max; px1 = p.x - r; px2 = p.x + r; py1 = p.y - r; py2 = p.y + r; constrain(q.root, p, q.xMin, q.yMin, q.xMax, q.yMax); } } }; return constraint; }; /** * Constructs a default position constraint using the fix attribute. * An optional position function can be specified to determine how the fixed * position per-particle is determined. * * @class Constraints particles to a fixed position. The fixed position per * particle is determined using a given position function, which defaults to * function(d) d.fix. * *

    If the position function returns null, then no position constraint is * applied to the given particle. Otherwise, the particle's position is set to * the returned position, as expressed by a {@link pv.Vector}. (Note: the * position does not need to be an instance of pv.Vector, but simply an * object with x and y attributes.) * *

    This constraint also supports a configurable alpha parameter, which * defaults to 1. If the alpha parameter is in the range [0,1], then rather than * setting the particle's new position directly to the position returned by the * supplied position function, the particle's position is interpolated towards * the fixed position. This results is a smooth (exponential) drift towards the * fixed position, which can increase the stability of the physics simulation. * In addition, the alpha parameter can be decayed over time, relaxing the * position constraint, which helps to stabilize on an optimal solution. * * @param {function} [f] the position function. */ pv.Constraint.position = function(f) { var a = 1, // default alpha constraint = {}; if (!arguments.length) /** @ignore */ f = function(p) { return p.fix; }; /** * Sets or gets the alpha parameter for position interpolation. If the alpha * parameter is in the range [0,1], then rather than setting the particle's * new position directly to the position returned by the supplied position * function, the particle's position is interpolated towards the fixed * position. * * @function * @name pv.Constraint.position.prototype.alpha * @param {number} x the new alpha parameter, in the range [0,1]. * @returns {pv.Constraint.position} this. */ constraint.alpha = function(x) { if (arguments.length) { a = Number(x); return constraint; } return a; }; /** * Applies this constraint to the specified particles. * * @function * @name pv.Constraint.position.prototype.apply * @param {pv.Particle} particles particles to which to apply this constraint. */ constraint.apply = function(particles) { for (var p = particles; p; p = p.next) { var v = f(p); if (v) { p.x += (v.x - p.x) * a; p.y += (v.y - p.y) * a; p.fx = p.fy = p.vx = p.vy = 0; } } }; return constraint; }; /** * Constructs a new bound constraint. Before the constraint can be used, the * {@link #x} and {@link #y} methods must be call to specify the bounds. * * @class Constrains particles to within fixed rectangular bounds. For example, * this constraint can be used to constrain particles in a physics simulation * within the bounds of an enclosing panel. * *

    Note that the current implementation treats particles as points, with no * area. If the particles are rendered as dots, be sure to include some * additional padding to inset the bounds such that the edges of the dots do not * get clipped by the panel bounds. If the particles have different radii, this * constraint would need to be extended using a radius function, similar to * {@link pv.Constraint.collision}. * * @see pv.Layout.Force * @extends pv.Constraint */ pv.Constraint.bound = function() { var constraint = {}, x, y; /** * Sets or gets the bounds on the x-coordinate. * * @function * @name pv.Constraint.bound.prototype.x * @param {number} min the minimum allowed x-coordinate. * @param {number} max the maximum allowed x-coordinate. * @returns {pv.Constraint.bound} this. */ constraint.x = function(min, max) { if (arguments.length) { x = {min: Math.min(min, max), max: Math.max(min, max)}; return this; } return x; }; /** * Sets or gets the bounds on the y-coordinate. * * @function * @name pv.Constraint.bound.prototype.y * @param {number} min the minimum allowed y-coordinate. * @param {number} max the maximum allowed y-coordinate. * @returns {pv.Constraint.bound} this. */ constraint.y = function(min, max) { if (arguments.length) { y = {min: Math.min(min, max), max: Math.max(min, max)}; return this; } return y; }; /** * Applies this constraint to the specified particles. * * @function * @name pv.Constraint.bound.prototype.apply * @param {pv.Particle} particles particles to which to apply this constraint. */ constraint.apply = function(particles) { if (x) for (var p = particles; p; p = p.next) { p.x = p.x < x.min ? x.min : (p.x > x.max ? x.max : p.x); } if (y) for (var p = particles; p; p = p.next) { p.y = p.y < y.min ? y.min : (p.y > y.max ? y.max : p.y); } }; return constraint; }; /** * Constructs a new, empty layout with default properties. Layouts are not * typically constructed directly; instead, a concrete subclass is added to an * existing panel via {@link pv.Mark#add}. * * @class Represents an abstract layout, encapsulating a visualization technique * such as a streamgraph or treemap. Layouts are themselves containers, * extending from {@link pv.Panel}, and defining a set of mark prototypes as * children. These mark prototypes provide default properties that together * implement the given visualization technique. * *

    Layouts do not initially contain any marks; any exported marks (such as a * network layout's link and node) are intended to be used as * prototypes. By adding a concrete mark, such as a {@link pv.Bar}, to the * appropriate mark prototype, the mark is added to the layout and inherits the * given properties. This approach allows further customization of the layout, * either by choosing a different mark type to add, or more simply by overriding * some of the layout's defined properties. * *

    Each concrete layout, such as treemap or circle-packing, has different * behavior and may export different mark prototypes, depending on what marks * are typically needed to render the desired visualization. Therefore it is * important to understand how each layout is structured, such that the provided * mark prototypes are used appropriately. * *

    In addition to the mark prototypes, layouts may define custom properties * that affect the overall behavior of the layout. For example, a treemap layout * might use a property to specify which layout algorithm to use. These * properties are just like other mark properties, and can be defined as * constants or as functions. As with panels, the data property can be used to * replicate layouts, and properties can be defined to in terms of layout data. * * @extends pv.Panel */ pv.Layout = function() { pv.Panel.call(this); }; pv.Layout.prototype = pv.extend(pv.Panel); /** * @private Defines a local property with the specified name and cast. Note that * although the property method is only defined locally, the cast function is * global, which is necessary since properties are inherited! * * @param {string} name the property name. * @param {function} [cast] the cast function for this property. */ pv.Layout.prototype.property = function(name, cast) { if (!this.hasOwnProperty("properties")) { this.properties = pv.extend(this.properties); } this.properties[name] = true; this.propertyMethod(name, false, pv.Mark.cast[name] = cast); return this; }; /** * Constructs a new, empty network layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Represents an abstract layout for network diagrams. This class * provides the basic structure for both node-link diagrams (such as * force-directed graph layout) and space-filling network diagrams (such as * sunbursts and treemaps). Note that "network" here is a general term that * includes hierarchical structures; a tree is represented using links from * child to parent. * *

    Network layouts require the graph data structure to be defined using two * properties:

      * *
    • nodes - an array of objects representing nodes. Objects in this * array must conform to the {@link pv.Layout.Network.Node} interface; which is * to say, be careful to avoid naming collisions with automatic attributes such * as index and linkDegree. If the nodes property is defined * as an array of primitives, such as numbers or strings, these primitives are * automatically wrapped in an object; the resulting object's nodeValue * attribute points to the original primitive value. * *

    • links - an array of objects representing links. Objects in * this array must conform to the {@link pv.Layout.Network.Link} interface; at a * minimum, either source and target indexes or * sourceNode and targetNode references must be set. Note that * if the links property is defined after the nodes property, the links can be * defined in terms of this.nodes(). * *
    * *

    Three standard mark prototypes are provided:

      * *
    • node - for rendering nodes; typically a {@link pv.Dot}. The node * mark is added directly to the layout, with the data property defined via the * layout's nodes property. Properties such as strokeStyle and * fillStyle can be overridden to compute properties from node data * dynamically. * *

    • link - for rendering links; typically a {@link pv.Line}. The * link mark is added to a child panel, whose data property is defined as * layout's links property. The link's data property is then a * two-element array of the source node and target node. Thus, poperties such as * strokeStyle and fillStyle can be overridden to compute * properties from either the node data (the first argument) or the link data * (the second argument; the parent panel data) dynamically. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. The label mark is added directly to the layout, with the * data property defined via the layout's nodes property. Properties * such as strokeStyle and fillStyle can be overridden to * compute properties from node data dynamically. * *
    Note that some network implementations may not support all three * standard mark prototypes; for example, space-filling hierarchical layouts * typically do not use a link prototype, as the parent-child links are * implied by the structure of the space-filling node marks. Check the * specific network layout for implementation details. * *

    Network layout properties, including nodes and links, * are typically cached rather than re-evaluated with every call to render. This * is a performance optimization, as network layout algorithms can be * expensive. If the network structure changes, call {@link #reset} to clear the * cache before rendering. Note that although the network layout properties are * cached, child mark properties, such as the marks used to render the nodes and * links, are not. Therefore, non-structural changes to the network * layout, such as changing the color of a mark on mouseover, do not need to * reset the layout. * * @see pv.Layout.Hierarchy * @see pv.Layout.Force * @see pv.Layout.Matrix * @see pv.Layout.Arc * @see pv.Layout.Rollup * @extends pv.Layout */ pv.Layout.Network = function() { pv.Layout.call(this); var that = this; /* @private Version tracking to cache layout state, improving performance. */ this.$id = pv.id(); /** * The node prototype. This prototype is intended to be used with a Dot mark * in conjunction with the link prototype. * * @type pv.Mark * @name pv.Layout.Network.prototype.node */ (this.node = new pv.Mark() .data(function() { return that.nodes(); }) .strokeStyle("#1f77b4") .fillStyle("#fff") .left(function(n) { return n.x; }) .top(function(n) { return n.y; })).parent = this; /** * The link prototype, which renders edges between source nodes and target * nodes. This prototype is intended to be used with a Line mark in * conjunction with the node prototype. * * @type pv.Mark * @name pv.Layout.Network.prototype.link */ this.link = new pv.Mark() .extend(this.node) .data(function(p) { return [p.sourceNode, p.targetNode]; }) .fillStyle(null) .lineWidth(function(d, p) { return p.linkValue * 1.5; }) .strokeStyle("rgba(0,0,0,.2)"); this.link.add = function(type) { return that.add(pv.Panel) .data(function() { return that.links(); }) .add(type) .extend(this); }; /** * The node label prototype, which renders the node name adjacent to the node. * This prototype is provided as an alternative to using the anchor on the * node mark; it is primarily intended to be used with radial node-link * layouts, since it provides a convenient mechanism to set the text angle. * * @type pv.Mark * @name pv.Layout.Network.prototype.label */ (this.label = new pv.Mark() .extend(this.node) .textMargin(7) .textBaseline("middle") .text(function(n) { return n.nodeName || n.nodeValue; }) .textAngle(function(n) { var a = n.midAngle; return pv.Wedge.upright(a) ? a : (a + Math.PI); }) .textAlign(function(n) { return pv.Wedge.upright(n.midAngle) ? "left" : "right"; })).parent = this; }; /** * @class Represents a node in a network layout. There is no explicit * constructor; this class merely serves to document the attributes that are * used on nodes in network layouts. (Note that hierarchical nodes place * additional requirements on node representation, vis {@link pv.Dom.Node}.) * * @see pv.Layout.Network * @name pv.Layout.Network.Node */ /** * The node index, zero-based. This attribute is populated automatically based * on the index in the array returned by the nodes property. * * @type number * @name pv.Layout.Network.Node.prototype.index */ /** * The link degree; the sum of link values for all incoming and outgoing links. * This attribute is populated automatically. * * @type number * @name pv.Layout.Network.Node.prototype.linkDegree */ /** * The node name; optional. If present, this attribute will be used to provide * the text for node labels. If not present, the label text will fallback to the * nodeValue attribute. * * @type string * @name pv.Layout.Network.Node.prototype.nodeName */ /** * The node value; optional. If present, and no nodeName attribute is * present, the node value will be used as the label text. This attribute is * also automatically populated if the nodes are specified as an array of * primitives, such as strings or numbers. * * @type object * @name pv.Layout.Network.Node.prototype.nodeValue */ /** * @class Represents a link in a network layout. There is no explicit * constructor; this class merely serves to document the attributes that are * used on links in network layouts. For hierarchical layouts, this class is * used to represent the parent-child links. * * @see pv.Layout.Network * @name pv.Layout.Network.Link */ /** * The link value, or weight; optional. If not specified (or not a number), the * default value of 1 is used. * * @type number * @name pv.Layout.Network.Link.prototype.linkValue */ /** * The link's source node. If not set, this value will be derived from the * source attribute index. * * @type pv.Layout.Network.Node * @name pv.Layout.Network.Link.prototype.sourceNode */ /** * The link's target node. If not set, this value will be derived from the * target attribute index. * * @type pv.Layout.Network.Node * @name pv.Layout.Network.Link.prototype.targetNode */ /** * Alias for sourceNode, as expressed by the index of the source node. * This attribute is not populated automatically, but may be used as a more * convenient identification of the link's source, for example in a static JSON * representation. * * @type number * @name pv.Layout.Network.Link.prototype.source */ /** * Alias for targetNode, as expressed by the index of the target node. * This attribute is not populated automatically, but may be used as a more * convenient identification of the link's target, for example in a static JSON * representation. * * @type number * @name pv.Layout.Network.Link.prototype.target */ /** * Alias for linkValue. This attribute is not populated automatically, * but may be used instead of the linkValue attribute when specifying * links. * * @type number * @name pv.Layout.Network.Link.prototype.value */ /** @private Transform nodes and links on cast. */ pv.Layout.Network.prototype = pv.extend(pv.Layout) .property("nodes", function(v) { return v.map(function(d, i) { if (typeof d != "object") d = {nodeValue: d}; d.index = i; d.linkDegree = 0; return d; }); }) .property("links", function(v) { return v.map(function(d) { if (isNaN(d.linkValue)) d.linkValue = isNaN(d.value) ? 1 : d.value; return d; }); }); /** * Resets the cache, such that changes to layout property definitions will be * visible on subsequent render. Unlike normal marks (and normal layouts), * properties associated with network layouts are not automatically re-evaluated * on render; the properties are cached, and any expensive layout algorithms are * only run after the layout is explicitly reset. * * @returns {pv.Layout.Network} this. */ pv.Layout.Network.prototype.reset = function() { this.$id = pv.id(); return this; }; /** @private Skip evaluating properties if cached. */ pv.Layout.Network.prototype.buildProperties = function(s, properties) { if ((s.$id || 0) < this.$id) { pv.Layout.prototype.buildProperties.call(this, s, properties); } }; /** @private Compute link degrees; map source and target indexes to nodes. */ pv.Layout.Network.prototype.buildImplied = function(s) { pv.Layout.prototype.buildImplied.call(this, s); if (s.$id >= this.$id) return true; s.$id = this.$id; s.links.forEach(function(d) { var v = d.linkValue; (d.sourceNode || (d.sourceNode = s.nodes[d.source])).linkDegree += v; (d.targetNode || (d.targetNode = s.nodes[d.target])).linkDegree += v; }); }; /** * Constructs a new, empty hierarchy layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Represents an abstract layout for hierarchy diagrams. This class is a * specialization of {@link pv.Layout.Network}, providing the basic structure * for both hierarchical node-link diagrams (such as Reingold-Tilford trees) and * space-filling hierarchy diagrams (such as sunbursts and treemaps). * *

    Unlike general network layouts, the links property need not be * defined explicitly. Instead, the links are computed implicitly from the * parentNode attribute of the node objects, as defined by the * nodes property. This implementation is also available as * {@link #links}, for reuse with non-hierarchical layouts; for example, to * render a tree using force-directed layout. * *

    Correspondingly, the nodes property is represented as a union of * {@link pv.Layout.Network.Node} and {@link pv.Dom.Node}. To construct a node * hierarchy from a simple JSON map, use the {@link pv.Dom} operator; this * operator also provides an easy way to sort nodes before passing them to the * layout. * *

    For more details on how to use this layout, see * {@link pv.Layout.Network}. * * @see pv.Layout.Cluster * @see pv.Layout.Partition * @see pv.Layout.Tree * @see pv.Layout.Treemap * @see pv.Layout.Indent * @see pv.Layout.Pack * @extends pv.Layout.Network */ pv.Layout.Hierarchy = function() { pv.Layout.Network.call(this); this.link.strokeStyle("#ccc"); }; pv.Layout.Hierarchy.prototype = pv.extend(pv.Layout.Network); /** @private Compute the implied links. (Links are null by default.) */ pv.Layout.Hierarchy.prototype.buildImplied = function(s) { if (!s.links) s.links = pv.Layout.Hierarchy.links.call(this); pv.Layout.Network.prototype.buildImplied.call(this, s); }; /** The implied links; computes links using the parentNode attribute. */ pv.Layout.Hierarchy.links = function() { return this.nodes() .filter(function(n) { return n.parentNode; }) .map(function(n) { return { sourceNode: n, targetNode: n.parentNode, linkValue: 1 }; }); }; /** @private Provides standard node-link layout based on breadth & depth. */ pv.Layout.Hierarchy.NodeLink = { /** @private */ buildImplied: function(s) { var nodes = s.nodes, orient = s.orient, horizontal = /^(top|bottom)$/.test(orient), w = s.width, h = s.height; /* Compute default inner and outer radius. */ if (orient == "radial") { var ir = s.innerRadius, or = s.outerRadius; if (ir == null) ir = 0; if (or == null) or = Math.min(w, h) / 2; } /** @private Returns the radius of the given node. */ function radius(n) { return n.parentNode ? (n.depth * (or - ir) + ir) : 0; } /** @private Returns the angle of the given node. */ function midAngle(n) { return (n.parentNode ? (n.breadth - .25) * 2 * Math.PI : 0); } /** @private */ function x(n) { switch (orient) { case "left": return n.depth * w; case "right": return w - n.depth * w; case "top": return n.breadth * w; case "bottom": return w - n.breadth * w; case "radial": return w / 2 + radius(n) * Math.cos(n.midAngle); } } /** @private */ function y(n) { switch (orient) { case "left": return n.breadth * h; case "right": return h - n.breadth * h; case "top": return n.depth * h; case "bottom": return h - n.depth * h; case "radial": return h / 2 + radius(n) * Math.sin(n.midAngle); } } for (var i = 0; i < nodes.length; i++) { var n = nodes[i]; n.midAngle = orient == "radial" ? midAngle(n) : horizontal ? Math.PI / 2 : 0; n.x = x(n); n.y = y(n); if (n.firstChild) n.midAngle += Math.PI; } } }; /** @private Provides standard space-filling layout based on breadth & depth. */ pv.Layout.Hierarchy.Fill = { /** @private */ constructor: function() { this.node .strokeStyle("#fff") .fillStyle("#ccc") .width(function(n) { return n.dx; }) .height(function(n) { return n.dy; }) .innerRadius(function(n) { return n.innerRadius; }) .outerRadius(function(n) { return n.outerRadius; }) .startAngle(function(n) { return n.startAngle; }) .angle(function(n) { return n.angle; }); this.label .textAlign("center") .left(function(n) { return n.x + (n.dx / 2); }) .top(function(n) { return n.y + (n.dy / 2); }); /* Hide unsupported link. */ delete this.link; }, /** @private */ buildImplied: function(s) { var nodes = s.nodes, orient = s.orient, horizontal = /^(top|bottom)$/.test(orient), w = s.width, h = s.height, depth = -nodes[0].minDepth; /* Compute default inner and outer radius. */ if (orient == "radial") { var ir = s.innerRadius, or = s.outerRadius; if (ir == null) ir = 0; if (ir) depth *= 2; // use full depth step for root if (or == null) or = Math.min(w, h) / 2; } /** @private Scales the specified depth for a space-filling layout. */ function scale(d, depth) { return (d + depth) / (1 + depth); } /** @private */ function x(n) { switch (orient) { case "left": return scale(n.minDepth, depth) * w; case "right": return (1 - scale(n.maxDepth, depth)) * w; case "top": return n.minBreadth * w; case "bottom": return (1 - n.maxBreadth) * w; case "radial": return w / 2; } } /** @private */ function y(n) { switch (orient) { case "left": return n.minBreadth * h; case "right": return (1 - n.maxBreadth) * h; case "top": return scale(n.minDepth, depth) * h; case "bottom": return (1 - scale(n.maxDepth, depth)) * h; case "radial": return h / 2; } } /** @private */ function dx(n) { switch (orient) { case "left": case "right": return (n.maxDepth - n.minDepth) / (1 + depth) * w; case "top": case "bottom": return (n.maxBreadth - n.minBreadth) * w; case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.cos(n.midAngle) : 0; } } /** @private */ function dy(n) { switch (orient) { case "left": case "right": return (n.maxBreadth - n.minBreadth) * h; case "top": case "bottom": return (n.maxDepth - n.minDepth) / (1 + depth) * h; case "radial": return n.parentNode ? (n.innerRadius + n.outerRadius) * Math.sin(n.midAngle) : 0; } } /** @private */ function innerRadius(n) { return Math.max(0, scale(n.minDepth, depth / 2)) * (or - ir) + ir; } /** @private */ function outerRadius(n) { return scale(n.maxDepth, depth / 2) * (or - ir) + ir; } /** @private */ function startAngle(n) { return (n.parentNode ? n.minBreadth - .25 : 0) * 2 * Math.PI; } /** @private */ function angle(n) { return (n.parentNode ? n.maxBreadth - n.minBreadth : 1) * 2 * Math.PI; } for (var i = 0; i < nodes.length; i++) { var n = nodes[i]; n.x = x(n); n.y = y(n); if (orient == "radial") { n.innerRadius = innerRadius(n); n.outerRadius = outerRadius(n); n.startAngle = startAngle(n); n.angle = angle(n); n.midAngle = n.startAngle + n.angle / 2; } else { n.midAngle = horizontal ? -Math.PI / 2 : 0; } n.dx = dx(n); n.dy = dy(n); } } }; /** * Constructs a new, empty grid layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a grid layout with regularly-sized rows and columns. The * number of rows and columns are determined from their respective * properties. For example, the 2×3 array: * *

    1 2 3
     * 4 5 6
    * * can be represented using the rows property as: * *
    [[1, 2, 3], [4, 5, 6]]
    * * If your data is in column-major order, you can equivalently use the * columns property. If the rows property is an array, it * takes priority over the columns property. The data is implicitly * transposed, as if the {@link pv.transpose} operator were applied. * *

    This layout exports a single cell mark prototype, which is * intended to be used with a bar, panel, layout, or subclass thereof. The data * property of the cell prototype is defined as the elements in the array. For * example, if the array is a two-dimensional array of values in the range * [0,1], a simple heatmap can be generated as: * *

    vis.add(pv.Layout.Grid)
     *     .rows(arrays)
     *   .cell.add(pv.Bar)
     *     .fillStyle(pv.ramp("white", "black"))
    * * The grid subdivides the full width and height of the parent panel into equal * rectangles. Note, however, that for large, interactive, or animated heatmaps, * you may see significantly better performance through dynamic {@link pv.Image} * generation. * *

    For irregular grids using value-based spatial partitioning, see {@link * pv.Layout.Treemap}. * * @extends pv.Layout */ pv.Layout.Grid = function() { pv.Layout.call(this); var that = this; /** * The cell prototype. This prototype is intended to be used with a bar, * panel, or layout (or subclass thereof) to render the grid cells. * * @type pv.Mark * @name pv.Layout.Grid.prototype.cell */ (this.cell = new pv.Mark() .data(function() { return that.scene[that.index].$grid; }) .width(function() { return that.width() / that.cols(); }) .height(function() { return that.height() / that.rows(); }) .left(function() { return this.width() * (this.index % that.cols()); }) .top(function() { return this.height() * Math.floor(this.index / that.cols()); })).parent = this; }; pv.Layout.Grid.prototype = pv.extend(pv.Layout) .property("rows") .property("cols"); /** * Default properties for grid layouts. By default, there is one row and one * column, and the data is the propagated to the child cell. * * @type pv.Layout.Grid */ pv.Layout.Grid.prototype.defaults = new pv.Layout.Grid() .extend(pv.Layout.prototype.defaults) .rows(1) .cols(1); /** @private */ pv.Layout.Grid.prototype.buildImplied = function(s) { pv.Layout.prototype.buildImplied.call(this, s); var r = s.rows, c = s.cols; if (typeof c == "object") r = pv.transpose(c); if (typeof r == "object") { s.$grid = pv.blend(r); s.rows = r.length; s.cols = r[0] ? r[0].length : 0; } else { s.$grid = pv.repeat([s.data], r * c); } }; /** * The number of rows. This property can also be specified as the data in * row-major order; in this case, the rows property is implicitly set to the * length of the array, and the cols property is set to the length of the first * element in the array. * * @type number * @name pv.Layout.Grid.prototype.rows */ /** * The number of columns. This property can also be specified as the data in * column-major order; in this case, the cols property is implicitly set to the * length of the array, and the rows property is set to the length of the first * element in the array. * * @type number * @name pv.Layout.Grid.prototype.cols */ /** * Constructs a new, empty stack layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a layout for stacked visualizations, ranging from simple * stacked bar charts to more elaborate "streamgraphs" composed of stacked * areas. Stack layouts uses length as a visual encoding, as opposed to * position, as the layers do not share an aligned axis. * *

    Marks can be stacked vertically or horizontally. For example, * *

    vis.add(pv.Layout.Stack)
     *     .layers([[1, 1.2, 1.7, 1.5, 1.7],
     *              [.5, 1, .8, 1.1, 1.3],
     *              [.2, .5, .8, .9, 1]])
     *     .x(function() this.index * 35)
     *     .y(function(d) d * 40)
     *   .layer.add(pv.Area);
    * * specifies a vertically-stacked area chart, using the default "bottom-left" * orientation with "zero" offset. This visualization can be easily changed into * a streamgraph using the "wiggle" offset, which attempts to minimize change in * slope weighted by layer thickness. See the {@link #offset} property for more * supported streamgraph algorithms. * *

    In the simplest case, the layer data can be specified as a two-dimensional * array of numbers. The x and y psuedo-properties are used to * define the thickness of each layer at the given position, respectively; in * the above example of the "bottom-left" orientation, the x and * y psuedo-properties are equivalent to the left and * height properties that you might use if you implemented a stacked * area by hand. * *

    The advantage of using the stack layout is that the baseline, i.e., the * bottom property is computed automatically using the specified offset * algorithm. In addition, the order of layers can be computed using a built-in * algorithm via the order property. * *

    With the exception of the "expand" offset, the stack layout does * not perform any automatic scaling of data; the values returned from * x and y specify pixel sizes. To simplify scaling math, use * this layout in conjunction with {@link pv.Scale.linear} or similar. * *

    In other cases, the values psuedo-property can be used to define * the data more flexibly. As with a typical panel & area, the * layers property corresponds to the data in the enclosing panel, * while the values psuedo-property corresponds to the data for the * area within the panel. For example, given an array of data values: * *

    var crimea = [
     *  { date: "4/1854", wounds: 0, other: 110, disease: 110 },
     *  { date: "5/1854", wounds: 0, other: 95, disease: 105 },
     *  { date: "6/1854", wounds: 0, other: 40, disease: 95 },
     *  ...
    * * and a corresponding array of series names: * *
    var causes = ["wounds", "other", "disease"];
    * * Separate layers can be defined for each cause like so: * *
    vis.add(pv.Layout.Stack)
     *     .layers(causes)
     *     .values(crimea)
     *     .x(function(d) x(d.date))
     *     .y(function(d, p) y(d[p]))
     *   .layer.add(pv.Area)
     *     ...
    * * As with the panel & area case, the datum that is passed to the * psuedo-properties x and y are the values (an element in * crimea); the second argument is the layer data (a string in * causes). Additional arguments specify the data of enclosing panels, * if any. * * @extends pv.Layout */ pv.Layout.Stack = function() { pv.Layout.call(this); var that = this, /** @ignore */ none = function() { return null; }, prop = {t: none, l: none, r: none, b: none, w: none, h: none}, values, buildImplied = that.buildImplied; /** @private Proxy the given property on the layer. */ function proxy(name) { return function() { return prop[name](this.parent.index, this.index); }; } /** @private Compute the layout! */ this.buildImplied = function(s) { buildImplied.call(this, s); var data = s.layers, n = data.length, m, orient = s.orient, horizontal = /^(top|bottom)\b/.test(orient), h = this.parent[horizontal ? "height" : "width"](), x = [], y = [], dy = []; /* * Iterate over the data, evaluating the values, x and y functions. The * context in which the x and y psuedo-properties are evaluated is a * pseudo-mark that is a grandchild of this layout. */ var stack = pv.Mark.stack, o = {parent: {parent: this}}; stack.unshift(null); values = []; for (var i = 0; i < n; i++) { dy[i] = []; y[i] = []; o.parent.index = i; stack[0] = data[i]; values[i] = this.$values.apply(o.parent, stack); if (!i) m = values[i].length; stack.unshift(null); for (var j = 0; j < m; j++) { stack[0] = values[i][j]; o.index = j; if (!i) x[j] = this.$x.apply(o, stack); dy[i][j] = this.$y.apply(o, stack); } stack.shift(); } stack.shift(); /* order */ var index; switch (s.order) { case "inside-out": { var max = dy.map(function(v) { return pv.max.index(v); }), map = pv.range(n).sort(function(a, b) { return max[a] - max[b]; }), sums = dy.map(function(v) { return pv.sum(v); }), top = 0, bottom = 0, tops = [], bottoms = []; for (var i = 0; i < n; i++) { var j = map[i]; if (top < bottom) { top += sums[j]; tops.push(j); } else { bottom += sums[j]; bottoms.push(j); } } index = bottoms.reverse().concat(tops); break; } case "reverse": index = pv.range(n - 1, -1, -1); break; default: index = pv.range(n); break; } /* offset */ switch (s.offset) { case "silohouette": { for (var j = 0; j < m; j++) { var o = 0; for (var i = 0; i < n; i++) o += dy[i][j]; y[index[0]][j] = (h - o) / 2; } break; } case "wiggle": { var o = 0; for (var i = 0; i < n; i++) o += dy[i][0]; y[index[0]][0] = o = (h - o) / 2; for (var j = 1; j < m; j++) { var s1 = 0, s2 = 0, dx = x[j] - x[j - 1]; for (var i = 0; i < n; i++) s1 += dy[i][j]; for (var i = 0; i < n; i++) { var s3 = (dy[index[i]][j] - dy[index[i]][j - 1]) / (2 * dx); for (var k = 0; k < i; k++) { s3 += (dy[index[k]][j] - dy[index[k]][j - 1]) / dx; } s2 += s3 * dy[index[i]][j]; } y[index[0]][j] = o -= s1 ? s2 / s1 * dx : 0; } break; } case "expand": { for (var j = 0; j < m; j++) { y[index[0]][j] = 0; var k = 0; for (var i = 0; i < n; i++) k += dy[i][j]; if (k) { k = h / k; for (var i = 0; i < n; i++) dy[i][j] *= k; } else { k = h / n; for (var i = 0; i < n; i++) dy[i][j] = k; } } break; } default: { for (var j = 0; j < m; j++) y[index[0]][j] = 0; break; } } /* Propagate the offset to the other series. */ for (var j = 0; j < m; j++) { var o = y[index[0]][j]; for (var i = 1; i < n; i++) { o += dy[index[i - 1]][j]; y[index[i]][j] = o; } } /* Find the property definitions for dynamic substitution. */ var i = orient.indexOf("-"), pdy = horizontal ? "h" : "w", px = i < 0 ? (horizontal ? "l" : "b") : orient.charAt(i + 1), py = orient.charAt(0); for (var p in prop) prop[p] = none; prop[px] = function(i, j) { return x[j]; }; prop[py] = function(i, j) { return y[i][j]; }; prop[pdy] = function(i, j) { return dy[i][j]; }; }; /** * The layer prototype. This prototype is intended to be used with an area, * bar or panel mark (or subclass thereof). Other mark types may be possible, * though note that the stack layout is not currently designed to support * radial stacked visualizations using wedges. * *

    The layer is not a direct child of the stack layout; a hidden panel is * used to replicate layers. * * @type pv.Mark * @name pv.Layout.Stack.prototype.layer */ this.layer = new pv.Mark() .data(function() { return values[this.parent.index]; }) .top(proxy("t")) .left(proxy("l")) .right(proxy("r")) .bottom(proxy("b")) .width(proxy("w")) .height(proxy("h")); this.layer.add = function(type) { return that.add(pv.Panel) .data(function() { return that.layers(); }) .add(type) .extend(this); }; }; pv.Layout.Stack.prototype = pv.extend(pv.Layout) .property("orient", String) .property("offset", String) .property("order", String) .property("layers"); /** * Default properties for stack layouts. The default orientation is * "bottom-left", the default offset is "zero", and the default layers is * [[]]. * * @type pv.Layout.Stack */ pv.Layout.Stack.prototype.defaults = new pv.Layout.Stack() .extend(pv.Layout.prototype.defaults) .orient("bottom-left") .offset("zero") .layers([[]]); /** @private */ pv.Layout.Stack.prototype.$x = /** @private */ pv.Layout.Stack.prototype.$y = function() { return 0; }; /** * The x psuedo-property; determines the position of the value within the layer. * This typically corresponds to the independent variable. For example, with the * default "bottom-left" orientation, this function defines the "left" property. * * @param {function} f the x function. * @returns {pv.Layout.Stack} this. */ pv.Layout.Stack.prototype.x = function(f) { /** @private */ this.$x = pv.functor(f); return this; }; /** * The y psuedo-property; determines the thickness of the layer at the given * value. This typically corresponds to the dependent variable. For example, * with the default "bottom-left" orientation, this function defines the * "height" property. * * @param {function} f the y function. * @returns {pv.Layout.Stack} this. */ pv.Layout.Stack.prototype.y = function(f) { /** @private */ this.$y = pv.functor(f); return this; }; /** @private The default value function; identity. */ pv.Layout.Stack.prototype.$values = pv.identity; /** * The values function; determines the values for a given layer. The default * value is the identity function, which assumes that the layers property is * specified as a two-dimensional (i.e., nested) array. * * @param {function} f the values function. * @returns {pv.Layout.Stack} this. */ pv.Layout.Stack.prototype.values = function(f) { this.$values = pv.functor(f); return this; }; /** * The layer data in row-major order. The value of this property is typically a * two-dimensional (i.e., nested) array, but any array can be used, provided the * values psuedo-property is defined accordingly. * * @type array[] * @name pv.Layout.Stack.prototype.layers */ /** * The layer orientation. The following values are supported:

      * *
    • bottom-left == bottom *
    • bottom-right *
    • top-left == top *
    • top-right *
    • left-top *
    • left-bottom == left *
    • right-top *
    • right-bottom == right * *
    . The default value is "bottom-left", which means that the layers will * be built from the bottom-up, and the values within layers will be laid out * from left-to-right. * *

    Note that with non-zero baselines, some orientations may give similar * results. For example, offset("silohouette") centers the layers, resulting in * a streamgraph. Thus, the orientations "bottom-left" and "top-left" will * produce similar results, differing only in the layer order. * * @type string * @name pv.Layout.Stack.prototype.orient */ /** * The layer order. The following values are supported:

      * *
    • null - use given layer order. *
    • inside-out - sort by maximum value, with balanced order. *
    • reverse - use reverse of given layer order. * *
    For details on the inside-out order algorithm, refer to "Stacked Graphs * -- Geometry & Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG * November/December 2008. * * @type string * @name pv.Layout.Stack.prototype.order */ /** * The layer offset; the y-position of the bottom of the lowest layer. The * following values are supported:
      * *
    • zero - use a zero baseline, i.e., the y-axis. *
    • silohouette - center the stream, i.e., ThemeRiver. *
    • wiggle - minimize weighted change in slope. *
    • expand - expand layers to fill the enclosing layout dimensions. * *
    For details on these offset algorithms, refer to "Stacked Graphs -- * Geometry & Aesthetics" by L. Byron and M. Wattenberg, IEEE TVCG * November/December 2008. * * @type string * @name pv.Layout.Stack.prototype.offset */ /** * Constructs a new, empty treemap layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a space-filling rectangular layout, with the hierarchy * represented via containment. Treemaps represent nodes as boxes, with child * nodes placed within parent boxes. The size of each box is proportional to the * size of the node in the tree. This particular algorithm is taken from Bruls, * D.M., C. Huizing, and J.J. van Wijk, "Squarified Treemaps" in * Data Visualization 2000, Proceedings of the Joint Eurographics and IEEE * TCVG Sumposium on Visualization, 2000, pp. 33-42. * *

    The meaning of the exported mark prototypes changes slightly in the * space-filling implementation:

      * *
    • node - for rendering nodes; typically a {@link pv.Bar}. The node * data is populated with dx and dy attributes, in addition to * the standard x and y position attributes. * *

    • leaf - for rendering leaf nodes only, with no fill or stroke * style by default; typically a {@link pv.Panel} or another layout! * *

    • link - unsupported; undefined. Links are encoded implicitly * in the arrangement of the space-filling nodes. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. * *
    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @extends pv.Layout.Hierarchy */ pv.Layout.Treemap = function() { pv.Layout.Hierarchy.call(this); this.node .strokeStyle("#fff") .fillStyle("rgba(31, 119, 180, .25)") .width(function(n) { return n.dx; }) .height(function(n) { return n.dy; }); this.label .visible(function(n) { return !n.firstChild; }) .left(function(n) { return n.x + (n.dx / 2); }) .top(function(n) { return n.y + (n.dy / 2); }) .textAlign("center") .textAngle(function(n) { return n.dx > n.dy ? 0 : -Math.PI / 2; }); (this.leaf = new pv.Mark() .extend(this.node) .fillStyle(null) .strokeStyle(null) .visible(function(n) { return !n.firstChild; })).parent = this; /* Hide unsupported link. */ delete this.link; }; pv.Layout.Treemap.prototype = pv.extend(pv.Layout.Hierarchy) .property("round", Boolean) .property("paddingLeft", Number) .property("paddingRight", Number) .property("paddingTop", Number) .property("paddingBottom", Number) .property("mode", String) .property("order", String); /** * Default propertiess for treemap layouts. The default mode is "squarify" and * the default order is "ascending". * * @type pv.Layout.Treemap */ pv.Layout.Treemap.prototype.defaults = new pv.Layout.Treemap() .extend(pv.Layout.Hierarchy.prototype.defaults) .mode("squarify") // squarify, slice-and-dice, slice, dice .order("ascending"); // ascending, descending, reverse, null /** * Whether node sizes should be rounded to integer values. This has a similar * effect to setting antialias(false) for node values, but allows the * treemap algorithm to accumulate error related to pixel rounding. * * @type boolean * @name pv.Layout.Treemap.prototype.round */ /** * The left inset between parent add child in pixels. Defaults to 0. * * @type number * @name pv.Layout.Treemap.prototype.paddingLeft * @see #padding */ /** * The right inset between parent add child in pixels. Defaults to 0. * * @type number * @name pv.Layout.Treemap.prototype.paddingRight * @see #padding */ /** * The top inset between parent and child in pixels. Defaults to 0. * * @type number * @name pv.Layout.Treemap.prototype.paddingTop * @see #padding */ /** * The bottom inset between parent and child in pixels. Defaults to 0. * * @type number * @name pv.Layout.Treemap.prototype.paddingBottom * @see #padding */ /** * The treemap algorithm. The default value is "squarify". The "slice-and-dice" * algorithm may also be used, which alternates between horizontal and vertical * slices for different depths. In addition, the "slice" and "dice" algorithms * may be specified explicitly to control whether horizontal or vertical slices * are used, which may be useful for nested treemap layouts. * * @type string * @name pv.Layout.Treemap.prototype.mode * @see "Ordered Treemap Layouts" by B. Shneiderman & M. Wattenberg, IEEE * InfoVis 2001. */ /** * The sibling node order. A null value means to use the sibling order * specified by the nodes property as-is; "reverse" will reverse the given * order. The default value "ascending" will sort siblings in ascending order of * size, while "descending" will do the reverse. For sorting based on data * attributes other than size, use the default null for the order * property, and sort the nodes beforehand using the {@link pv.Dom} operator. * * @type string * @name pv.Layout.Treemap.prototype.order */ /** * Alias for setting the left, right, top and bottom padding properties * simultaneously. * * @see #paddingLeft * @see #paddingRight * @see #paddingTop * @see #paddingBottom * @returns {pv.Layout.Treemap} this. */ pv.Layout.Treemap.prototype.padding = function(n) { return this.paddingLeft(n).paddingRight(n).paddingTop(n).paddingBottom(n); }; /** @private The default size function. */ pv.Layout.Treemap.prototype.$size = function(d) { return Number(d.nodeValue); }; /** * Specifies the sizing function. By default, the size function uses the * nodeValue attribute of nodes as a numeric value: function(d) * Number(d.nodeValue). * *

    The sizing function is invoked for each leaf node in the tree, per the * nodes property. For example, if the tree data structure represents a * file system, with files as leaf nodes, and each file has a bytes * attribute, you can specify a size function as: * *

        .size(function(d) d.bytes)
    * * @param {function} f the new sizing function. * @returns {pv.Layout.Treemap} this. */ pv.Layout.Treemap.prototype.size = function(f) { this.$size = pv.functor(f); return this; }; /** @private */ pv.Layout.Treemap.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var that = this, nodes = s.nodes, root = nodes[0], stack = pv.Mark.stack, left = s.paddingLeft, right = s.paddingRight, top = s.paddingTop, bottom = s.paddingBottom, /** @ignore */ size = function(n) { return n.size; }, round = s.round ? Math.round : Number, mode = s.mode; /** @private */ function slice(row, sum, horizontal, x, y, w, h) { for (var i = 0, d = 0; i < row.length; i++) { var n = row[i]; if (horizontal) { n.x = x + d; n.y = y; d += n.dx = round(w * n.size / sum); n.dy = h; } else { n.x = x; n.y = y + d; n.dx = w; d += n.dy = round(h * n.size / sum); } } if (n) { // correct on-axis rounding error if (horizontal) { n.dx += w - d; } else { n.dy += h - d; } } } /** @private */ function ratio(row, l) { var rmax = -Infinity, rmin = Infinity, s = 0; for (var i = 0; i < row.length; i++) { var r = row[i].size; if (r < rmin) rmin = r; if (r > rmax) rmax = r; s += r; } s = s * s; l = l * l; return Math.max(l * rmax / s, s / (l * rmin)); } /** @private */ function layout(n, i) { var x = n.x + left, y = n.y + top, w = n.dx - left - right, h = n.dy - top - bottom; /* Assume squarify by default. */ if (mode != "squarify") { slice(n.childNodes, n.size, mode == "slice" ? true : mode == "dice" ? false : i & 1, x, y, w, h); return; } var row = [], mink = Infinity, l = Math.min(w, h), k = w * h / n.size; /* Abort if the size is nonpositive. */ if (n.size <= 0) return; /* Scale the sizes to fill the current subregion. */ n.visitBefore(function(n) { n.size *= k; }); /** @private Position the specified nodes along one dimension. */ function position(row) { var horizontal = w == l, sum = pv.sum(row, size), r = l ? round(sum / l) : 0; slice(row, sum, horizontal, x, y, horizontal ? w : r, horizontal ? r : h); if (horizontal) { y += r; h -= r; } else { x += r; w -= r; } l = Math.min(w, h); return horizontal; } var children = n.childNodes.slice(); // copy while (children.length) { var child = children[children.length - 1]; if (!child.size) { children.pop(); continue; } row.push(child); var k = ratio(row, l); if (k <= mink) { children.pop(); mink = k; } else { row.pop(); position(row); row.length = 0; mink = Infinity; } } /* correct off-axis rounding error */ if (position(row)) for (var i = 0; i < row.length; i++) { row[i].dy += h; } else for (var i = 0; i < row.length; i++) { row[i].dx += w; } } /* Recursively compute the node depth and size. */ stack.unshift(null); root.visitAfter(function(n, i) { n.depth = i; n.x = n.y = n.dx = n.dy = 0; n.size = n.firstChild ? pv.sum(n.childNodes, function(n) { return n.size; }) : that.$size.apply(that, (stack[0] = n, stack)); }); stack.shift(); /* Sort. */ switch (s.order) { case "ascending": { root.sort(function(a, b) { return a.size - b.size; }); break; } case "descending": { root.sort(function(a, b) { return b.size - a.size; }); break; } case "reverse": root.reverse(); break; } /* Recursively compute the layout. */ root.x = 0; root.y = 0; root.dx = s.width; root.dy = s.height; root.visitBefore(layout); }; /** * Constructs a new, empty tree layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a node-link tree diagram using the Reingold-Tilford "tidy" * tree layout algorithm. The specific algorithm used by this layout is based on * "Improving * Walker's Algorithm to Run in Linear Time" by C. Buchheim, M. Jünger * & S. Leipert, Graph Drawing 2002. This layout supports both cartesian and * radial orientations orientations for node-link diagrams. * *

    The tree layout supports a "group" property, which if true causes siblings * to be positioned closer together than unrelated nodes at the same depth. The * layout can be configured using the depth and breadth * properties, which control the increments in pixel space between nodes in both * dimensions, similar to the indent layout. * *

    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @extends pv.Layout.Hierarchy */ pv.Layout.Tree = function() { pv.Layout.Hierarchy.call(this); }; pv.Layout.Tree.prototype = pv.extend(pv.Layout.Hierarchy) .property("group", Number) .property("breadth", Number) .property("depth", Number) .property("orient", String); /** * Default properties for tree layouts. The default orientation is "top", the * default group parameter is 1, and the default breadth and depth offsets are * 15 and 60 respectively. * * @type pv.Layout.Tree */ pv.Layout.Tree.prototype.defaults = new pv.Layout.Tree() .extend(pv.Layout.Hierarchy.prototype.defaults) .group(1) .breadth(15) .depth(60) .orient("top"); /** @private */ pv.Layout.Tree.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var nodes = s.nodes, orient = s.orient, depth = s.depth, breadth = s.breadth, group = s.group, w = s.width, h = s.height; /** @private */ function firstWalk(v) { var l, r, a; if (!v.firstChild) { if (l = v.previousSibling) { v.prelim = l.prelim + distance(v.depth, true); } } else { l = v.firstChild; r = v.lastChild; a = l; // default ancestor for (var c = l; c; c = c.nextSibling) { firstWalk(c); a = apportion(c, a); } executeShifts(v); var midpoint = .5 * (l.prelim + r.prelim); if (l = v.previousSibling) { v.prelim = l.prelim + distance(v.depth, true); v.mod = v.prelim - midpoint; } else { v.prelim = midpoint; } } } /** @private */ function secondWalk(v, m, depth) { v.breadth = v.prelim + m; m += v.mod; for (var c = v.firstChild; c; c = c.nextSibling) { secondWalk(c, m, depth); } } /** @private */ function apportion(v, a) { var w = v.previousSibling; if (w) { var vip = v, vop = v, vim = w, vom = v.parentNode.firstChild, sip = vip.mod, sop = vop.mod, sim = vim.mod, som = vom.mod, nr = nextRight(vim), nl = nextLeft(vip); while (nr && nl) { vim = nr; vip = nl; vom = nextLeft(vom); vop = nextRight(vop); vop.ancestor = v; var shift = (vim.prelim + sim) - (vip.prelim + sip) + distance(vim.depth, false); if (shift > 0) { moveSubtree(ancestor(vim, v, a), v, shift); sip += shift; sop += shift; } sim += vim.mod; sip += vip.mod; som += vom.mod; sop += vop.mod; nr = nextRight(vim); nl = nextLeft(vip); } if (nr && !nextRight(vop)) { vop.thread = nr; vop.mod += sim - sop; } if (nl && !nextLeft(vom)) { vom.thread = nl; vom.mod += sip - som; a = v; } } return a; } /** @private */ function nextLeft(v) { return v.firstChild || v.thread; } /** @private */ function nextRight(v) { return v.lastChild || v.thread; } /** @private */ function moveSubtree(wm, wp, shift) { var subtrees = wp.number - wm.number; wp.change -= shift / subtrees; wp.shift += shift; wm.change += shift / subtrees; wp.prelim += shift; wp.mod += shift; } /** @private */ function executeShifts(v) { var shift = 0, change = 0; for (var c = v.lastChild; c; c = c.previousSibling) { c.prelim += shift; c.mod += shift; change += c.change; shift += c.shift + change; } } /** @private */ function ancestor(vim, v, a) { return (vim.ancestor.parentNode == v.parentNode) ? vim.ancestor : a; } /** @private */ function distance(depth, siblings) { return (siblings ? 1 : (group + 1)) / ((orient == "radial") ? depth : 1); } /* Initialize temporary layout variables. TODO: store separately. */ var root = nodes[0]; root.visitAfter(function(v, i) { v.ancestor = v; v.prelim = 0; v.mod = 0; v.change = 0; v.shift = 0; v.number = v.previousSibling ? (v.previousSibling.number + 1) : 0; v.depth = i; }); /* Compute the layout using Buchheim et al.'s algorithm. */ firstWalk(root); secondWalk(root, -root.prelim, 0); /** @private Returns the angle of the given node. */ function midAngle(n) { return (orient == "radial") ? n.breadth / depth : 0; } /** @private */ function x(n) { switch (orient) { case "left": return n.depth; case "right": return w - n.depth; case "top": case "bottom": return n.breadth + w / 2; case "radial": return w / 2 + n.depth * Math.cos(midAngle(n)); } } /** @private */ function y(n) { switch (orient) { case "left": case "right": return n.breadth + h / 2; case "top": return n.depth; case "bottom": return h - n.depth; case "radial": return h / 2 + n.depth * Math.sin(midAngle(n)); } } /* Clear temporary layout variables; transform depth and breadth. */ root.visitAfter(function(v) { v.breadth *= breadth; v.depth *= depth; v.midAngle = midAngle(v); v.x = x(v); v.y = y(v); if (v.firstChild) v.midAngle += Math.PI; delete v.breadth; delete v.depth; delete v.ancestor; delete v.prelim; delete v.mod; delete v.change; delete v.shift; delete v.number; delete v.thread; }); }; /** * The offset between siblings nodes; defaults to 15. * * @type number * @name pv.Layout.Tree.prototype.breadth */ /** * The offset between parent and child nodes; defaults to 60. * * @type number * @name pv.Layout.Tree.prototype.depth */ /** * The orientation. The default orientation is "top", which means that the root * node is placed on the top edge, leaf nodes appear at the bottom, and internal * nodes are in-between. The following orientations are supported:

      * *
    • left - left-to-right. *
    • right - right-to-left. *
    • top - top-to-bottom. *
    • bottom - bottom-to-top. *
    • radial - radially, with the root at the center.
    * * @type string * @name pv.Layout.Tree.prototype.orient */ /** * The sibling grouping, i.e., whether differentiating space is placed between * sibling groups. The default is 1 (or true), causing sibling leaves to be * separated by one breadth offset. Setting this to false (or 0) causes * non-siblings to be adjacent. * * @type number * @name pv.Layout.Tree.prototype.group */ /** * Constructs a new, empty indent layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a hierarchical layout using the indent algorithm. This * layout implements a node-link diagram where the nodes are presented in * preorder traversal, and nodes are indented based on their depth from the * root. This technique is used ubiquitously by operating systems to represent * file directories; although it requires much vertical space, indented trees * allow efficient interactive exploration of trees to find a specific * node. In addition they allow rapid scanning of node labels, and multivariate * data such as file sizes can be displayed adjacent to the hierarchy. * *

    The indent layout can be configured using the depth and * breadth properties, which control the increments in pixel space for * each indent and row in the layout. This layout does not support multiple * orientations; the root node is rendered in the top-left, while * breadth is a vertical offset from the top, and depth is a * horizontal offset from the left. * *

    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @extends pv.Layout.Hierarchy */ pv.Layout.Indent = function() { pv.Layout.Hierarchy.call(this); this.link.interpolate("step-after"); }; pv.Layout.Indent.prototype = pv.extend(pv.Layout.Hierarchy) .property("depth", Number) .property("breadth", Number); /** * The horizontal offset between different levels of the tree; defaults to 15. * * @type number * @name pv.Layout.Indent.prototype.depth */ /** * The vertical offset between nodes; defaults to 15. * * @type number * @name pv.Layout.Indent.prototype.breadth */ /** * Default properties for indent layouts. By default the depth and breadth * offsets are 15 pixels. * * @type pv.Layout.Indent */ pv.Layout.Indent.prototype.defaults = new pv.Layout.Indent() .extend(pv.Layout.Hierarchy.prototype.defaults) .depth(15) .breadth(15); /** @private */ pv.Layout.Indent.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var nodes = s.nodes, bspace = s.breadth, dspace = s.depth, ax = 0, ay = 0; /** @private */ function position(n, breadth, depth) { n.x = ax + depth++ * dspace; n.y = ay + breadth++ * bspace; n.midAngle = 0; for (var c = n.firstChild; c; c = c.nextSibling) { breadth = position(c, breadth, depth); } return breadth; } position(nodes[0], 1, 1); }; /** * Constructs a new, empty circle-packing layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a hierarchical layout using circle-packing. The meaning of * the exported mark prototypes changes slightly in the space-filling * implementation:

      * *
    • node - for rendering nodes; typically a {@link pv.Dot}. * *

    • link - unsupported; undefined. Links are encoded implicitly * in the arrangement of the space-filling nodes. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. * *
    The pack layout support dynamic sizing for leaf nodes, if a * {@link #size} psuedo-property is specified. The default size function returns * 1, causing all leaf nodes to be sized equally, and all internal nodes to be * sized by the number of leaf nodes they have as descendants. * *

    The size function can be used in conjunction with the order property, * which allows the nodes to the sorted by the computed size. Note: for sorting * based on other data attributes, simply use the default null for the * order property, and sort the nodes beforehand using the {@link pv.Dom} * operator. * *

    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @extends pv.Layout.Hierarchy * @see "Visualization of large hierarchical data by circle packing" by W. Wang, * H. Wang, G. Dai, and H. Wang, ACM CHI 2006. */ pv.Layout.Pack = function() { pv.Layout.Hierarchy.call(this); this.node .radius(function(n) { return n.radius; }) .strokeStyle("rgb(31, 119, 180)") .fillStyle("rgba(31, 119, 180, .25)"); this.label .textAlign("center"); /* Hide unsupported link. */ delete this.link; }; pv.Layout.Pack.prototype = pv.extend(pv.Layout.Hierarchy) .property("spacing", Number) .property("order", String); // ascending, descending, reverse, null /** * Default properties for circle-packing layouts. The default spacing parameter * is 1 and the default order is "ascending". * * @type pv.Layout.Pack */ pv.Layout.Pack.prototype.defaults = new pv.Layout.Pack() .extend(pv.Layout.Hierarchy.prototype.defaults) .spacing(1) .order("ascending"); /** * The spacing parameter; defaults to 1, which provides a little bit of padding * between sibling nodes and the enclosing circle. Larger values increase the * spacing, by making the sibling nodes smaller; a value of zero makes the leaf * nodes as large as possible, with no padding on enclosing circles. * * @type number * @name pv.Layout.Pack.prototype.spacing */ /** * The sibling node order. The default order is null, which means to * use the sibling order specified by the nodes property as-is. A value of * "ascending" will sort siblings in ascending order of size, while "descending" * will do the reverse. For sorting based on data attributes other than size, * use the default null for the order property, and sort the nodes * beforehand using the {@link pv.Dom} operator. * * @see pv.Dom.Node#sort * @type string * @name pv.Layout.Pack.prototype.order */ /** @private The default size function. */ pv.Layout.Pack.prototype.$radius = function() { return 1; }; // TODO is it possible for spacing to operate in pixel space? // Right now it appears to be multiples of the smallest radius. /** * Specifies the sizing function. By default, a sizing function is disabled and * all nodes are given constant size. The sizing function is invoked for each * leaf node in the tree (passed to the constructor). * *

    For example, if the tree data structure represents a file system, with * files as leaf nodes, and each file has a bytes attribute, you can * specify a size function as: * *

        .size(function(d) d.bytes)
    * * As with other properties, a size function may specify additional arguments to * access the data associated with the layout and any enclosing panels. * * @param {function} f the new sizing function. * @returns {pv.Layout.Pack} this. */ pv.Layout.Pack.prototype.size = function(f) { this.$radius = typeof f == "function" ? function() { return Math.sqrt(f.apply(this, arguments)); } : (f = Math.sqrt(f), function() { return f; }); return this; }; /** @private */ pv.Layout.Pack.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var that = this, nodes = s.nodes, root = nodes[0]; /** @private Compute the radii of the leaf nodes. */ function radii(nodes) { var stack = pv.Mark.stack; stack.unshift(null); for (var i = 0, n = nodes.length; i < n; i++) { var c = nodes[i]; if (!c.firstChild) { c.radius = that.$radius.apply(that, (stack[0] = c, stack)); } } stack.shift(); } /** @private */ function packTree(n) { var nodes = []; for (var c = n.firstChild; c; c = c.nextSibling) { if (c.firstChild) c.radius = packTree(c); c.n = c.p = c; nodes.push(c); } /* Sort. */ switch (s.order) { case "ascending": { nodes.sort(function(a, b) { return a.radius - b.radius; }); break; } case "descending": { nodes.sort(function(a, b) { return b.radius - a.radius; }); break; } case "reverse": nodes.reverse(); break; } return packCircle(nodes); } /** @private */ function packCircle(nodes) { var xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, j, k; /** @private */ function bound(n) { xMin = Math.min(n.x - n.radius, xMin); xMax = Math.max(n.x + n.radius, xMax); yMin = Math.min(n.y - n.radius, yMin); yMax = Math.max(n.y + n.radius, yMax); } /** @private */ function insert(a, b) { var c = a.n; a.n = b; b.p = a; b.n = c; c.p = b; } /** @private */ function splice(a, b) { a.n = b; b.p = a; } /** @private */ function intersects(a, b) { var dx = b.x - a.x, dy = b.y - a.y, dr = a.radius + b.radius; return (dr * dr - dx * dx - dy * dy) > .001; // within epsilon } /* Create first node. */ a = nodes[0]; a.x = -a.radius; a.y = 0; bound(a); /* Create second node. */ if (nodes.length > 1) { b = nodes[1]; b.x = b.radius; b.y = 0; bound(b); /* Create third node and build chain. */ if (nodes.length > 2) { c = nodes[2]; place(a, b, c); bound(c); insert(a, c); a.p = c; insert(c, b); b = a.n; /* Now iterate through the rest. */ for (var i = 3; i < nodes.length; i++) { place(a, b, c = nodes[i]); /* Search for the closest intersection. */ var isect = 0, s1 = 1, s2 = 1; for (j = b.n; j != b; j = j.n, s1++) { if (intersects(j, c)) { isect = 1; break; } } if (isect == 1) { for (k = a.p; k != j.p; k = k.p, s2++) { if (intersects(k, c)) { if (s2 < s1) { isect = -1; j = k; } break; } } } /* Update node chain. */ if (isect == 0) { insert(a, c); b = c; bound(c); } else if (isect > 0) { splice(a, j); b = j; i--; } else if (isect < 0) { splice(j, b); a = j; i--; } } } } /* Re-center the circles and return the encompassing radius. */ var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; for (var i = 0; i < nodes.length; i++) { var n = nodes[i]; n.x -= cx; n.y -= cy; cr = Math.max(cr, n.radius + Math.sqrt(n.x * n.x + n.y * n.y)); } return cr + s.spacing; } /** @private */ function place(a, b, c) { var da = b.radius + c.radius, db = a.radius + c.radius, dx = b.x - a.x, dy = b.y - a.y, dc = Math.sqrt(dx * dx + dy * dy), cos = (db * db + dc * dc - da * da) / (2 * db * dc), theta = Math.acos(cos), x = cos * db, h = Math.sin(theta) * db; dx /= dc; dy /= dc; c.x = a.x + x * dx + h * dy; c.y = a.y + x * dy - h * dx; } /** @private */ function transform(n, x, y, k) { for (var c = n.firstChild; c; c = c.nextSibling) { c.x += n.x; c.y += n.y; transform(c, x, y, k); } n.x = x + k * n.x; n.y = y + k * n.y; n.radius *= k; } radii(nodes); /* Recursively compute the layout. */ root.x = 0; root.y = 0; root.radius = packTree(root); var w = this.width(), h = this.height(), k = 1 / Math.max(2 * root.radius / w, 2 * root.radius / h); transform(root, w / 2, h / 2, k); }; /** * Constructs a new, empty force-directed layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements force-directed network layout as a node-link diagram. This * layout uses the Fruchterman-Reingold algorithm, which applies an attractive * spring force between neighboring nodes, and a repulsive electrical charge * force between all nodes. An additional drag force improves stability of the * simulation. See {@link pv.Force.spring}, {@link pv.Force.drag} and {@link * pv.Force.charge} for more details; note that the n-body charge force is * approximated using the Barnes-Hut algorithm. * *

    This layout is implemented on top of {@link pv.Simulation}, which can be * used directly for more control over simulation parameters. The simulation * uses Position Verlet integration, which does not compute velocities * explicitly, but allows for easy geometric constraints, such as bounding the * nodes within the layout panel. Many of the configuration properties supported * by this layout are simply passed through to the underlying forces and * constraints of the simulation. * *

    Force layouts are typically interactive. The gradual movement of the nodes * as they stabilize to a local stress minimum can help reveal the structure of * the network, as can {@link pv.Behavior.drag}, which allows the user to pick * up nodes and reposition them while the physics simulation continues. This * layout can also be used with pan & zoom behaviors for interaction. * *

    To facilitate interaction, this layout by default automatically re-renders * using a setInterval every 42 milliseconds. This can be disabled via * the iterations property, which if non-null specifies the number of * simulation iterations to run before the force-directed layout is finalized. * Be careful not to use too high an iteration count, as this can lead to an * annoying delay on page load. * *

    As with other network layouts, the network data can be updated * dynamically, provided the property cache is reset. See * {@link pv.Layout.Network} for details. New nodes are initialized with random * positions near the center. Alternatively, positions can be specified manually * by setting the x and y attributes on nodes. * * @extends pv.Layout.Network * @see "Graph Drawing by Force-directed Placement" by T. Fruchterman & * E. Reingold, Software--Practice & Experience, November 1991. */ pv.Layout.Force = function() { pv.Layout.Network.call(this); /* Force-directed graphs can be messy, so reduce the link width. */ this.link.lineWidth(function(d, p) { return Math.sqrt(p.linkValue) * 1.5; }); this.label.textAlign("center"); }; pv.Layout.Force.prototype = pv.extend(pv.Layout.Network) .property("bound", Boolean) .property("iterations", Number) .property("dragConstant", Number) .property("chargeConstant", Number) .property("chargeMinDistance", Number) .property("chargeMaxDistance", Number) .property("chargeTheta", Number) .property("springConstant", Number) .property("springDamping", Number) .property("springLength", Number); /** * The bound parameter; true if nodes should be constrained within the layout * panel. Bounding is disabled by default. Currently the layout does not observe * the radius of the nodes; strictly speaking, only the center of the node is * constrained to be within the panel, with an additional 6-pixel offset for * padding. A future enhancement could extend the bound constraint to observe * the node's radius, which would also support bounding for variable-size nodes. * *

    Note that if this layout is used in conjunction with pan & zoom * behaviors, those behaviors should have their bound parameter set to the same * value. * * @type boolean * @name pv.Layout.Force.prototype.bound */ /** * The number of simulation iterations to run, or null if this layout is * interactive. Force-directed layouts are interactive by default, using a * setInterval to advance the physics simulation and re-render * automatically. * * @type number * @name pv.Layout.Force.prototype.iterations */ /** * The drag constant, in the range [0,1]. A value of 0 means no drag (a * perfectly frictionless environment), while a value of 1 means friction * immediately cancels all momentum. The default value is 0.1, which provides a * minimum amount of drag that helps stabilize bouncy springs; lower values may * result in excessive bounciness, while higher values cause the simulation to * take longer to converge. * * @type number * @name pv.Layout.Force.prototype.dragConstant * @see pv.Force.drag#constant */ /** * The charge constant, which should be a negative number. The default value is * -40; more negative values will result in a stronger repulsive force, which * may lead to faster convergence at the risk of instability. Too strong * repulsive charge forces can cause comparatively weak springs to be stretched * well beyond their rest length, emphasizing global structure over local * structure. A nonnegative value will break the Fruchterman-Reingold algorithm, * and is for entertainment purposes only. * * @type number * @name pv.Layout.Force.prototype.chargeConstant * @see pv.Force.charge#constant */ /** * The minimum distance at which charge forces are applied. The default minimum * distance of 2 avoids applying forces that are two strong; because the physics * simulation is run at discrete time intervals, it is possible for two same- * charged particles to become very close or even a singularity! Since the * charge force is inversely proportional to the square of the distance, very * small distances can break the simulation. * *

    In rare cases, two particles can become stuck on top of each other, as a * minimum distance threshold will prevent the charge force from repelling them. * However, this occurs very rarely because other forces and momentum typically * cause the particles to become separated again, at which point the repulsive * charge force kicks in. * * @type number * @name pv.Layout.Force.prototype.chargeMinDistance * @see pv.Force.charge#domain */ /** * The maximum distance at which charge forces are applied. This improves * performance by ignoring weak charge forces at great distances. Note that this * parameter is partly redundant, as the Barnes-Hut algorithm for n-body forces * already improves performance for far-away particles through approximation. * * @type number * @name pv.Layout.Force.prototype.chargeMaxDistance * @see pv.Force.charge#domain */ /** * The Barnes-Hut approximation factor. The Barnes-Hut approximation criterion * is the ratio of the size of the quadtree node to the distance from the point * to the node's center of mass is beneath some threshold. The default value is * 0.9. * * @type number * @name pv.Layout.Force.prototype.chargeTheta * @see pv.Force.charge#theta */ /** * The spring constant, which should be a positive number. The default value is * 0.1; greater values will result in a stronger attractive force, which may * lead to faster convergence at the risk of instability. Too strong spring * forces can cause comparatively weak charge forces to be ignored, emphasizing * local structure over global structure. A nonpositive value will break the * Fruchterman-Reingold algorithm, and is for entertainment purposes only. * *

    The spring tension is automatically normalized using the inverse square * root of the maximum link degree of attached nodes. * * @type number * @name pv.Layout.Force.prototype.springConstant * @see pv.Force.spring#constant */ /** * The spring damping factor, in the range [0,1]. Damping functions identically * to drag forces, damping spring bounciness by applying a force in the opposite * direction of attached nodes' velocities. The default value is 0.3. * *

    The spring damping is automatically normalized using the inverse square * root of the maximum link degree of attached nodes. * * @type number * @name pv.Layout.Force.prototype.springDamping * @see pv.Force.spring#damping */ /** * The spring rest length. The default value is 20 pixels. Larger values may be * appropriate if the layout panel is larger, or if the nodes are rendered * larger than the default dot size of 20. * * @type number * @name pv.Layout.Force.prototype.springLength * @see pv.Force.spring#length */ /** * Default properties for force-directed layouts. The default drag constant is * 0.1, the default charge constant is -40 (with a domain of [2, 500] and theta * of 0.9), and the default spring constant is 0.1 (with a damping of 0.3 and a * rest length of 20). * * @type pv.Layout.Force */ pv.Layout.Force.prototype.defaults = new pv.Layout.Force() .extend(pv.Layout.Network.prototype.defaults) .dragConstant(.1) .chargeConstant(-40) .chargeMinDistance(2) .chargeMaxDistance(500) .chargeTheta(.9) .springConstant(.1) .springDamping(.3) .springLength(20); /** @private Initialize the physics simulation. */ pv.Layout.Force.prototype.buildImplied = function(s) { /* Any cached interactive layouts need to be rebound for the timer. */ if (pv.Layout.Network.prototype.buildImplied.call(this, s)) { var f = s.$force; if (f) { f.next = this.binds.$force; this.binds.$force = f; } return; } var that = this, nodes = s.nodes, links = s.links, k = s.iterations, w = s.width, h = s.height; /* Initialize positions randomly near the center. */ for (var i = 0, n; i < nodes.length; i++) { n = nodes[i]; if (isNaN(n.x)) n.x = w / 2 + 40 * Math.random() - 20; if (isNaN(n.y)) n.y = h / 2 + 40 * Math.random() - 20; } /* Initialize the simulation. */ var sim = pv.simulation(nodes); /* Drag force. */ sim.force(pv.Force.drag(s.dragConstant)); /* Charge (repelling) force. */ sim.force(pv.Force.charge(s.chargeConstant) .domain(s.chargeMinDistance, s.chargeMaxDistance) .theta(s.chargeTheta)); /* Spring (attracting) force. */ sim.force(pv.Force.spring(s.springConstant) .damping(s.springDamping) .length(s.springLength) .links(links)); /* Position constraint (for interactive dragging). */ sim.constraint(pv.Constraint.position()); /* Optionally add bound constraint. TODO: better padding. */ if (s.bound) { sim.constraint(pv.Constraint.bound().x(6, w - 6).y(6, h - 6)); } /** @private Returns the speed of the given node, to determine cooling. */ function speed(n) { return n.fix ? 1 : n.vx * n.vx + n.vy * n.vy; } /* * If the iterations property is null (the default), the layout is * interactive. The simulation is run until the fastest particle drops below * an arbitrary minimum speed. Although the timer keeps firing, this speed * calculation is fast so there is minimal CPU overhead. Note: if a particle * is fixed for interactivity, treat this as a high speed and resume * simulation. */ if (k == null) { sim.step(); // compute initial previous velocities sim.step(); // compute initial velocities /* Add the simulation state to the bound list. */ var force = s.$force = this.binds.$force = { next: this.binds.$force, nodes: nodes, min: 1e-4 * (links.length + 1), sim: sim }; /* Start the timer, if not already started. */ if (!this.$timer) this.$timer = setInterval(function() { var render = false; for (var f = that.binds.$force; f; f = f.next) { if (pv.max(f.nodes, speed) > f.min) { f.sim.step(); render = true; } } if (render) that.render(); }, 42); } else for (var i = 0; i < k; i++) { sim.step(); } }; /** * Constructs a new, empty cluster layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a hierarchical layout using the cluster (or dendrogram) * algorithm. This layout provides both node-link and space-filling * implementations of cluster diagrams. In many ways it is similar to * {@link pv.Layout.Partition}, except that leaf nodes are positioned at maximum * depth, and the depth of internal nodes is based on their distance from their * deepest descendant, rather than their distance from the root. * *

    The cluster layout supports a "group" property, which if true causes * siblings to be positioned closer together than unrelated nodes at the same * depth. Unlike the partition layout, this layout does not support dynamic * sizing for leaf nodes; all leaf nodes are the same size. * *

    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @see pv.Layout.Cluster.Fill * @extends pv.Layout.Hierarchy */ pv.Layout.Cluster = function() { pv.Layout.Hierarchy.call(this); var interpolate, // cached interpolate buildImplied = this.buildImplied; /** @private Cache layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, s); interpolate = /^(top|bottom)$/.test(s.orient) ? "step-before" : /^(left|right)$/.test(s.orient) ? "step-after" : "linear"; }; this.link.interpolate(function() { return interpolate; }); }; pv.Layout.Cluster.prototype = pv.extend(pv.Layout.Hierarchy) .property("group", Number) .property("orient", String) .property("innerRadius", Number) .property("outerRadius", Number); /** * The group parameter; defaults to 0, disabling grouping of siblings. If this * parameter is set to a positive number (or true, which is equivalent to 1), * then additional space will be allotted between sibling groups. In other * words, siblings (nodes that share the same parent) will be positioned more * closely than nodes at the same depth that do not share a parent. * * @type number * @name pv.Layout.Cluster.prototype.group */ /** * The orientation. The default orientation is "top", which means that the root * node is placed on the top edge, leaf nodes appear on the bottom edge, and * internal nodes are in-between. The following orientations are supported:

      * *
    • left - left-to-right. *
    • right - right-to-left. *
    • top - top-to-bottom. *
    • bottom - bottom-to-top. *
    • radial - radially, with the root at the center.
    * * @type string * @name pv.Layout.Cluster.prototype.orient */ /** * The inner radius; defaults to 0. This property applies only to radial * orientations, and can be used to compress the layout radially. Note that for * the node-link implementation, the root node is always at the center, * regardless of the value of this property; this property only affects internal * and leaf nodes. For the space-filling implementation, a non-zero value of * this property will result in the root node represented as a ring rather than * a circle. * * @type number * @name pv.Layout.Cluster.prototype.innerRadius */ /** * The outer radius; defaults to fill the containing panel, based on the height * and width of the layout. If the layout has no height and width specified, it * will extend to fill the enclosing panel. * * @type number * @name pv.Layout.Cluster.prototype.outerRadius */ /** * Defaults for cluster layouts. The default group parameter is 0 and the * default orientation is "top". * * @type pv.Layout.Cluster */ pv.Layout.Cluster.prototype.defaults = new pv.Layout.Cluster() .extend(pv.Layout.Hierarchy.prototype.defaults) .group(0) .orient("top"); /** @private */ pv.Layout.Cluster.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var root = s.nodes[0], group = s.group, breadth, depth, leafCount = 0, leafIndex = .5 - group / 2; /* Count the leaf nodes and compute the depth of descendants. */ var p = undefined; root.visitAfter(function(n) { if (n.firstChild) { n.depth = 1 + pv.max(n.childNodes, function(n) { return n.depth; }); } else { if (group && (p != n.parentNode)) { p = n.parentNode; leafCount += group; } leafCount++; n.depth = 0; } }); breadth = 1 / leafCount; depth = 1 / root.depth; /* Compute the unit breadth and depth of each node. */ var p = undefined; root.visitAfter(function(n) { if (n.firstChild) { n.breadth = pv.mean(n.childNodes, function(n) { return n.breadth; }); } else { if (group && (p != n.parentNode)) { p = n.parentNode; leafIndex += group; } n.breadth = breadth * leafIndex++; } n.depth = 1 - n.depth * depth; }); /* Compute breadth and depth ranges for space-filling layouts. */ root.visitAfter(function(n) { n.minBreadth = n.firstChild ? n.firstChild.minBreadth : (n.breadth - breadth / 2); n.maxBreadth = n.firstChild ? n.lastChild.maxBreadth : (n.breadth + breadth / 2); }); root.visitBefore(function(n) { n.minDepth = n.parentNode ? n.parentNode.maxDepth : 0; n.maxDepth = n.parentNode ? (n.depth + root.depth) : (n.minDepth + 2 * root.depth); }); root.minDepth = -depth; pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s); }; /** * Constructs a new, empty space-filling cluster layout. Layouts are not * typically constructed directly; instead, they are added to an existing panel * via {@link pv.Mark#add}. * * @class A variant of cluster layout that is space-filling. The meaning of the * exported mark prototypes changes slightly in the space-filling * implementation:
      * *
    • node - for rendering nodes; typically a {@link pv.Bar} for * non-radial orientations, and a {@link pv.Wedge} for radial orientations. * *

    • link - unsupported; undefined. Links are encoded implicitly * in the arrangement of the space-filling nodes. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. * *
    For more details on how to use this layout, see * {@link pv.Layout.Cluster}. * * @extends pv.Layout.Cluster */ pv.Layout.Cluster.Fill = function() { pv.Layout.Cluster.call(this); pv.Layout.Hierarchy.Fill.constructor.call(this); }; pv.Layout.Cluster.Fill.prototype = pv.extend(pv.Layout.Cluster); /** @private */ pv.Layout.Cluster.Fill.prototype.buildImplied = function(s) { if (pv.Layout.Cluster.prototype.buildImplied.call(this, s)) return; pv.Layout.Hierarchy.Fill.buildImplied.call(this, s); }; /** * Constructs a new, empty partition layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implemeents a hierarchical layout using the partition (or sunburst, * icicle) algorithm. This layout provides both node-link and space-filling * implementations of partition diagrams. In many ways it is similar to * {@link pv.Layout.Cluster}, except that leaf nodes are positioned based on * their distance from the root. * *

    The partition layout support dynamic sizing for leaf nodes, if a * {@link #size} psuedo-property is specified. The default size function returns * 1, causing all leaf nodes to be sized equally, and all internal nodes to be * sized by the number of leaf nodes they have as descendants. * *

    The size function can be used in conjunction with the order property, * which allows the nodes to the sorted by the computed size. Note: for sorting * based on other data attributes, simply use the default null for the * order property, and sort the nodes beforehand using the {@link pv.Dom} * operator. * *

    For more details on how to use this layout, see * {@link pv.Layout.Hierarchy}. * * @see pv.Layout.Partition.Fill * @extends pv.Layout.Hierarchy */ pv.Layout.Partition = function() { pv.Layout.Hierarchy.call(this); }; pv.Layout.Partition.prototype = pv.extend(pv.Layout.Hierarchy) .property("order", String) // null, ascending, descending? .property("orient", String) // top, left, right, bottom, radial .property("innerRadius", Number) .property("outerRadius", Number); /** * The sibling node order. The default order is null, which means to * use the sibling order specified by the nodes property as-is. A value of * "ascending" will sort siblings in ascending order of size, while "descending" * will do the reverse. For sorting based on data attributes other than size, * use the default null for the order property, and sort the nodes * beforehand using the {@link pv.Dom} operator. * * @see pv.Dom.Node#sort * @type string * @name pv.Layout.Partition.prototype.order */ /** * The orientation. The default orientation is "top", which means that the root * node is placed on the top edge, leaf nodes appear at the bottom, and internal * nodes are in-between. The following orientations are supported:

      * *
    • left - left-to-right. *
    • right - right-to-left. *
    • top - top-to-bottom. *
    • bottom - bottom-to-top. *
    • radial - radially, with the root at the center.
    * * @type string * @name pv.Layout.Partition.prototype.orient */ /** * The inner radius; defaults to 0. This property applies only to radial * orientations, and can be used to compress the layout radially. Note that for * the node-link implementation, the root node is always at the center, * regardless of the value of this property; this property only affects internal * and leaf nodes. For the space-filling implementation, a non-zero value of * this property will result in the root node represented as a ring rather than * a circle. * * @type number * @name pv.Layout.Partition.prototype.innerRadius */ /** * The outer radius; defaults to fill the containing panel, based on the height * and width of the layout. If the layout has no height and width specified, it * will extend to fill the enclosing panel. * * @type number * @name pv.Layout.Partition.prototype.outerRadius */ /** * Default properties for partition layouts. The default orientation is "top". * * @type pv.Layout.Partition */ pv.Layout.Partition.prototype.defaults = new pv.Layout.Partition() .extend(pv.Layout.Hierarchy.prototype.defaults) .orient("top"); /** @private */ pv.Layout.Partition.prototype.$size = function() { return 1; }; /** * Specifies the sizing function. By default, a sizing function is disabled and * all nodes are given constant size. The sizing function is invoked for each * leaf node in the tree (passed to the constructor). * *

    For example, if the tree data structure represents a file system, with * files as leaf nodes, and each file has a bytes attribute, you can * specify a size function as: * *

        .size(function(d) d.bytes)
    * * As with other properties, a size function may specify additional arguments to * access the data associated with the layout and any enclosing panels. * * @param {function} f the new sizing function. * @returns {pv.Layout.Partition} this. */ pv.Layout.Partition.prototype.size = function(f) { this.$size = f; return this; }; /** @private */ pv.Layout.Partition.prototype.buildImplied = function(s) { if (pv.Layout.Hierarchy.prototype.buildImplied.call(this, s)) return; var that = this, root = s.nodes[0], stack = pv.Mark.stack, maxDepth = 0; /* Recursively compute the tree depth and node size. */ stack.unshift(null); root.visitAfter(function(n, i) { if (i > maxDepth) maxDepth = i; n.size = n.firstChild ? pv.sum(n.childNodes, function(n) { return n.size; }) : that.$size.apply(that, (stack[0] = n, stack)); }); stack.shift(); /* Order */ switch (s.order) { case "ascending": root.sort(function(a, b) { return a.size - b.size; }); break; case "descending": root.sort(function(b, a) { return a.size - b.size; }); break; } /* Compute the unit breadth and depth of each node. */ var ds = 1 / maxDepth; root.minBreadth = 0; root.breadth = .5; root.maxBreadth = 1; root.visitBefore(function(n) { var b = n.minBreadth, s = n.maxBreadth - b; for (var c = n.firstChild; c; c = c.nextSibling) { c.minBreadth = b; c.maxBreadth = b += (c.size / n.size) * s; c.breadth = (b + c.minBreadth) / 2; } }); root.visitAfter(function(n, i) { n.minDepth = (i - 1) * ds; n.maxDepth = n.depth = i * ds; }); pv.Layout.Hierarchy.NodeLink.buildImplied.call(this, s); }; /** * Constructs a new, empty space-filling partition layout. Layouts are not * typically constructed directly; instead, they are added to an existing panel * via {@link pv.Mark#add}. * * @class A variant of partition layout that is space-filling. The meaning of * the exported mark prototypes changes slightly in the space-filling * implementation:
      * *
    • node - for rendering nodes; typically a {@link pv.Bar} for * non-radial orientations, and a {@link pv.Wedge} for radial orientations. * *

    • link - unsupported; undefined. Links are encoded implicitly * in the arrangement of the space-filling nodes. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. * *
    For more details on how to use this layout, see * {@link pv.Layout.Partition}. * * @extends pv.Layout.Partition */ pv.Layout.Partition.Fill = function() { pv.Layout.Partition.call(this); pv.Layout.Hierarchy.Fill.constructor.call(this); }; pv.Layout.Partition.Fill.prototype = pv.extend(pv.Layout.Partition); /** @private */ pv.Layout.Partition.Fill.prototype.buildImplied = function(s) { if (pv.Layout.Partition.prototype.buildImplied.call(this, s)) return; pv.Layout.Hierarchy.Fill.buildImplied.call(this, s); }; /** * Constructs a new, empty arc layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a layout for arc diagrams. An arc diagram is a network * visualization with a one-dimensional layout of nodes, using circular arcs to * render links between nodes. For undirected networks, arcs are rendering on a * single side; this makes arc diagrams useful as annotations to other * two-dimensional network layouts, such as rollup, matrix or table layouts. For * directed networks, links in opposite directions can be rendered on opposite * sides using directed(true). * *

    Arc layouts are particularly sensitive to node ordering; for best results, * order the nodes such that related nodes are close to each other. A poor * (e.g., random) order may result in large arcs with crossovers that impede * visual processing. A future improvement to this layout may include automatic * reordering using, e.g., spectral graph layout or simulated annealing. * *

    This visualization technique is related to that developed by * M. Wattenberg, "Arc * Diagrams: Visualizing Structure in Strings" in IEEE InfoVis, 2002. * However, this implementation is limited to simple node-link networks, as * opposed to structures with hierarchical self-similarity (such as strings). * *

    As with other network layouts, three mark prototypes are provided:

      * *
    • node - for rendering nodes; typically a {@link pv.Dot}. *
    • link - for rendering links; typically a {@link pv.Line}. *
    • label - for rendering node labels; typically a {@link pv.Label}. * *
    For more details on how this layout is structured and can be customized, * see {@link pv.Layout.Network}. * * @extends pv.Layout.Network **/ pv.Layout.Arc = function() { pv.Layout.Network.call(this); var interpolate, // cached interpolate directed, // cached directed reverse, // cached reverse buildImplied = this.buildImplied; /** @private Cache layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, s); directed = s.directed; interpolate = s.orient == "radial" ? "linear" : "polar"; reverse = s.orient == "right" || s.orient == "top"; }; /* Override link properties to handle directedness and orientation. */ this.link .data(function(p) { var s = p.sourceNode, t = p.targetNode; return reverse != (directed || (s.breadth < t.breadth)) ? [s, t] : [t, s]; }) .interpolate(function() { return interpolate; }); }; pv.Layout.Arc.prototype = pv.extend(pv.Layout.Network) .property("orient", String) .property("directed", Boolean); /** * Default properties for arc layouts. By default, the orientation is "bottom". * * @type pv.Layout.Arc */ pv.Layout.Arc.prototype.defaults = new pv.Layout.Arc() .extend(pv.Layout.Network.prototype.defaults) .orient("bottom"); /** * Specifies an optional sort function. The sort function follows the same * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort * function provides an alternative to sort the nodes as they are specified by * the nodes property; the main advantage of doing this is that the * comparator function can access implicit fields populated by the network * layout, such as the linkDegree. * *

    Note that arc diagrams are particularly sensitive to order. This is * referred to as the seriation problem, and many different techniques exist to * find good node orders that emphasize clusters, such as spectral layout and * simulated annealing. * * @param {function} f comparator function for nodes. * @returns {pv.Layout.Arc} this. */ pv.Layout.Arc.prototype.sort = function(f) { this.$sort = f; return this; }; /** @private Populates the x, y and angle attributes on the nodes. */ pv.Layout.Arc.prototype.buildImplied = function(s) { if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return; var nodes = s.nodes, orient = s.orient, sort = this.$sort, index = pv.range(nodes.length), w = s.width, h = s.height, r = Math.min(w, h) / 2; /* Sort the nodes. */ if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); }); /** @private Returns the mid-angle, given the breadth. */ function midAngle(b) { switch (orient) { case "top": return -Math.PI / 2; case "bottom": return Math.PI / 2; case "left": return Math.PI; case "right": return 0; case "radial": return (b - .25) * 2 * Math.PI; } } /** @private Returns the x-position, given the breadth. */ function x(b) { switch (orient) { case "top": case "bottom": return b * w; case "left": return 0; case "right": return w; case "radial": return w / 2 + r * Math.cos(midAngle(b)); } } /** @private Returns the y-position, given the breadth. */ function y(b) { switch (orient) { case "top": return 0; case "bottom": return h; case "left": case "right": return b * h; case "radial": return h / 2 + r * Math.sin(midAngle(b)); } } /* Populate the x, y and mid-angle attributes. */ for (var i = 0; i < nodes.length; i++) { var n = nodes[index[i]], b = n.breadth = (i + .5) / nodes.length; n.x = x(b); n.y = y(b); n.midAngle = midAngle(b); } }; /** * The orientation. The default orientation is "left", which means that nodes * will be positioned from left-to-right in the order they are specified in the * nodes property. The following orientations are supported:

      * *
    • left - left-to-right. *
    • right - right-to-left. *
    • top - top-to-bottom. *
    • bottom - bottom-to-top. *
    • radial - radially, starting at 12 o'clock and proceeding clockwise.
    * * @type string * @name pv.Layout.Arc.prototype.orient */ /** * Whether this arc digram is directed (bidirectional); only applies to * non-radial orientations. By default, arc digrams are undirected, such that * all arcs appear on one side. If the arc digram is directed, then forward * links are drawn on the conventional side (the same as as undirected * links--right, left, bottom and top for left, right, top and bottom, * respectively), while reverse links are drawn on the opposite side. * * @type boolean * @name pv.Layout.Arc.prototype.directed */ /** * Constructs a new, empty horizon layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a horizon layout, which is a variation of a single-series * area chart where the area is folded into multiple bands. Color is used to * encode band, allowing the size of the chart to be reduced significantly * without impeding readability. This layout algorithm is based on the work of * J. Heer, N. Kong and M. Agrawala in "Sizing * the Horizon: The Effects of Chart Size and Layering on the Graphical * Perception of Time Series Visualizations", CHI 2009. * *

    This layout exports a single band mark prototype, which is * intended to be used with an area mark. The band mark is contained in a panel * which is replicated per band (and for negative/positive bands). For example, * to create a simple horizon graph given an array of numbers: * *

    vis.add(pv.Layout.Horizon)
     *     .bands(n)
     *   .band.add(pv.Area)
     *     .data(data)
     *     .left(function() this.index * 35)
     *     .height(function(d) d * 40);
    * * The layout can be further customized by changing the number of bands, and * toggling whether the negative bands are mirrored or offset. (See the * above-referenced paper for guidance.) * *

    The fillStyle of the area can be overridden, though typically it * is easier to customize the layout's behavior through the custom * backgroundStyle, positiveStyle and negativeStyle * properties. By default, the background is white, positive bands are blue, and * negative bands are red. For the most accurate presentation, use fully-opaque * colors of equal intensity for the negative and positive bands. * * @extends pv.Layout */ pv.Layout.Horizon = function() { pv.Layout.call(this); var that = this, bands, // cached bands mode, // cached mode size, // cached height fill, // cached background style red, // cached negative color (ramp) blue, // cached positive color (ramp) buildImplied = this.buildImplied; /** @private Cache the layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, s); bands = s.bands; mode = s.mode; size = Math.round((mode == "color" ? .5 : 1) * s.height); fill = s.backgroundStyle; red = pv.ramp(fill, s.negativeStyle).domain(0, bands); blue = pv.ramp(fill, s.positiveStyle).domain(0, bands); }; var bands = new pv.Panel() .data(function() { return pv.range(bands * 2); }) .overflow("hidden") .height(function() { return size; }) .top(function(i) { return mode == "color" ? (i & 1) * size : 0; }) .fillStyle(function(i) { return i ? null : fill; }); /** * The band prototype. This prototype is intended to be used with an Area * mark to render the horizon bands. * * @type pv.Mark * @name pv.Layout.Horizon.prototype.band */ this.band = new pv.Mark() .top(function(d, i) { return mode == "mirror" && i & 1 ? (i + 1 >> 1) * size : null; }) .bottom(function(d, i) { return mode == "mirror" ? (i & 1 ? null : (i + 1 >> 1) * -size) : ((i & 1 || -1) * (i + 1 >> 1) * size); }) .fillStyle(function(d, i) { return (i & 1 ? red : blue)((i >> 1) + 1); }); this.band.add = function(type) { return that.add(pv.Panel).extend(bands).add(type).extend(this); }; }; pv.Layout.Horizon.prototype = pv.extend(pv.Layout) .property("bands", Number) .property("mode", String) .property("backgroundStyle", pv.color) .property("positiveStyle", pv.color) .property("negativeStyle", pv.color); /** * Default properties for horizon layouts. By default, there are two bands, the * mode is "offset", the background style is "white", the positive style is * blue, negative style is red. * * @type pv.Layout.Horizon */ pv.Layout.Horizon.prototype.defaults = new pv.Layout.Horizon() .extend(pv.Layout.prototype.defaults) .bands(2) .mode("offset") .backgroundStyle("white") .positiveStyle("#1f77b4") .negativeStyle("#d62728"); /** * The horizon mode: offset, mirror, or color. The default is "offset". * * @type string * @name pv.Layout.Horizon.prototype.mode */ /** * The number of bands. Must be at least one. The default value is two. * * @type number * @name pv.Layout.Horizon.prototype.bands */ /** * The positive band color; if non-null, the interior of positive bands are * filled with the specified color. The default value of this property is blue. * For accurate blending, this color should be fully opaque. * * @type pv.Color * @name pv.Layout.Horizon.prototype.positiveStyle */ /** * The negative band color; if non-null, the interior of negative bands are * filled with the specified color. The default value of this property is red. * For accurate blending, this color should be fully opaque. * * @type pv.Color * @name pv.Layout.Horizon.prototype.negativeStyle */ /** * The background color. The panel background is filled with the specified * color, and the negative and positive bands are filled with an interpolated * color between this color and the respective band color. The default value of * this property is white. For accurate blending, this color should be fully * opaque. * * @type pv.Color * @name pv.Layout.Horizon.prototype.backgroundStyle */ /** * Constructs a new, empty rollup network layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a network visualization using a node-link diagram where * nodes are rolled up along two dimensions. This implementation is based on the * "PivotGraph" designed by Martin Wattenberg: * *

    The method is designed for graphs that are "multivariate", i.e., * where each node is associated with several attributes. Unlike visualizations * which emphasize global graph topology, PivotGraph uses a simple grid-based * approach to focus on the relationship between node attributes & * connections.
    * * This layout requires two psuedo-properties to be specified, which assign node * positions along the two dimensions {@link #x} and {@link #y}, corresponding * to the left and top properties, respectively. Typically, these functions are * specified using an {@link pv.Scale.ordinal}. Nodes that share the same * position in x and y are "rolled up" into a meta-node, and * similarly links are aggregated between meta-nodes. For example, to construct * a rollup to analyze links by gender and affiliation, first define two ordinal * scales: * *
    var x = pv.Scale.ordinal(nodes, function(d) d.gender).split(0, w),
     *     y = pv.Scale.ordinal(nodes, function(d) d.aff).split(0, h);
    * * Next, define the position psuedo-properties: * *
        .x(function(d) x(d.gender))
     *     .y(function(d) y(d.aff))
    * * Linear and other quantitative scales can alternatively be used to position * the nodes along either dimension. Note, however, that the rollup requires * that the positions match exactly, and thus ordinal scales are recommended to * avoid precision errors. * *

    Note that because this layout provides a visualization of the rolled up * graph, the data properties for the mark prototypes (node, * link and label) are different from most other network * layouts: they reference the rolled-up nodes and links, rather than the nodes * and links of the full network. The underlying nodes and links for each * rolled-up node and link can be accessed via the nodes and * links attributes, respectively. The aggregated link values for * rolled-up links can similarly be accessed via the linkValue * attribute. * *

    For undirected networks, links are duplicated in both directions. For * directed networks, use directed(true). The graph is assumed to be * undirected by default. * * @extends pv.Layout.Network * @see "Visual Exploration of Multivariate Graphs" by M. Wattenberg, CHI 2006. */ pv.Layout.Rollup = function() { pv.Layout.Network.call(this); var that = this, nodes, // cached rollup nodes links, // cached rollup links buildImplied = that.buildImplied; /** @private Cache layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, s); nodes = s.$rollup.nodes; links = s.$rollup.links; }; /* Render rollup nodes. */ this.node .data(function() { return nodes; }) .size(function(d) { return d.nodes.length * 20; }); /* Render rollup links. */ this.link .interpolate("polar") .eccentricity(.8); this.link.add = function(type) { return that.add(pv.Panel) .data(function() { return links; }) .add(type) .extend(this); }; }; pv.Layout.Rollup.prototype = pv.extend(pv.Layout.Network) .property("directed", Boolean); /** * Whether the underlying network is directed. By default, the graph is assumed * to be undirected, and links are rendered in both directions. If the network * is directed, then forward links are drawn above the diagonal, while reverse * links are drawn below. * * @type boolean * @name pv.Layout.Rollup.prototype.directed */ /** * Specifies the x-position function used to rollup nodes. The rolled up * nodes are positioned horizontally using the return values from the given * function. Typically the function is specified as an ordinal scale. For * single-dimension rollups, a constant value can be specified. * * @param {function} f the x-position function. * @returns {pv.Layout.Rollup} this. * @see pv.Scale.ordinal */ pv.Layout.Rollup.prototype.x = function(f) { this.$x = pv.functor(f); return this; }; /** * Specifies the y-position function used to rollup nodes. The rolled up * nodes are positioned vertically using the return values from the given * function. Typically the function is specified as an ordinal scale. For * single-dimension rollups, a constant value can be specified. * * @param {function} f the y-position function. * @returns {pv.Layout.Rollup} this. * @see pv.Scale.ordinal */ pv.Layout.Rollup.prototype.y = function(f) { this.$y = pv.functor(f); return this; }; /** @private */ pv.Layout.Rollup.prototype.buildImplied = function(s) { if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return; var nodes = s.nodes, links = s.links, directed = s.directed, n = nodes.length, x = [], y = [], rnindex = 0, rnodes = {}, rlinks = {}; /** @private */ function id(i) { return x[i] + "," + y[i]; } /* Iterate over the data, evaluating the x and y functions. */ var stack = pv.Mark.stack, o = {parent: this}; stack.unshift(null); for (var i = 0; i < n; i++) { o.index = i; stack[0] = nodes[i]; x[i] = this.$x.apply(o, stack); y[i] = this.$y.apply(o, stack); } stack.shift(); /* Compute rollup nodes. */ for (var i = 0; i < nodes.length; i++) { var nodeId = id(i), rn = rnodes[nodeId]; if (!rn) { rn = rnodes[nodeId] = pv.extend(nodes[i]); rn.index = rnindex++; rn.x = x[i]; rn.y = y[i]; rn.nodes = []; } rn.nodes.push(nodes[i]); } /* Compute rollup links. */ for (var i = 0; i < links.length; i++) { var source = links[i].sourceNode, target = links[i].targetNode, rsource = rnodes[id(source.index)], rtarget = rnodes[id(target.index)], reverse = !directed && rsource.index > rtarget.index, linkId = reverse ? rtarget.index + "," + rsource.index : rsource.index + "," + rtarget.index, rl = rlinks[linkId]; if (!rl) { rl = rlinks[linkId] = { sourceNode: rsource, targetNode: rtarget, linkValue: 0, links: [] }; } rl.links.push(links[i]); rl.linkValue += links[i].linkValue; } /* Export the rolled up nodes and links to the scene. */ s.$rollup = { nodes: pv.values(rnodes), links: pv.values(rlinks) }; }; /** * Constructs a new, empty matrix network layout. Layouts are not typically * constructed directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class Implements a network visualization using a matrix view. This is, in * effect, a visualization of the graph's adjacency matrix: the cell at * row i, column j, corresponds to the link from node i to * node j. The fill color of each cell is binary by default, and * corresponds to whether a link exists between the two nodes. If the underlying * graph has links with variable values, the fillStyle property can be * substited to use an appropriate color function, such as {@link pv.ramp}. * *

    For undirected networks, the matrix is symmetric around the diagonal. For * directed networks, links in opposite directions can be rendered on opposite * sides of the diagonal using directed(true). The graph is assumed to * be undirected by default. * *

    The mark prototypes for this network layout are slightly different than * other implementations:

      * *
    • node - unsupported; undefined. No mark is needed to visualize * nodes directly, as the nodes are implicit in the location (rows and columns) * of the links. * *

    • link - for rendering links; typically a {@link pv.Bar}. The * link mark is added directly to the layout, with the data property defined as * all possible pairs of nodes. Each pair is represented as a * {@link pv.Network.Layout.Link}, though the linkValue attribute may * be 0 if no link exists in the graph. * *

    • label - for rendering node labels; typically a * {@link pv.Label}. The label mark is added directly to the layout, with the * data property defined via the layout's nodes property; note, * however, that the nodes are duplicated so as to provide a label across the * top and down the side. Properties such as strokeStyle and * fillStyle can be overridden to compute properties from node data * dynamically. * *
    For more details on how to use this layout, see * {@link pv.Layout.Network}. * * @extends pv.Layout.Network */ pv.Layout.Matrix = function() { pv.Layout.Network.call(this); var that = this, n, // cached matrix size dx, // cached cell width dy, // cached cell height labels, // cached labels (array of strings) pairs, // cached pairs (array of links) buildImplied = that.buildImplied; /** @private Cache layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, s); n = s.nodes.length; dx = s.width / n; dy = s.height / n; labels = s.$matrix.labels; pairs = s.$matrix.pairs; }; /* Links are all pairs of nodes. */ this.link .data(function() { return pairs; }) .left(function() { return dx * (this.index % n); }) .top(function() { return dy * Math.floor(this.index / n); }) .width(function() { return dx; }) .height(function() { return dy; }) .lineWidth(1.5) .strokeStyle("#fff") .fillStyle(function(l) { return l.linkValue ? "#555" : "#eee"; }) .parent = this; /* No special add for links! */ delete this.link.add; /* Labels are duplicated for top & left. */ this.label .data(function() { return labels; }) .left(function() { return this.index & 1 ? dx * ((this.index >> 1) + .5) : null; }) .top(function() { return this.index & 1 ? null : dy * ((this.index >> 1) + .5); }) .textMargin(4) .textAlign(function() { return this.index & 1 ? "left" : "right"; }) .textAngle(function() { return this.index & 1 ? -Math.PI / 2 : 0; }); /* The node mark is unused. */ delete this.node; }; pv.Layout.Matrix.prototype = pv.extend(pv.Layout.Network) .property("directed", Boolean); /** * Whether this matrix visualization is directed (bidirectional). By default, * the graph is assumed to be undirected, such that the visualization is * symmetric across the matrix diagonal. If the network is directed, then * forward links are drawn above the diagonal, while reverse links are drawn * below. * * @type boolean * @name pv.Layout.Matrix.prototype.directed */ /** * Specifies an optional sort function. The sort function follows the same * comparator contract required by {@link pv.Dom.Node#sort}. Specifying a sort * function provides an alternative to sort the nodes as they are specified by * the nodes property; the main advantage of doing this is that the * comparator function can access implicit fields populated by the network * layout, such as the linkDegree. * *

    Note that matrix visualizations are particularly sensitive to order. This * is referred to as the seriation problem, and many different techniques exist * to find good node orders that emphasize clusters, such as spectral layout and * simulated annealing. * * @param {function} f comparator function for nodes. * @returns {pv.Layout.Matrix} this. */ pv.Layout.Matrix.prototype.sort = function(f) { this.$sort = f; return this; }; /** @private */ pv.Layout.Matrix.prototype.buildImplied = function(s) { if (pv.Layout.Network.prototype.buildImplied.call(this, s)) return; var nodes = s.nodes, links = s.links, sort = this.$sort, n = nodes.length, index = pv.range(n), labels = [], pairs = [], map = {}; s.$matrix = {labels: labels, pairs: pairs}; /* Sort the nodes. */ if (sort) index.sort(function(a, b) { return sort(nodes[a], nodes[b]); }); /* Create pairs. */ for (var i = 0; i < n; i++) { for (var j = 0; j < n; j++) { var a = index[i], b = index[j], p = { row: i, col: j, sourceNode: nodes[a], targetNode: nodes[b], linkValue: 0 }; pairs.push(map[a + "." + b] = p); } } /* Create labels. */ for (var i = 0; i < n; i++) { var a = index[i]; labels.push(nodes[a], nodes[a]); } /* Accumulate link values. */ for (var i = 0; i < links.length; i++) { var l = links[i], source = l.sourceNode.index, target = l.targetNode.index, value = l.linkValue; map[source + "." + target].linkValue += value; if (!s.directed) map[target + "." + source].linkValue += value; } }; // ranges (bad, satisfactory, good) // measures (actual, forecast) // markers (previous, goal) /* * Chart design based on the recommendations of Stephen Few. Implementation * based on the work of Clint Ivy, Jamie Love, and Jason Davies. * http://projects.instantcognition.com/protovis/bulletchart/ */ /** * Constructs a new, empty bullet layout. Layouts are not typically constructed * directly; instead, they are added to an existing panel via * {@link pv.Mark#add}. * * @class * @extends pv.Layout */ pv.Layout.Bullet = function() { pv.Layout.call(this); var that = this, buildImplied = that.buildImplied, scale = that.x = pv.Scale.linear(), orient, horizontal, rangeColor, measureColor, x; /** @private Cache layout state to optimize properties. */ this.buildImplied = function(s) { buildImplied.call(this, x = s); orient = s.orient; horizontal = /^left|right$/.test(orient); rangeColor = pv.ramp("#bbb", "#eee") .domain(0, Math.max(1, x.ranges.length - 1)); measureColor = pv.ramp("steelblue", "lightsteelblue") .domain(0, Math.max(1, x.measures.length - 1)); }; /** * The range prototype. * * @type pv.Mark * @name pv.Layout.Bullet.prototype.range */ (this.range = new pv.Mark()) .data(function() { return x.ranges; }) .reverse(true) .left(function() { return orient == "left" ? 0 : null; }) .top(function() { return orient == "top" ? 0 : null; }) .right(function() { return orient == "right" ? 0 : null; }) .bottom(function() { return orient == "bottom" ? 0 : null; }) .width(function(d) { return horizontal ? scale(d) : null; }) .height(function(d) { return horizontal ? null : scale(d); }) .fillStyle(function() { return rangeColor(this.index); }) .antialias(false) .parent = that; /** * The measure prototype. * * @type pv.Mark * @name pv.Layout.Bullet.prototype.measure */ (this.measure = new pv.Mark()) .extend(this.range) .data(function() { return x.measures; }) .left(function() { return orient == "left" ? 0 : horizontal ? null : this.parent.width() / 3.25; }) .top(function() { return orient == "top" ? 0 : horizontal ? this.parent.height() / 3.25 : null; }) .right(function() { return orient == "right" ? 0 : horizontal ? null : this.parent.width() / 3.25; }) .bottom(function() { return orient == "bottom" ? 0 : horizontal ? this.parent.height() / 3.25 : null; }) .fillStyle(function() { return measureColor(this.index); }) .parent = that; /** * The marker prototype. * * @type pv.Mark * @name pv.Layout.Bullet.prototype.marker */ (this.marker = new pv.Mark()) .data(function() { return x.markers; }) .left(function(d) { return orient == "left" ? scale(d) : horizontal ? null : this.parent.width() / 2; }) .top(function(d) { return orient == "top" ? scale(d) : horizontal ? this.parent.height() / 2 : null; }) .right(function(d) { return orient == "right" ? scale(d) : null; }) .bottom(function(d) { return orient == "bottom" ? scale(d) : null; }) .strokeStyle("black") .shape("bar") .angle(function() { return horizontal ? 0 : Math.PI / 2; }) .parent = that; (this.tick = new pv.Mark()) .data(function() { return scale.ticks(7); }) .left(function(d) { return orient == "left" ? scale(d) : null; }) .top(function(d) { return orient == "top" ? scale(d) : null; }) .right(function(d) { return orient == "right" ? scale(d) : horizontal ? null : -6; }) .bottom(function(d) { return orient == "bottom" ? scale(d) : horizontal ? -8 : null; }) .height(function() { return horizontal ? 6 : null; }) .width(function() { return horizontal ? null : 6; }) .parent = that; }; pv.Layout.Bullet.prototype = pv.extend(pv.Layout) .property("orient", String) // left, right, top, bottom .property("ranges") .property("markers") .property("measures") .property("maximum", Number); /** * Default properties for bullet layouts. * * @type pv.Layout.Bullet */ pv.Layout.Bullet.prototype.defaults = new pv.Layout.Bullet() .extend(pv.Layout.prototype.defaults) .orient("left") .ranges([]) .markers([]) .measures([]); /** * The orientation. * * @type string * @name pv.Layout.Bullet.prototype.orient */ /** * The array of range values. * * @type array * @name pv.Layout.Bullet.prototype.ranges */ /** * The array of marker values. * * @type array * @name pv.Layout.Bullet.prototype.markers */ /** * The array of measure values. * * @type array * @name pv.Layout.Bullet.prototype.measures */ /** * Optional; the maximum range value. * * @type number * @name pv.Layout.Bullet.prototype.maximum */ /** @private */ pv.Layout.Bullet.prototype.buildImplied = function(s) { pv.Layout.prototype.buildImplied.call(this, s); var size = this.parent[/^left|right$/.test(s.orient) ? "width" : "height"](); s.maximum = s.maximum || pv.max([].concat(s.ranges, s.markers, s.measures)); this.x.domain(0, s.maximum).range(0, size); }; /** * Abstract; see an implementing class for details. * * @class Represents a reusable interaction; applies an interactive behavior to * a given mark. Behaviors are themselves functions designed to be used as event * handlers. For example, to add pan and zoom support to any panel, say: * *

        .event("mousedown", pv.Behavior.pan())
     *     .event("mousewheel", pv.Behavior.zoom())
    * * The behavior should be registered on the event that triggers the start of the * behavior. Typically, the behavior will take care of registering for any * additional events that are necessary. For example, dragging starts on * mousedown, while the drag behavior automatically listens for mousemove and * mouseup events on the window. By listening to the window, the behavior can * continue to receive mouse events even if the mouse briefly leaves the mark * being dragged, or even the root panel. * *

    Each behavior implementation has specific requirements as to which events * it supports, and how it should be used. For example, the drag behavior * requires that the data associated with the mark be an object with x * and y attributes, such as a {@link pv.Vector}, storing the mark's * position. See an implementing class for details. * * @see pv.Behavior.drag * @see pv.Behavior.pan * @see pv.Behavior.point * @see pv.Behavior.select * @see pv.Behavior.zoom * @extends function */ pv.Behavior = {}; /** * Returns a new drag behavior to be registered on mousedown events. * * @class Implements interactive dragging starting with mousedown events. * Register this behavior on marks that should be draggable by the user, such as * the selected region for brushing and linking. This behavior can be used in * tandom with {@link pv.Behavior.select} to allow the selected region to be * dragged interactively. * *

    After the initial mousedown event is triggered, this behavior listens for * mousemove and mouseup events on the window. This allows dragging to continue * even if the mouse temporarily leaves the mark that is being dragged, or even * the root panel. * *

    This behavior requires that the data associated with the mark being * dragged have x and y attributes that correspond to the * mark's location in pixels. The mark's positional properties are not set * directly by this behavior; instead, the positional properties should be * defined as: * *

        .left(function(d) d.x)
     *     .top(function(d) d.y)
    * * Thus, the behavior does not move the mark directly, but instead updates the * mark position by updating the underlying data. Note that if the positional * properties are defined with bottom and right (rather than top and left), the * drag behavior will be inverted, which will confuse users! * *

    The drag behavior is bounded by the parent panel; the x and * y attributes are clamped such that the mark being dragged does not * extend outside the enclosing panel's bounds. To facilitate this, the drag * behavior also queries for dx and dy attributes on the * underlying data, to determine the dimensions of the bar being dragged. For * non-rectangular marks, the drag behavior simply treats the mark as a point, * which means that only the mark's center is bounded. * *

    The mark being dragged is automatically re-rendered for each mouse event * as part of the drag operation. In addition, a fix attribute is * populated on the mark, which allows visual feedback for dragging. For * example, to change the mark fill color while dragging: * *

        .fillStyle(function(d) d.fix ? "#ff7f0e" : "#aec7e8")
    * * In some cases, such as with network layouts, dragging the mark may cause * related marks to change, in which case additional marks may also need to be * rendered. This can be accomplished by listening for the drag * psuedo-events:
      * *
    • dragstart (on mousedown) *
    • drag (on mousemove) *
    • dragend (on mouseup) * *
    For example, to render the parent panel while dragging, thus * re-rendering all sibling marks: * *
        .event("mousedown", pv.Behavior.drag())
     *     .event("drag", function() this.parent)
    * * This behavior may be enhanced in the future to allow more flexible * configuration of drag behavior. * * @extends pv.Behavior * @see pv.Behavior * @see pv.Behavior.select * @see pv.Layout.force */ pv.Behavior.drag = function() { var scene, // scene context index, // scene context p, // particle being dragged v1, // initial mouse-particle offset max; /** @private */ function mousedown(d) { index = this.index; scene = this.scene; var m = this.mouse(); v1 = ((p = d).fix = pv.vector(d.x, d.y)).minus(m); max = { x: this.parent.width() - (d.dx || 0), y: this.parent.height() - (d.dy || 0) }; scene.mark.context(scene, index, function() { this.render(); }); pv.Mark.dispatch("dragstart", scene, index); } /** @private */ function mousemove() { if (!scene) return; scene.mark.context(scene, index, function() { var m = this.mouse(); p.x = p.fix.x = Math.max(0, Math.min(v1.x + m.x, max.x)); p.y = p.fix.y = Math.max(0, Math.min(v1.y + m.y, max.y)); this.render(); }); pv.Mark.dispatch("drag", scene, index); } /** @private */ function mouseup() { if (!scene) return; p.fix = null; scene.mark.context(scene, index, function() { this.render(); }); pv.Mark.dispatch("dragend", scene, index); scene = null; } pv.listen(window, "mousemove", mousemove); pv.listen(window, "mouseup", mouseup); return mousedown; }; /** * Returns a new point behavior to be registered on mousemove events. * * @class Implements interactive fuzzy pointing, identifying marks that are in * close proximity to the mouse cursor. This behavior is an alternative to the * native mouseover and mouseout events, improving usability. Rather than * requiring the user to mouseover a mark exactly, the mouse simply needs to * move near the given mark and a "point" event is triggered. In addition, if * multiple marks overlap, the point behavior can be used to identify the mark * instance closest to the cursor, as opposed to the one that is rendered on * top. * *

    The point behavior can also identify the closest mark instance for marks * that produce a continuous graphic primitive. The point behavior can thus be * used to provide details-on-demand for both discrete marks (such as dots and * bars), as well as continuous marks (such as lines and areas). * *

    This behavior is implemented by finding the closest mark instance to the * mouse cursor on every mousemove event. If this closest mark is within the * given radius threshold, which defaults to 30 pixels, a "point" psuedo-event * is dispatched to the given mark instance. If any mark were previously * pointed, it would receive a corresponding "unpoint" event. These two * psuedo-event types correspond to the native "mouseover" and "mouseout" * events, respectively. To increase the radius at which the point behavior can * be applied, specify an appropriate threshold to the constructor, up to * Infinity. * *

    By default, the standard Cartesian distance is computed. However, with * some visualizations it is desirable to consider only a single dimension, such * as the x-dimension for an independent variable. In this case, the * collapse parameter can be set to collapse the y dimension: * *

        .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))
    * *

    This behavior only listens to mousemove events on the assigned panel, * which is typically the root panel. The behavior will search recursively for * descendant marks to point. If the mouse leaves the assigned panel, the * behavior no longer receives mousemove events; an unpoint psuedo-event is * automatically dispatched to unpoint any pointed mark. Marks may be re-pointed * when the mouse reenters the panel. * *

    Panels have transparent fill styles by default; this means that panels may * not receive the initial mousemove event to start pointing. To fix this * problem, either given the panel a visible fill style (such as "white"), or * set the events property to "all" such that the panel receives events * despite its transparent fill. * *

    Note: this behavior does not currently wedge marks. * * @extends pv.Behavior * * @param {number} [r] the fuzzy radius threshold in pixels * @see "The Bubble Cursor: Enhancing Target Acquisition by Dynamic Resizing of the * Cursor's Activation Area" by T. Grossman & R. Balakrishnan, CHI 2005. */ pv.Behavior.point = function(r) { var unpoint, // the current pointer target collapse = null, // dimensions to collapse kx = 1, // x-dimension cost scale ky = 1, // y-dimension cost scale r2 = arguments.length ? r * r : 900; // fuzzy radius /** @private Search for the mark closest to the mouse. */ function search(scene, index) { var s = scene[index], point = {cost: Infinity}; for (var i = 0, n = s.visible && s.children.length; i < n; i++) { var child = s.children[i], mark = child.mark, p; if (mark.type == "panel") { mark.scene = child; for (var j = 0, m = child.length; j < m; j++) { mark.index = j; p = search(child, j); if (p.cost < point.cost) point = p; } delete mark.scene; delete mark.index; } else if (mark.$handlers.point) { var v = mark.mouse(); for (var j = 0, m = child.length; j < m; j++) { var c = child[j], dx = v.x - c.left - (c.width || 0) / 2, dy = v.y - c.top - (c.height || 0) / 2, dd = kx * dx * dx + ky * dy * dy; if (dd < point.cost) { point.distance = dx * dx + dy * dy; point.cost = dd; point.scene = child; point.index = j; } } } } return point; } /** @private */ function mousemove() { /* If the closest mark is far away, clear the current target. */ var point = search(this.scene, this.index); if ((point.cost == Infinity) || (point.distance > r2)) point = null; /* Unpoint the old target, if it's not the new target. */ if (unpoint) { if (point && (unpoint.scene == point.scene) && (unpoint.index == point.index)) return; pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index); } /* Point the new target, if there is one. */ if (unpoint = point) { pv.Mark.dispatch("point", point.scene, point.index); /* Unpoint when the mouse leaves the root panel. */ pv.listen(this.root.canvas(), "mouseout", mouseout); } } /** @private */ function mouseout(e) { if (unpoint && !pv.ancestor(this, e.relatedTarget)) { pv.Mark.dispatch("unpoint", unpoint.scene, unpoint.index); unpoint = null; } } /** * Sets or gets the collapse parameter. By default, the standard Cartesian * distance is computed. However, with some visualizations it is desirable to * consider only a single dimension, such as the x-dimension for an * independent variable. In this case, the collapse parameter can be set to * collapse the y dimension: * *

        .event("mousemove", pv.Behavior.point(Infinity).collapse("y"))
    * * @function * @returns {pv.Behavior.point} this, or the current collapse parameter. * @name pv.Behavior.point.prototype.collapse * @param {string} [x] the new collapse parameter */ mousemove.collapse = function(x) { if (arguments.length) { collapse = String(x); switch (collapse) { case "y": kx = 1; ky = 0; break; case "x": kx = 0; ky = 1; break; default: kx = 1; ky = 1; break; } return mousemove; } return collapse; }; return mousemove; }; /** * Returns a new select behavior to be registered on mousedown events. * * @class Implements interactive selecting starting with mousedown events. * Register this behavior on panels that should be selectable by the user, such * for brushing and linking. This behavior can be used in tandom with * {@link pv.Behavior.drag} to allow the selected region to be dragged * interactively. * *

    After the initial mousedown event is triggered, this behavior listens for * mousemove and mouseup events on the window. This allows selecting to continue * even if the mouse temporarily leaves the assigned panel, or even the root * panel. * *

    This behavior requires that the data associated with the mark being * dragged have x, y, dx and dy attributes * that correspond to the mark's location and dimensions in pixels. The mark's * positional properties are not set directly by this behavior; instead, the * positional properties should be defined as: * *

        .left(function(d) d.x)
     *     .top(function(d) d.y)
     *     .width(function(d) d.dx)
     *     .height(function(d) d.dy)
    * * Thus, the behavior does not resize the mark directly, but instead updates the * selection by updating the assigned panel's underlying data. Note that if the * positional properties are defined with bottom and right (rather than top and * left), the drag behavior will be inverted, which will confuse users! * *

    The select behavior is bounded by the assigned panel; the positional * attributes are clamped such that the selection does not extend outside the * panel's bounds. * *

    The panel being selected is automatically re-rendered for each mouse event * as part of the drag operation. This behavior may be enhanced in the future to * allow more flexible configuration of select behavior. In some cases, such as * with parallel coordinates, making a selection may cause related marks to * change, in which case additional marks may also need to be rendered. This can * be accomplished by listening for the select psuedo-events:

      * *
    • selectstart (on mousedown) *
    • select (on mousemove) *
    • selectend (on mouseup) * *
    For example, to render the parent panel while selecting, thus * re-rendering all sibling marks: * *
        .event("mousedown", pv.Behavior.drag())
     *     .event("select", function() this.parent)
    * * This behavior may be enhanced in the future to allow more flexible * configuration of the selection behavior. * * @extends pv.Behavior * @see pv.Behavior.drag */ pv.Behavior.select = function() { var scene, // scene context index, // scene context r, // region being selected m1; // initial mouse position /** @private */ function mousedown(d) { index = this.index; scene = this.scene; m1 = this.mouse(); r = d; r.x = m1.x; r.y = m1.y; r.dx = r.dy = 0; pv.Mark.dispatch("selectstart", scene, index); } /** @private */ function mousemove() { if (!scene) return; scene.mark.context(scene, index, function() { var m2 = this.mouse(); r.x = Math.max(0, Math.min(m1.x, m2.x)); r.y = Math.max(0, Math.min(m1.y, m2.y)); r.dx = Math.min(this.width(), Math.max(m2.x, m1.x)) - r.x; r.dy = Math.min(this.height(), Math.max(m2.y, m1.y)) - r.y; this.render(); }); pv.Mark.dispatch("select", scene, index); } /** @private */ function mouseup() { if (!scene) return; pv.Mark.dispatch("selectend", scene, index); scene = null; } pv.listen(window, "mousemove", mousemove); pv.listen(window, "mouseup", mouseup); return mousedown; }; /** * Returns a new resize behavior to be registered on mousedown events. * * @class Implements interactive resizing of a selection starting with mousedown * events. Register this behavior on selection handles that should be resizeable * by the user, such for brushing and linking. This behavior can be used in * tandom with {@link pv.Behavior.select} and {@link pv.Behavior.drag} to allow * the selected region to be selected and dragged interactively. * *

    After the initial mousedown event is triggered, this behavior listens for * mousemove and mouseup events on the window. This allows resizing to continue * even if the mouse temporarily leaves the assigned panel, or even the root * panel. * *

    This behavior requires that the data associated with the mark being * resized have x, y, dx and dy attributes * that correspond to the mark's location and dimensions in pixels. The mark's * positional properties are not set directly by this behavior; instead, the * positional properties should be defined as: * *

        .left(function(d) d.x)
     *     .top(function(d) d.y)
     *     .width(function(d) d.dx)
     *     .height(function(d) d.dy)
    * * Thus, the behavior does not resize the mark directly, but instead updates the * size by updating the assigned panel's underlying data. Note that if the * positional properties are defined with bottom and right (rather than top and * left), the resize behavior will be inverted, which will confuse users! * *

    The resize behavior is bounded by the assigned mark's enclosing panel; the * positional attributes are clamped such that the selection does not extend * outside the panel's bounds. * *

    The mark being resized is automatically re-rendered for each mouse event * as part of the resize operation. This behavior may be enhanced in the future * to allow more flexible configuration. In some cases, such as with parallel * coordinates, resizing the selection may cause related marks to change, in * which case additional marks may also need to be rendered. This can be * accomplished by listening for the select psuedo-events:

      * *
    • resizestart (on mousedown) *
    • resize (on mousemove) *
    • resizeend (on mouseup) * *
    For example, to render the parent panel while resizing, thus * re-rendering all sibling marks: * *
        .event("mousedown", pv.Behavior.resize("left"))
     *     .event("resize", function() this.parent)
    * * This behavior may be enhanced in the future to allow more flexible * configuration of the selection behavior. * * @extends pv.Behavior * @see pv.Behavior.select * @see pv.Behavior.drag */ pv.Behavior.resize = function(side) { var scene, // scene context index, // scene context r, // region being selected m1; // initial mouse position /** @private */ function mousedown(d) { index = this.index; scene = this.scene; m1 = this.mouse(); r = d; switch (side) { case "left": m1.x = r.x + r.dx; break; case "right": m1.x = r.x; break; case "top": m1.y = r.y + r.dy; break; case "bottom": m1.y = r.y; break; } pv.Mark.dispatch("resizestart", scene, index); } /** @private */ function mousemove() { if (!scene) return; scene.mark.context(scene, index, function() { var m2 = this.mouse(); r.x = Math.max(0, Math.min(m1.x, m2.x)); r.y = Math.max(0, Math.min(m1.y, m2.y)); r.dx = Math.min(this.parent.width(), Math.max(m2.x, m1.x)) - r.x; r.dy = Math.min(this.parent.height(), Math.max(m2.y, m1.y)) - r.y; this.render(); }); pv.Mark.dispatch("resize", scene, index); } /** @private */ function mouseup() { if (!scene) return; pv.Mark.dispatch("resizeend", scene, index); scene = null; } pv.listen(window, "mousemove", mousemove); pv.listen(window, "mouseup", mouseup); return mousedown; }; /** * Returns a new pan behavior to be registered on mousedown events. * * @class Implements interactive panning starting with mousedown events. * Register this behavior on panels to allow panning. This behavior can be used * in tandem with {@link pv.Behavior.zoom} to allow both panning and zooming: * *
        .event("mousedown", pv.Behavior.pan())
     *     .event("mousewheel", pv.Behavior.zoom())
    * * The pan behavior currently supports only mouse events; support for keyboard * shortcuts to improve accessibility may be added in the future. * *

    After the initial mousedown event is triggered, this behavior listens for * mousemove and mouseup events on the window. This allows panning to continue * even if the mouse temporarily leaves the panel that is being panned, or even * the root panel. * *

    The implementation of this behavior relies on the panel's * transform property, which specifies a matrix transformation that is * applied to child marks. Note that the transform property only affects the * panel's children, but not the panel itself; therefore the panel's fill and * stroke will not change when the contents are panned. * *

    Panels have transparent fill styles by default; this means that panels may * not receive the initial mousedown event to start panning. To fix this * problem, either given the panel a visible fill style (such as "white"), or * set the events property to "all" such that the panel receives events * despite its transparent fill. * *

    The pan behavior has optional support for bounding. If enabled, the user * will not be able to pan the panel outside of the initial bounds. This feature * is designed to work in conjunction with the zoom behavior; otherwise, * bounding the panel effectively disables all panning. * * @extends pv.Behavior * @see pv.Behavior.zoom * @see pv.Panel#transform */ pv.Behavior.pan = function() { var scene, // scene context index, // scene context m1, // transformation matrix at the start of panning v1, // mouse location at the start of panning k, // inverse scale bound; // whether to bound to the panel /** @private */ function mousedown() { index = this.index; scene = this.scene; v1 = pv.vector(pv.event.pageX, pv.event.pageY); m1 = this.transform(); k = 1 / (m1.k * this.scale); if (bound) { bound = { x: (1 - m1.k) * this.width(), y: (1 - m1.k) * this.height() }; } } /** @private */ function mousemove() { if (!scene) return; scene.mark.context(scene, index, function() { var x = (pv.event.pageX - v1.x) * k, y = (pv.event.pageY - v1.y) * k, m = m1.translate(x, y); if (bound) { m.x = Math.max(bound.x, Math.min(0, m.x)); m.y = Math.max(bound.y, Math.min(0, m.y)); } this.transform(m).render(); }); pv.Mark.dispatch("pan", scene, index); } /** @private */ function mouseup() { scene = null; } /** * Sets or gets the bound parameter. If bounding is enabled, the user will not * be able to pan outside the initial panel bounds; this typically applies * only when the pan behavior is used in tandem with the zoom behavior. * Bounding is not enabled by default. * *

    Note: enabling bounding after panning has already occurred will not * immediately reset the transform. Bounding should be enabled before the * panning behavior is applied. * * @function * @returns {pv.Behavior.pan} this, or the current bound parameter. * @name pv.Behavior.pan.prototype.bound * @param {boolean} [x] the new bound parameter. */ mousedown.bound = function(x) { if (arguments.length) { bound = Boolean(x); return this; } return Boolean(bound); }; pv.listen(window, "mousemove", mousemove); pv.listen(window, "mouseup", mouseup); return mousedown; }; /** * Returns a new zoom behavior to be registered on mousewheel events. * * @class Implements interactive zooming using mousewheel events. Register this * behavior on panels to allow zooming. This behavior can be used in tandem with * {@link pv.Behavior.pan} to allow both panning and zooming: * *

        .event("mousedown", pv.Behavior.pan())
     *     .event("mousewheel", pv.Behavior.zoom())
    * * The zoom behavior currently supports only mousewheel events; support for * keyboard shortcuts and gesture events to improve accessibility may be added * in the future. * *

    The implementation of this behavior relies on the panel's * transform property, which specifies a matrix transformation that is * applied to child marks. Note that the transform property only affects the * panel's children, but not the panel itself; therefore the panel's fill and * stroke will not change when the contents are zoomed. The built-in support for * transforms only supports uniform scaling and translates, which is sufficient * for panning and zooming. Note that this is not a strict geometric * transformation, as the lineWidth property is scale-aware: strokes * are drawn at constant size independent of scale. * *

    Panels have transparent fill styles by default; this means that panels may * not receive mousewheel events to zoom. To fix this problem, either given the * panel a visible fill style (such as "white"), or set the events * property to "all" such that the panel receives events despite its transparent * fill. * *

    The zoom behavior has optional support for bounding. If enabled, the user * will not be able to zoom out farther than the initial bounds. This feature is * designed to work in conjunction with the pan behavior. * * @extends pv.Behavior * @see pv.Panel#transform * @see pv.Mark#scale * @param {number} speed */ pv.Behavior.zoom = function(speed) { var bound; // whether to bound to the panel if (!arguments.length) speed = 1 / 48; /** @private */ function mousewheel() { var v = this.mouse(), k = pv.event.wheel * speed, m = this.transform().translate(v.x, v.y) .scale((k < 0) ? (1e3 / (1e3 - k)) : ((1e3 + k) / 1e3)) .translate(-v.x, -v.y); if (bound) { m.k = Math.max(1, m.k); m.x = Math.max((1 - m.k) * this.width(), Math.min(0, m.x)); m.y = Math.max((1 - m.k) * this.height(), Math.min(0, m.y)); } this.transform(m).render(); pv.Mark.dispatch("zoom", this.scene, this.index); } /** * Sets or gets the bound parameter. If bounding is enabled, the user will not * be able to zoom out farther than the initial panel bounds. Bounding is not * enabled by default. If this behavior is used in tandem with the pan * behavior, both should use the same bound parameter. * *

    Note: enabling bounding after zooming has already occurred will not * immediately reset the transform. Bounding should be enabled before the zoom * behavior is applied. * * @function * @returns {pv.Behavior.zoom} this, or the current bound parameter. * @name pv.Behavior.zoom.prototype.bound * @param {boolean} [x] the new bound parameter. */ mousewheel.bound = function(x) { if (arguments.length) { bound = Boolean(x); return this; } return Boolean(bound); }; return mousewheel; }; /** * @ignore * @namespace */ pv.Geo = function() {}; /** * Abstract; not implemented. There is no explicit constructor; this class * merely serves to document the representation used by {@link pv.Geo.scale}. * * @class Represents a pair of geographic coordinates. * * @name pv.Geo.LatLng * @see pv.Geo.scale */ /** * The latitude coordinate in degrees; positive is North. * * @type number * @name pv.Geo.LatLng.prototype.lat */ /** * The longitude coordinate in degrees; positive is East. * * @type number * @name pv.Geo.LatLng.prototype.lng */ /** * Abstract; not implemented. There is no explicit constructor; this class * merely serves to document the representation used by {@link pv.Geo.scale}. * * @class Represents a geographic projection. This class provides the core * implementation for {@link pv.Geo.scale}s, mapping between geographic * coordinates (latitude and longitude) and normalized screen space in the range * [-1,1]. The remaining mapping between normalized screen space and actual * pixels is performed by pv.Geo.scale. * *

    Many geographic projections have a point around which the projection is * centered. Rather than have each implementation add support for a * user-specified center point, the pv.Geo.scale translates the * geographic coordinates relative to the center point for both the forward and * inverse projection. * *

    In general, this class should not be used directly, unless the desire is * to implement a new geographic projection. Instead, use pv.Geo.scale. * Implementations are not required to implement inverse projections, but are * needed for some forms of interactivity. Also note that some inverse * projections are ambiguous, such as the connecting points in Dymaxian maps. * * @name pv.Geo.Projection * @see pv.Geo.scale */ /** * The forward projection. * * @function * @name pv.Geo.Projection.prototype.project * @param {pv.Geo.LatLng} latlng the latitude and longitude to project. * @returns {pv.Vector} the xy-coordinates of the given point. */ /** * The inverse projection; optional. * * @function * @name pv.Geo.Projection.prototype.invert * @param {pv.Vector} xy the x- and y-coordinates to invert. * @returns {pv.Geo.LatLng} the latitude and longitude of the given point. */ /** * The built-in projections. * * @see pv.Geo.Projection * @namespace */ pv.Geo.projections = { /** @see http://en.wikipedia.org/wiki/Mercator_projection */ mercator: { project: function(latlng) { return { x: latlng.lng / 180, y: latlng.lat > 85 ? 1 : latlng.lat < -85 ? -1 : Math.log(Math.tan(Math.PI / 4 + pv.radians(latlng.lat) / 2)) / Math.PI }; }, invert: function(xy) { return { lng: xy.x * 180, lat: pv.degrees(2 * Math.atan(Math.exp(xy.y * Math.PI)) - Math.PI / 2) }; } }, /** @see http://en.wikipedia.org/wiki/Gall-Peters_projection */ "gall-peters": { project: function(latlng) { return { x: latlng.lng / 180, y: Math.sin(pv.radians(latlng.lat)) }; }, invert: function(xy) { return { lng: xy.x * 180, lat: pv.degrees(Math.asin(xy.y)) }; } }, /** @see http://en.wikipedia.org/wiki/Sinusoidal_projection */ sinusoidal: { project: function(latlng) { return { x: pv.radians(latlng.lng) * Math.cos(pv.radians(latlng.lat)) / Math.PI, y: latlng.lat / 90 }; }, invert: function(xy) { return { lng: pv.degrees((xy.x * Math.PI) / Math.cos(xy.y * Math.PI / 2)), lat: xy.y * 90 }; } }, /** @see http://en.wikipedia.org/wiki/Aitoff_projection */ aitoff: { project: function(latlng) { var l = pv.radians(latlng.lng), f = pv.radians(latlng.lat), a = Math.acos(Math.cos(f) * Math.cos(l / 2)); return { x: 2 * (a ? (Math.cos(f) * Math.sin(l / 2) * a / Math.sin(a)) : 0) / Math.PI, y: 2 * (a ? (Math.sin(f) * a / Math.sin(a)) : 0) / Math.PI }; }, invert: function(xy) { var x = xy.x * Math.PI / 2, y = xy.y * Math.PI / 2; return { lng: pv.degrees(x / Math.cos(y)), lat: pv.degrees(y) }; } }, /** @see http://en.wikipedia.org/wiki/Hammer_projection */ hammer: { project: function(latlng) { var l = pv.radians(latlng.lng), f = pv.radians(latlng.lat), c = Math.sqrt(1 + Math.cos(f) * Math.cos(l / 2)); return { x: 2 * Math.SQRT2 * Math.cos(f) * Math.sin(l / 2) / c / 3, y: Math.SQRT2 * Math.sin(f) / c / 1.5 }; }, invert: function(xy) { var x = xy.x * 3, y = xy.y * 1.5, z = Math.sqrt(1 - x * x / 16 - y * y / 4); return { lng: pv.degrees(2 * Math.atan2(z * x, 2 * (2 * z * z - 1))), lat: pv.degrees(Math.asin(z * y)) }; } }, /** The identity or "none" projection. */ identity: { project: function(latlng) { return { x: latlng.lng / 180, y: latlng.lat / 90 }; }, invert: function(xy) { return { lng: xy.x * 180, lat: xy.y * 90 }; } } }; /** * Returns a geographic scale. The arguments to this constructor are optional, * and equivalent to calling {@link #projection}. * * @class Represents a geographic scale; a mapping between latitude-longitude * coordinates and screen pixel coordinates. By default, the domain is inferred * from the geographic coordinates, so that the domain fills the output range. * *

    Note that geographic scales are two-dimensional transformations, rather * than the one-dimensional bidrectional mapping typical of other scales. * Rather than mapping (for example) between a numeric domain and a numeric * range, geographic scales map between two coordinate objects: {@link * pv.Geo.LatLng} and {@link pv.Vector}. * * @param {pv.Geo.Projection} [p] optional projection. * @see pv.Geo.scale#ticks */ pv.Geo.scale = function(p) { var rmin = {x: 0, y: 0}, // default range minimum rmax = {x: 1, y: 1}, // default range maximum d = [], // default domain j = pv.Geo.projections.identity, // domain <-> normalized range x = pv.Scale.linear(-1, 1).range(0, 1), // normalized <-> range y = pv.Scale.linear(-1, 1).range(1, 0), // normalized <-> range c = {lng: 0, lat: 0}, // Center Point lastLatLng, // cached latlng lastPoint; // cached point /** @private */ function scale(latlng) { if (!lastLatLng || (latlng.lng != lastLatLng.lng) || (latlng.lat != lastLatLng.lat)) { lastLatLng = latlng; var p = project(latlng); lastPoint = {x: x(p.x), y: y(p.y)}; } return lastPoint; } /** @private */ function project(latlng) { var offset = {lng: latlng.lng - c.lng, lat: latlng.lat}; return j.project(offset); } /** @private */ function invert(xy) { var latlng = j.invert(xy); latlng.lng += c.lng; return latlng; } /** Returns the projected x-coordinate. */ scale.x = function(latlng) { return scale(latlng).x; }; /** Returns the projected y-coordinate. */ scale.y = function(latlng) { return scale(latlng).y; }; /** * Abstract; this is a local namespace on a given geographic scale. * * @namespace Tick functions for geographic scales. Because geographic scales * represent two-dimensional transformations (as opposed to one-dimensional * transformations typical of other scales), the tick values are similarly * represented as two-dimensional coordinates in the input domain, i.e., * {@link pv.Geo.LatLng} objects. * *

    Also, note that non-rectilinear projections, such as sinsuoidal and * aitoff, may not produce straight lines for constant longitude or constant * latitude. Therefore the returned array of ticks is a two-dimensional array, * sampling various latitudes as constant longitude, and vice versa. * *

    The tick lines can therefore be approximated as polylines, either with * "linear" or "cardinal" interpolation. This is not as accurate as drawing * the true curve through the projection space, but is usually sufficient. * * @name pv.Geo.scale.prototype.ticks * @see pv.Geo.scale * @see pv.Geo.LatLng * @see pv.Line#interpolate */ scale.ticks = { /** * Returns longitude ticks. * * @function * @param {number} [m] the desired number of ticks. * @returns {array} a nested array of pv.Geo.LatLng ticks. * @name pv.Geo.scale.prototype.ticks.prototype.lng */ lng: function(m) { var lat, lng; if (d.length > 1) { var s = pv.Scale.linear(); if (m == undefined) m = 10; lat = s.domain(d, function(d) { return d.lat; }).ticks(m); lng = s.domain(d, function(d) { return d.lng; }).ticks(m); } else { lat = pv.range(-80, 81, 10); lng = pv.range(-180, 181, 10); } return lng.map(function(lng) { return lat.map(function(lat) { return {lat: lat, lng: lng}; }); }); }, /** * Returns latitude ticks. * * @function * @param {number} [m] the desired number of ticks. * @returns {array} a nested array of pv.Geo.LatLng ticks. * @name pv.Geo.scale.prototype.ticks.prototype.lat */ lat: function(m) { return pv.transpose(scale.ticks.lng(m)); } }; /** * Inverts the specified value in the output range, returning the * corresponding value in the input domain. This is frequently used to convert * the mouse location (see {@link pv.Mark#mouse}) to a value in the input * domain. Inversion is only supported for numeric ranges, and not colors. * *

    Note that this method does not do any rounding or bounds checking. If * the input domain is discrete (e.g., an array index), the returned value * should be rounded. If the specified y value is outside the range, * the returned value may be equivalently outside the input domain. * * @function * @name pv.Geo.scale.prototype.invert * @param {number} y a value in the output range (a pixel location). * @returns {number} a value in the input domain. */ scale.invert = function(p) { return invert({x: x.invert(p.x), y: y.invert(p.y)}); }; /** * Sets or gets the input domain. Note that unlike quantitative scales, the * domain cannot be reduced to a simple rectangle (i.e., minimum and maximum * values for latitude and longitude). Instead, the domain values must be * projected to normalized space, effectively finding the domain in normalized * space rather than in terms of latitude and longitude. Thus, changing the * projection requires recomputing the normalized domain. * *

    This method can be invoked several ways: * *

    1. domain(values...) * *

    Specifying the domain as a series of {@link pv.Geo.LatLng}s is the most * explicit and recommended approach. However, if the domain values are * derived from data, you may find the second method more appropriate. * *

    2. domain(array, f) * *

    Rather than enumerating the domain explicitly, you can specify a single * argument of an array. In addition, you can specify an optional accessor * function to extract the domain values (as {@link pv.Geo.LatLng}s) from the * array. If the specified array has fewer than two elements, this scale will * default to the full normalized domain. * *

    2. domain() * *

    Invoking the domain method with no arguments returns the * current domain as an array. * * @function * @name pv.Geo.scale.prototype.domain * @param {...} domain... domain values. * @returns {pv.Geo.scale} this, or the current domain. */ scale.domain = function(array, f) { if (arguments.length) { d = (array instanceof Array) ? ((arguments.length > 1) ? pv.map(array, f) : array) : Array.prototype.slice.call(arguments); if (d.length > 1) { var lngs = d.map(function(c) { return c.lng; }); var lats = d.map(function(c) { return c.lat; }); c = { lng: (pv.max(lngs) + pv.min(lngs)) / 2, lat: (pv.max(lats) + pv.min(lats)) / 2 }; var n = d.map(project); // normalized domain x.domain(n, function(p) { return p.x; }); y.domain(n, function(p) { return p.y; }); } else { c = {lng: 0, lat: 0}; x.domain(-1, 1); y.domain(-1, 1); } lastLatLng = null; // invalidate the cache return this; } return d; }; /** * Sets or gets the output range. This method can be invoked several ways: * *

    1. range(min, max) * *

    If two objects are specified, the arguments should be {@link pv.Vector}s * which specify the minimum and maximum values of the x- and y-coordinates * explicitly. * *

    2. range(width, height) * *

    If two numbers are specified, the arguments specify the maximum values * of the x- and y-coordinates explicitly; the minimum values are implicitly * zero. * *

    3. range() * *

    Invoking the range method with no arguments returns the current * range as an array of two {@link pv.Vector}s: the minimum (top-left) and * maximum (bottom-right) values. * * @function * @name pv.Geo.scale.prototype.range * @param {...} range... range values. * @returns {pv.Geo.scale} this, or the current range. */ scale.range = function(min, max) { if (arguments.length) { if (typeof min == "object") { rmin = {x: Number(min.x), y: Number(min.y)}; rmax = {x: Number(max.x), y: Number(max.y)}; } else { rmin = {x: 0, y: 0}; rmax = {x: Number(min), y: Number(max)}; } x.range(rmin.x, rmax.x); y.range(rmax.y, rmin.y); // XXX flipped? lastLatLng = null; // invalidate the cache return this; } return [rmin, rmax]; }; /** * Sets or gets the projection. This method can be invoked several ways: * *

    1. projection(string) * *

    Specifying a string sets the projection to the given named projection in * {@link pv.Geo.projections}. If no such projection is found, the identity * projection is used. * *

    2. projection(object) * *

    Specifying an object sets the projection to the given custom projection, * which must implement the forward and inverse methods per the * {@link pv.Geo.Projection} interface. * *

    3. projection() * *

    Invoking the projection method with no arguments returns the * current object that defined the projection. * * @function * @name pv.Scale.geo.prototype.projection * @param {...} range... range values. * @returns {pv.Scale.geo} this, or the current range. */ scale.projection = function(p) { if (arguments.length) { j = typeof p == "string" ? pv.Geo.projections[p] || pv.Geo.projections.identity : p; return this.domain(d); // recompute normalized domain } return p; }; /** * Returns a view of this scale by the specified accessor function f. * Given a scale g, g.by(function(d) d.foo) is equivalent to * function(d) g(d.foo). This method should be used judiciously; it * is typically more clear to invoke the scale directly, passing in the value * to be scaled. * * @function * @name pv.Geo.scale.prototype.by * @param {function} f an accessor function. * @returns {pv.Geo.scale} a view of this scale by the specified accessor * function. */ scale.by = function(f) { function by() { return scale(f.apply(this, arguments)); } for (var method in scale) by[method] = scale[method]; return by; }; if (arguments.length) scale.projection(p); return scale; }; ganglia-web-3.6.1/js/protovis-r3.2.js000066400000000000000000003435431231750357400172420ustar00rootroot00000000000000// fba9dc2 var a;if(!Array.prototype.map)Array.prototype.map=function(b,c){for(var d=this.length,f=new Array(d),g=0;g>>0,f=0;f=d)throw new Error("reduce: no values, no initial value");}for(;f=0&&d=69&&m<100?1900:0)});return"([0-9]+)";case "%Y":q.push(function(m){g=m});return"([0-9]+)";case "%%":q.push(function(){}); return"%"}return n});(f=f.match(o))&&f.forEach(function(n,m){q[m](n)});return new Date(g,h,i,j,l,k)};return c}; pv.Format.time=function(b){function c(f){f=Number(f);switch(b){case "short":if(f>=31536E6)return(f/31536E6).toFixed(1)+" years";else if(f>=6048E5)return(f/6048E5).toFixed(1)+" weeks";else if(f>=864E5)return(f/864E5).toFixed(1)+" days";else if(f>=36E5)return(f/36E5).toFixed(1)+" hours";else if(f>=6E4)return(f/6E4).toFixed(1)+" minutes";return(f/1E3).toFixed(1)+" seconds";case "long":var g=[],h=f%36E5/6E4>>0;g.push(d("0",2,f%6E4/1E3>>0));if(f>=36E5){var i=f%864E5/36E5>>0;g.push(d("0",2,h));if(f>=864E5){g.push(d("0", 2,i));g.push(Math.floor(f/864E5).toFixed())}else g.push(i.toFixed())}else g.push(h.toFixed());return g.reverse().join(":")}}var d=pv.Format.pad;c.format=c;c.parse=function(f){switch(b){case "short":for(var g=/([0-9,.]+)\s*([a-z]+)/g,h,i=0;h=g.exec(f);){var j=parseFloat(h[0].replace(",","")),l=0;switch(h[2].toLowerCase()){case "year":case "years":l=31536E6;break;case "week":case "weeks":l=6048E5;break;case "day":case "days":l=864E5;break;case "hour":case "hours":l=36E5;break;case "minute":case "minutes":l= 6E4;break;case "second":case "seconds":l=1E3;break}i+=j*l}return i;case "long":h=f.replace(",","").split(":").reverse();i=0;if(h.length)i+=parseFloat(h[0])*1E3;if(h.length>1)i+=parseFloat(h[1])*6E4;if(h.length>2)i+=parseFloat(h[2])*36E5;if(h.length>3)i+=parseFloat(h[3])*864E5;return i}};return c}; pv.Format.number=function(){function b(n){if(Infinity>h)n=Math.round(n*i)/i;var m=String(Math.abs(n)).split("."),r=m[0];n=n<0?"-":"";if(r.length>d)r=r.substring(r.length-d);if(k&&r.length3)r=r.replace(/\B(?=(?:\d{3})+(?!\d))/g,o);if(!k&&r.lengthd)m=m.substring(m.length-d);n=n[1]?Number("0."+n[1]):0;if(Infinity>h)n=Math.round(n*i)/i;return Math.round(m)+n};b.integerDigits=function(n,m){if(arguments.length){c=Number(n);d=arguments.length>1?Number(m):c;f=c+Math.floor(c/3)*o.length;return this}return[c,d]};b.fractionDigits=function(n,m){if(arguments.length){g= Number(n);h=arguments.length>1?Number(m):g;i=Math.pow(10,h);return this}return[g,h]};b.integerPad=function(n){if(arguments.length){j=String(n);k=/\d/.test(j);return this}return j};b.fractionPad=function(n){if(arguments.length){l=String(n);return this}return l};b.decimal=function(n){if(arguments.length){q=String(n);return this}return q};b.group=function(n){if(arguments.length){o=n?String(n):"";f=c+Math.floor(c/3)*o.length;return this}return o};return b}; pv.map=function(b,c){var d={};return c?b.map(function(f,g){d.index=g;return c.call(d,f)}):b.slice()};pv.repeat=function(b,c){if(arguments.length==1)c=2;return pv.blend(pv.range(c).map(function(){return b}))};pv.cross=function(b,c){for(var d=[],f=0,g=b.length,h=c.length;fc){b.length=d;for(var f=c;fc?1:0}; pv.reverseOrder=function(b,c){return cb?1:0};pv.search=function(b,c,d){if(!d)d=pv.identity;for(var f=0,g=b.length-1;f<=g;){var h=f+g>>1,i=d(b[h]);if(ic)g=h-1;else return h}return-f-1};pv.search.index=function(b,c,d){b=pv.search(b,c,d);return b<0?-b-1:b}; pv.range=function(b,c,d){if(arguments.length==1){c=b;b=0}if(d==undefined)d=1;if((c-b)/d==Infinity)throw new Error("range must be finite");var f=[],g=0,h;if(d<0)for(;(h=b+d*g++)>c;)f.push(h);else for(;(h=b+d*g++)f){f=i;d=h}}return d}; pv.min=function(b,c){if(c==pv.index)return 0;return Math.min.apply(null,c?pv.map(b,c):b)};pv.min.index=function(b,c){if(!b.length)return-1;if(c==pv.index)return 0;if(!c)c=pv.identity;for(var d=0,f=Infinity,g={},h=0;h0?Math.pow(c,Math.floor(pv.log(b,c))):-Math.pow(c,-Math.floor(-pv.log(-b,c)))};pv.logCeil=function(b,c){return b>0?Math.pow(c,Math.ceil(pv.log(b,c))):-Math.pow(c,-Math.ceil(-pv.log(-b,c)))}; (function(){var b=Math.PI/180,c=180/Math.PI;pv.radians=function(d){return b*d};pv.degrees=function(d){return c*d}})();pv.keys=function(b){var c=[];for(var d in b)c.push(d);return c};pv.entries=function(b){var c=[];for(var d in b)c.push({key:d,value:b[d]});return c};pv.values=function(b){var c=[];for(var d in b)c.push(b[d]);return c};pv.dict=function(b,c){for(var d={},f={},g=0;g=94608E6){n=31536E6;t="%Y";p=function(w){w.setFullYear(w.getFullYear()+v)}}else if(u>=7776E6){n=2592E6;t="%m/%Y";p=function(w){w.setMonth(w.getMonth()+v)}}else if(u>=18144E5){n=6048E5;t="%m/%d";p=function(w){w.setDate(w.getDate()+7*v)}}else if(u>=2592E5){n=864E5;t="%m/%d";p=function(w){w.setDate(w.getDate()+v)}}else if(u>=108E5){n=36E5;t="%I:%M %p";p=function(w){w.setHours(w.getHours()+ v)}}else if(u>=18E4){n=6E4;t="%I:%M %p";p=function(w){w.setMinutes(w.getMinutes()+v)}}else if(u>=3E3){n=1E3;t="%I:%M:%S";p=function(w){w.setSeconds(w.getSeconds()+v)}}else{n=1;t="%S.%Qs";p=function(w){w.setTime(w.getTime()+v)}}q=pv.Format.date(t);s=new Date(s);t=[];x(s,n);u=u/n;if(u>10)switch(n){case 36E5:v=u>20?6:3;s.setHours(Math.floor(s.getHours()/v)*v);break;case 2592E6:v=3;s.setMonth(Math.floor(s.getMonth()/v)*v);break;case 6E4:v=u>30?15:u>15?10:5;s.setMinutes(Math.floor(s.getMinutes()/v)*v); break;case 1E3:v=u>90?15:u>60?10:5;s.setSeconds(Math.floor(s.getSeconds()/v)*v);break;case 1:v=u>1E3?250:u>200?100:u>100?50:u>50?25:5;s.setMilliseconds(Math.floor(s.getMilliseconds()/v)*v);break;default:v=pv.logCeil(u/15,10);if(u/v<2)v/=5;else if(u/v<5)v/=2;s.setFullYear(Math.floor(s.getFullYear()/v)*v);break}for(;;){p(s);if(s>m)break;t.push(new Date(s))}return r?t.reverse():t}arguments.length||(o=10);v=pv.logFloor(u/o,10);n=o/(u/v);if(n<=0.15)v*=10;else if(n<=0.35)v*=5;else if(n<=0.75)v*=2;n=Math.ceil(s/ v)*v;m=Math.floor(m/v)*v;q=pv.Format.number().fractionDigits(Math.max(0,-Math.floor(pv.log(v,10)+0.01)));m=pv.range(n,m+v,v);return r?m.reverse():m};c.tickFormat=function(o){return q(o)};c.nice=function(){if(d.length!=2)return this;var o=d[0],n=d[d.length-1],m=n0;i--)k.push(-g(-j)*i);else{for(;jh[1];l--);return k.slice(j,l)};b.tickFormat=function(h){return h.toPrecision(1)}; b.nice=function(){var h=b.domain();return b.domain(pv.logFloor(h[0],c),pv.logCeil(h[1],c))};b.base=function(h){if(arguments.length){c=Number(h);d=Math.log(c);b.transform(f,g);return this}return c};b.domain.apply(b,arguments);return b.base(10)};pv.Scale.root=function(){var b=pv.Scale.quantitative();b.power=function(c){if(arguments.length){var d=Number(c),f=1/d;b.transform(function(g){return Math.pow(g,f)},function(g){return Math.pow(g,d)});return this}return d};b.domain.apply(b,arguments);return b.power(2)}; pv.Scale.ordinal=function(){function b(g){g in d||(d[g]=c.push(g)-1);return f[d[g]%f.length]}var c=[],d={},f=[];b.domain=function(g,h){if(arguments.length){g=g instanceof Array?arguments.length>1?pv.map(g,h):g:Array.prototype.slice.call(arguments);c=[];for(var i={},j=0;j1?pv.map(g,h):g:Array.prototype.slice.call(arguments); if(typeof f[0]=="string")f=f.map(pv.color);return this}return f};b.split=function(g,h){var i=(h-g)/this.domain().length;f=pv.range(g+i/2,h,i);return this};b.splitFlush=function(g,h){var i=this.domain().length,j=(h-g)/(i-1);f=i==1?[(g+h)/2]:pv.range(g,h+j/2,j);return this};b.splitBanded=function(g,h,i){if(arguments.length<3)i=1;if(i<0){var j=this.domain().length;j=(h-g- -i*j)/(j+1);f=pv.range(g+j,h,j-i);f.band=-i}else{j=(h-g)/(this.domain().length+(1-i));f=pv.range(g+j*(1-i),h,j);f.band=j*i}return this}; b.by=function(g){function h(){return b(g.apply(this,arguments))}for(var i in b)h[i]=b[i];return h};b.domain.apply(b,arguments);return b}; pv.Scale.quantile=function(){function b(i){return h(Math.max(0,Math.min(d,pv.search.index(f,i)-1))/d)}var c=-1,d=-1,f=[],g=[],h=pv.Scale.linear();b.quantiles=function(i){if(arguments.length){c=Number(i);if(c<0){f=[g[0]].concat(g);d=g.length-1}else{f=[];f[0]=g[0];for(var j=1;j<=c;j++)f[j]=g[~~(j*(g.length-1)/c)];d=c-1}return this}return f};b.domain=function(i,j){if(arguments.length){g=i instanceof Array?pv.map(i,j):Array.prototype.slice.call(arguments);g.sort(pv.naturalOrder);b.quantiles(c);return this}return g}; b.range=function(){if(arguments.length){h.range.apply(h,arguments);return this}return h.range()};b.by=function(i){function j(){return b(i.apply(this,arguments))}for(var l in b)j[l]=b[l];return j};b.domain.apply(b,arguments);return b}; pv.histogram=function(b,c){var d=true;return{bins:function(f){var g=pv.map(b,c),h=[];arguments.length||(f=pv.Scale.linear(g).ticks());for(var i=0;i360)j-=360;else if(j<0)j+=360;if(j<60)return i+(h-i)*j/60;if(j<180)return h;if(j<240)return i+(h-i)*(240-j)/60;return i}function c(j){return Math.round(b(j)*255)}var d=this.h,f=this.s,g=this.l;d%=360;if(d<0)d+=360;f=Math.max(0,Math.min(f,1));g=Math.max(0,Math.min(g,1));var h=g<=0.5?g*(1+f):g+f-g*f,i=2*g-h;return pv.rgb(c(d+120),c(d),c(d-120),this.a)}; pv.Color.names={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400", darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc", ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a", lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1", moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57", seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32",transparent:pv.Color.transparent=pv.rgb(0,0,0,0)};(function(){var b=pv.Color.names;for(var c in b)b[c]=pv.color(b[c])})(); pv.colors=function(){var b=pv.Scale.ordinal();b.range.apply(b,arguments);return b};pv.Colors={};pv.Colors.category10=function(){var b=pv.colors("#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf");b.domain.apply(b,arguments);return b}; pv.Colors.category20=function(){var b=pv.colors("#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5");b.domain.apply(b,arguments);return b}; pv.Colors.category19=function(){var b=pv.colors("#9c9ede","#7375b5","#4a5584","#cedb9c","#b5cf6b","#8ca252","#637939","#e7cb94","#e7ba52","#bd9e39","#8c6d31","#e7969c","#d6616b","#ad494a","#843c39","#de9ed6","#ce6dbd","#a55194","#7b4173");b.domain.apply(b,arguments);return b};pv.ramp=function(){var b=pv.Scale.linear();b.range.apply(b,arguments);return b}; pv.Scene=pv.SvgScene={svg:"http://www.w3.org/2000/svg",xmlns:"http://www.w3.org/2000/xmlns",xlink:"http://www.w3.org/1999/xlink",xhtml:"http://www.w3.org/1999/xhtml",scale:1,events:["DOMMouseScroll","mousewheel","mousedown","mouseup","mouseover","mouseout","mousemove","click","dblclick"],implicit:{svg:{"shape-rendering":"auto","pointer-events":"painted",x:0,y:0,dy:0,"text-anchor":"start",transform:"translate(0,0)",fill:"none","fill-opacity":1,stroke:"none","stroke-opacity":1,"stroke-width":1.5,"stroke-linejoin":"miter"}, css:{font:"10px sans-serif"}}};pv.SvgScene.updateAll=function(b){if(b.length&&b[0].reverse&&b.type!="line"&&b.type!="area"){for(var c=pv.extend(b),d=0,f=b.length-1;f>=0;d++,f--)c[d]=b[f];b=c}this.removeSiblings(this[b.type](b))};pv.SvgScene.create=function(b){return document.createElementNS(this.svg,b)}; pv.SvgScene.expect=function(b,c,d,f){if(b){if(b.tagName=="a")b=b.firstChild;if(b.tagName!=c){c=this.create(c);b.parentNode.replaceChild(c,b);b=c}}else b=this.create(c);for(var g in d){c=d[g];if(c==this.implicit.svg[g])c=null;c==null?b.removeAttribute(g):b.setAttribute(g,c)}for(g in f){c=f[g];if(c==this.implicit.css[g])c=null;if(c==null)b.style.removeProperty(g);else b.style[g]=c}return b}; pv.SvgScene.append=function(b,c,d){b.$scene={scenes:c,index:d};b=this.title(b,c[d]);b.parentNode||c.$g.appendChild(b);return b.nextSibling};pv.SvgScene.title=function(b,c){var d=b.parentNode;if(d&&d.tagName!="a")d=null;if(c.title){if(!d){d=this.create("a");b.parentNode&&b.parentNode.replaceChild(d,b);d.appendChild(b)}d.setAttributeNS(this.xlink,"title",c.title);return d}d&&d.parentNode.replaceChild(b,d);return b}; pv.SvgScene.dispatch=pv.listener(function(b){var c=b.target.$scene;if(c){var d=b.type;switch(d){case "DOMMouseScroll":d="mousewheel";b.wheel=-480*b.detail;break;case "mousewheel":b.wheel=(window.opera?12:1)*b.wheelDelta;break}pv.Mark.dispatch(d,c.scenes,c.index)&&b.preventDefault()}});pv.SvgScene.removeSiblings=function(b){for(;b;){var c=b.nextSibling;b.parentNode.removeChild(b);b=c}};pv.SvgScene.undefined=function(){}; pv.SvgScene.pathBasis=function(){function b(f,g,h,i,j){return{x:f[0]*g.left+f[1]*h.left+f[2]*i.left+f[3]*j.left,y:f[0]*g.top+f[1]*h.top+f[2]*i.top+f[3]*j.top}}var c=[[1/6,2/3,1/6,0],[0,2/3,1/3,0],[0,1/3,2/3,0],[0,1/6,2/3,1/6]],d=function(f,g,h,i){var j=b(c[1],f,g,h,i),l=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"C"+j.x+","+j.y+","+l.x+","+l.y+","+f.x+","+f.y};d.segment=function(f,g,h,i){var j=b(c[0],f,g,h,i),l=b(c[1],f,g,h,i),k=b(c[2],f,g,h,i);f=b(c[3],f,g,h,i);return"M"+j.x+","+j.y+"C"+l.x+","+l.y+ ","+k.x+","+k.y+","+f.x+","+f.y};return d}();pv.SvgScene.curveBasis=function(b){if(b.length<=2)return"";var c="",d=b[0],f=d,g=d,h=b[1];c+=this.pathBasis(d,f,g,h);for(var i=2;i1){j=c[1];h=b[l];l++;f+="C"+(g.left+i.x)+","+(g.top+i.y)+","+(h.left-j.x)+","+(h.top-j.y)+","+h.left+","+h.top;for(g=2;g9){l=3/Math.sqrt(l);f[h]= l*i*d[h];f[h+1]=l*j*d[h]}}for(h=0;h2&&(g.interpolate=="basis"||g.interpolate=="cardinal"||g.interpolate=="monotone")?d:c)(k,q-1));k=q-1}}if(!j.length)return f;f=this.expect(f,"path",{"shape-rendering":g.antialias?null:"crispEdges","pointer-events":g.events,cursor:g.cursor,d:"M"+j.join("ZM")+"Z",fill:h.color,"fill-opacity":h.opacity|| null,stroke:i.color,"stroke-opacity":i.opacity||null,"stroke-width":i.opacity?g.lineWidth/this.scale:null});return this.append(f,b,0)}; pv.SvgScene.areaSegment=function(b){var c=b.$g.firstChild,d=b[0],f,g;if(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"){f=[];g=[];for(var h=0,i=b.length;h2&&(d.interpolate=="basis"||d.interpolate=="cardinal"||d.interpolate=="monotone"))switch(d.interpolate){case "basis":h+=this.curveBasis(b);break;case "cardinal":h+=this.curveCardinal(b,d.tension);break;case "monotone":h+=this.curveMonotone(b); break}else for(var i=1;i1)break;return"A"+f+","+f+" 0 0,"+d+" "+c.left+","+c.top;case "step-before":return"V"+c.top+"H"+c.left;case "step-after":return"H"+c.left+"V"+c.top}return"L"+c.left+","+c.top};pv.SvgScene.lineIntersect=function(b,c,d,f){return b.plus(c.times(d.minus(b).dot(f.perp())/c.dot(f.perp())))}; pv.SvgScene.pathJoin=function(b,c,d,f){var g=pv.vector(c.left,c.top);d=pv.vector(d.left,d.top);var h=d.minus(g),i=h.perp().norm(),j=i.times(c.lineWidth/(2*this.scale));c=g.plus(j);var l=d.plus(j),k=d.minus(j);j=g.minus(j);if(b&&b.visible){b=g.minus(b.left,b.top).perp().norm().plus(i);j=this.lineIntersect(g,b,j,h);c=this.lineIntersect(g,b,c,h)}if(f&&f.visible){f=pv.vector(f.left,f.top).minus(d).perp().norm().plus(i);k=this.lineIntersect(d,f,k,h);l=this.lineIntersect(d,f,l,h)}return"M"+c.x+","+c.y+ "L"+l.x+","+l.y+" "+k.x+","+k.y+" "+j.x+","+j.y}; pv.SvgScene.panel=function(b){for(var c=b.$g,d=c&&c.firstChild,f=0;f=2*Math.PI)i=i?"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":"M0,"+j+"A"+j+","+j+" 0 1,1 0,"+-j+"A"+j+","+j+" 0 1,1 0,"+j+"Z";else{var k=Math.min(f.startAngle,f.endAngle),q=Math.max(f.startAngle,f.endAngle), o=Math.cos(k),n=Math.cos(q);k=Math.sin(k);q=Math.sin(q);i=i?"M"+j*o+","+j*k+"A"+j+","+j+" 0 "+(l1?c:null)}; a.anchor=function(b){function c(g){for(var h=d,i=[];!(f=h.scene);){g=g.parent;i.push({index:g.index,childIndex:h.childIndex});h=h.parent}for(;i.length;){g=i.pop();f=f[g.index].children[g.childIndex]}if(d.hasOwnProperty("index")){i=pv.extend(f[d.index]);i.right=i.top=i.left=i.bottom=0;return[i]}return f}var d=this,f;b||(b="center");return(new pv.Anchor(this)).name(b).def("$mark.anchor",function(){f=this.scene.target=c(this)}).data(function(){return f.map(function(g){return g.data})}).visible(function(){return f[this.index].visible}).left(function(){var g= f[this.index],h=g.width||0;switch(this.name()){case "bottom":case "top":case "center":return g.left+h/2;case "left":return null}return g.left+h}).top(function(){var g=f[this.index],h=g.height||0;switch(this.name()){case "left":case "right":case "center":return g.top+h/2;case "top":return null}return g.top+h}).right(function(){var g=f[this.index];return this.name()=="left"?g.right+(g.width||0):null}).bottom(function(){var g=f[this.index];return this.name()=="top"?g.bottom+(g.height||0):null}).textAlign(function(){switch(this.name()){case "bottom":case "top":case "center":return"center"; case "right":return"right"}return"left"}).textBaseline(function(){switch(this.name()){case "right":case "left":case "center":return"middle";case "top":return"top"}return"bottom"})};a.anchorTarget=function(){return this.proto.anchorTarget()};a.margin=function(b){return this.left(b).right(b).top(b).bottom(b)};a.instance=function(b){var c=this.scene||this.parent.instance(-1).children[this.childIndex],d=!arguments.length||this.hasOwnProperty("index")?this.index:b;return c[d<0?c.length-1:d]};a.first=function(){return this.scene[0]}; a.last=function(){return this.scene[this.scene.length-1]};a.sibling=function(){return this.index==0?null:this.scene[this.index-1]};a.cousin=function(){var b=this.parent;return(b=b&&b.sibling())&&b.children?b.children[this.childIndex][this.index]:null}; a.render=function(){function b(i,j,l){i.scale=l;if(j=0;k--){var q=l[k];if(!(q.name in c)){c[q.name]=q;switch(q.name){case "data":f=q;break;case "visible":g=q;break;default:d[q.type].push(q);break}}}while(j=j.proto)}var c={},d=[[],[],[],[]],f,g;b(this);b(this.defaults);d[1].reverse();d[3].reverse();var h=this;do for(var i in h.properties)i in c||d[2].push(c[i]={name:i,type:2,value:null});while(h=h.proto);h=d[0].concat(d[1]);for(i=0;ih.id)d[g.name]={id:0,value:g.type&1?g.value.apply(this,c):g.value}}}d=this.binds.data;d=d.type&1?d.value.apply(this,c):d.value;c.unshift(null); b.length=d.length;for(f=0;f0;k--){n=m[k];n.scale=q;q*=n.scene[n.index].transform.k}if(o.children){k=0;for(m=o.children.length;k=3*Math.PI/2};pv.Wedge.prototype.buildImplied=function(b){if(b.angle==null)b.angle=b.endAngle-b.startAngle;else if(b.endAngle==null)b.endAngle=b.startAngle+b.angle;pv.Mark.prototype.buildImplied.call(this,b)};pv.simulation=function(b){return new pv.Simulation(b)};pv.Simulation=function(b){for(var c=0;c=s,t=q.y>=u;k.leaf=false;switch((t<<1)+x){case 0:k=k.c1||(k.c1=new pv.Quadtree.Node);break;case 1:k=k.c2||(k.c2=new pv.Quadtree.Node);break;case 2:k=k.c3||(k.c3=new pv.Quadtree.Node);break;case 3:k=k.c4||(k.c4=new pv.Quadtree.Node); break}if(x)o=s;else m=s;if(t)n=u;else r=u;c(k,q,o,n,m,r)}var f,g=Number.POSITIVE_INFINITY,h=g,i=Number.NEGATIVE_INFINITY,j=i;for(f=b;f;f=f.next){if(f.xi)i=f.x;if(f.y>j)j=f.y}f=i-g;var l=j-h;if(f>l)j=h+f;else i=g+l;this.xMin=g;this.yMin=h;this.xMax=i;this.yMax=j;this.root=new pv.Quadtree.Node;for(f=b;f;f=f.next)c(this.root,f,g,h,i,j)};pv.Quadtree.Node=function(){this.leaf=true;this.p=this.c4=this.c3=this.c2=this.c1=null};pv.Force={}; pv.Force.charge=function(b){function c(k){function q(m){c(m);k.cn+=m.cn;o+=m.cn*m.cx;n+=m.cn*m.cy}var o=0,n=0;k.cn=0;if(!k.leaf){k.c1&&q(k.c1);k.c2&&q(k.c2);k.c3&&q(k.c3);k.c4&&q(k.c4)}if(k.p){k.cn+=b;o+=b*k.p.x;n+=b*k.p.y}k.cx=o/k.cn;k.cy=n/k.cn}function d(k,q,o,n,m,r){var s=k.cx-q.x,u=k.cy-q.y,x=1/Math.sqrt(s*s+u*u);if(k.leaf&&k.p!=q||(m-o)*xg)x=g;k=k.cn*x*x*x;s=s*k;u=u*k;q.fx+=s;q.fy+=u}}else if(!k.leaf){var t=(o+m)*0.5,p=(n+r)*0.5;k.c1&&d(k.c1,q,o,n,t,p);k.c2&&d(k.c2,q,t,n, m,p);k.c3&&d(k.c3,q,o,p,t,r);k.c4&&d(k.c4,q,t,p,m,r);if(!(xg)x=g;if(k.p&&k.p!=q){k=b*x*x*x;s=s*k;u=u*k;q.fx+=s;q.fy+=u}}}}var f=2,g=1/f,h=500,i=1/h,j=0.9,l={};arguments.length||(b=-40);l.constant=function(k){if(arguments.length){b=Number(k);return l}return b};l.domain=function(k,q){if(arguments.length){f=Number(k);g=1/f;h=Number(q);i=1/h;return l}return[f,h]};l.theta=function(k){if(arguments.length){j=Number(k);return l}return j};l.apply=function(k,q){c(q.root);for(k=k;k;k=k.next)d(q.root, k,q.xMin,q.yMin,q.xMax,q.yMax)};return l};pv.Force.drag=function(b){var c={};arguments.length||(b=0.1);c.constant=function(d){if(arguments.length){b=d;return c}return b};c.apply=function(d){if(b)for(d=d;d;d=d.next){d.fx-=b*d.vx;d.fy-=b*d.vy}};return c}; pv.Force.spring=function(b){var c=0.1,d=20,f,g,h={};arguments.length||(b=0.1);h.links=function(i){if(arguments.length){f=i;g=i.map(function(j){return 1/Math.sqrt(Math.max(j.sourceNode.linkDegree,j.targetNode.linkDegree))});return h}return f};h.constant=function(i){if(arguments.length){b=Number(i);return h}return b};h.damping=function(i){if(arguments.length){c=Number(i);return h}return c};h.length=function(i){if(arguments.length){d=Number(i);return h}return d};h.apply=function(){for(var i=0;ig,p=sh){k.c1&&t&&c(k.c1,q,o,n,s,u);k.c2&&p&&c(k.c2,q,s,n,m,u)}if(x){k.c3&&t&&c(k.c3,q,o,u,s,r);k.c4&&p&&c(k.c4,q,s,u,m,r)}}if(k.p&&k.p!=q){o=q.x-k.p.x;n=q.y-k.p.y;m=Math.sqrt(o*o+n*n);r=f+b(k.p);if(mm)m=n}for(var r=0;rc.max?c.max:g.x;if(d)for(g=f;g;g=g.next)g.y=g.yd.max?d.max:g.y};return b};pv.Layout=function(){pv.Panel.call(this)};pv.Layout.prototype=pv.extend(pv.Panel); pv.Layout.prototype.property=function(b,c){if(!this.hasOwnProperty("properties"))this.properties=pv.extend(this.properties);this.properties[b]=true;this.propertyMethod(b,false,pv.Mark.cast[b]=c);return this}; pv.Layout.Network=function(){pv.Layout.call(this);var b=this;this.$id=pv.id();(this.node=(new pv.Mark).data(function(){return b.nodes()}).strokeStyle("#1f77b4").fillStyle("#fff").left(function(c){return c.x}).top(function(c){return c.y})).parent=this;this.link=(new pv.Mark).extend(this.node).data(function(c){return[c.sourceNode,c.targetNode]}).fillStyle(null).lineWidth(function(c,d){return d.linkValue*1.5}).strokeStyle("rgba(0,0,0,.2)");this.link.add=function(c){return b.add(pv.Panel).data(function(){return b.links()}).add(c).extend(this)}; (this.label=(new pv.Mark).extend(this.node).textMargin(7).textBaseline("middle").text(function(c){return c.nodeName||c.nodeValue}).textAngle(function(c){c=c.midAngle;return pv.Wedge.upright(c)?c:c+Math.PI}).textAlign(function(c){return pv.Wedge.upright(c.midAngle)?"left":"right"})).parent=this}; pv.Layout.Network.prototype=pv.extend(pv.Layout).property("nodes",function(b){return b.map(function(c,d){if(typeof c!="object")c={nodeValue:c};c.index=d;c.linkDegree=0;return c})}).property("links",function(b){return b.map(function(c){if(isNaN(c.linkValue))c.linkValue=isNaN(c.value)?1:c.value;return c})});pv.Layout.Network.prototype.reset=function(){this.$id=pv.id();return this}; pv.Layout.Network.prototype.buildProperties=function(b,c){if((b.$id||0)=this.$id)return true;b.$id=this.$id;b.links.forEach(function(c){var d=c.linkValue;(c.sourceNode||(c.sourceNode=b.nodes[c.source])).linkDegree+=d;(c.targetNode||(c.targetNode=b.nodes[c.target])).linkDegree+=d})}; pv.Layout.Hierarchy=function(){pv.Layout.Network.call(this);this.link.strokeStyle("#ccc")};pv.Layout.Hierarchy.prototype=pv.extend(pv.Layout.Network);pv.Layout.Hierarchy.prototype.buildImplied=function(b){if(!b.links)b.links=pv.Layout.Hierarchy.links.call(this);pv.Layout.Network.prototype.buildImplied.call(this,b)};pv.Layout.Hierarchy.links=function(){return this.nodes().filter(function(b){return b.parentNode}).map(function(b){return{sourceNode:b,targetNode:b.parentNode,linkValue:1}})}; pv.Layout.Hierarchy.NodeLink={buildImplied:function(b){function c(m){return m.parentNode?m.depth*(o-q)+q:0}function d(m){return m.parentNode?(m.breadth-0.25)*2*Math.PI:0}function f(m){switch(i){case "left":return m.depth*l;case "right":return l-m.depth*l;case "top":return m.breadth*l;case "bottom":return l-m.breadth*l;case "radial":return l/2+c(m)*Math.cos(m.midAngle)}}function g(m){switch(i){case "left":return m.breadth*k;case "right":return k-m.breadth*k;case "top":return m.depth*k;case "bottom":return k- m.depth*k;case "radial":return k/2+c(m)*Math.sin(m.midAngle)}}var h=b.nodes,i=b.orient,j=/^(top|bottom)$/.test(i),l=b.width,k=b.height;if(i=="radial"){var q=b.innerRadius,o=b.outerRadius;if(q==null)q=0;if(o==null)o=Math.min(l,k)/2}for(b=0;bb.dy?0:-Math.PI/2});(this.leaf=(new pv.Mark).extend(this.node).fillStyle(null).strokeStyle(null).visible(function(b){return!b.firstChild})).parent= this;delete this.link};pv.Layout.Treemap.prototype=pv.extend(pv.Layout.Hierarchy).property("round",Boolean).property("paddingLeft",Number).property("paddingRight",Number).property("paddingTop",Number).property("paddingBottom",Number).property("mode",String).property("order",String);a=pv.Layout.Treemap.prototype;a.defaults=(new pv.Layout.Treemap).extend(pv.Layout.Hierarchy.prototype.defaults).mode("squarify").order("ascending");a.padding=function(b){return this.paddingLeft(b).paddingRight(b).paddingTop(b).paddingBottom(b)}; a.$size=function(b){return Number(b.nodeValue)};a.size=function(b){this.$size=pv.functor(b);return this}; a.buildImplied=function(b){function c(r,s,u,x,t,p,v){for(var w=0,y=0;wu)u=v;t+=v}t*=t;s*=s;return Math.max(s*u/t,t/(s*x))}function f(r,s){function u(A){var D=p==y,G=pv.sum(A,o),E=y?n(G/y):0;c(A,G,D,x,t,D?p:E,D?E:v);if(D){t+=E;v-=E}else{x+= E;p-=E}y=Math.min(p,v);return D}var x=r.x+j,t=r.y+k,p=r.dx-j-l,v=r.dy-k-q;if(m!="squarify")c(r.childNodes,r.size,m=="slice"?true:m=="dice"?false:s&1,x,t,p,v);else{var w=[];s=Infinity;var y=Math.min(p,v),z=p*v/r.size;if(!(r.size<=0)){r.visitBefore(function(A){A.size*=z});for(r=r.childNodes.slice();r.length;){var C=r[r.length-1];if(C.size){w.push(C);z=d(w,y);if(z<=s){r.pop();s=z}else{w.pop();u(w);w.length=0;s=Infinity}}else r.pop()}if(u(w))for(s=0;s0){i(l(C,p,v),p,B);A+=B;D+=B}G+=C.mod;A+=y.mod;E+=w.mod;D+=z.mod;C=h(C);y=g(y)}if(C&&!h(z)){z.thread=C;z.mod+=G-D}if(y&&!g(w)){w.thread=y;w.mod+=A-E;v=p}}return v}function g(p){return p.firstChild||p.thread}function h(p){return p.lastChild||p.thread}function i(p,v,w){var y=v.number-p.number;v.change-=w/y;v.shift+=w;p.change+= w/y;v.prelim+=w;v.mod+=w}function j(p){var v=0,w=0;for(p=p.lastChild;p;p=p.previousSibling){p.prelim+=v;p.mod+=v;w+=p.change;v+=p.shift+w}}function l(p,v,w){return p.ancestor.parentNode==v.parentNode?p.ancestor:w}function k(p,v){return(v?1:u+1)/(m=="radial"?p:1)}function q(p){return m=="radial"?p.breadth/r:0}function o(p){switch(m){case "left":return p.depth;case "right":return x-p.depth;case "top":case "bottom":return p.breadth+x/2;case "radial":return x/2+p.depth*Math.cos(q(p))}}function n(p){switch(m){case "left":case "right":return p.breadth+ t/2;case "top":return p.depth;case "bottom":return t-p.depth;case "radial":return t/2+p.depth*Math.sin(q(p))}}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var m=b.orient,r=b.depth,s=b.breadth,u=b.group,x=b.width,t=b.height;b=b.nodes[0];b.visitAfter(function(p,v){p.ancestor=p;p.prelim=0;p.mod=0;p.change=0;p.shift=0;p.number=p.previousSibling?p.previousSibling.number+1:0;p.depth=v});c(b);d(b,-b.prelim,0);b.visitAfter(function(p){p.breadth*=s;p.depth*=r;p.midAngle=q(p);p.x=o(p);p.y=n(p); if(p.firstChild)p.midAngle+=Math.PI;delete p.breadth;delete p.depth;delete p.ancestor;delete p.prelim;delete p.mod;delete p.change;delete p.shift;delete p.number;delete p.thread})}};pv.Layout.Indent=function(){pv.Layout.Hierarchy.call(this);this.link.interpolate("step-after")};pv.Layout.Indent.prototype=pv.extend(pv.Layout.Hierarchy).property("depth",Number).property("breadth",Number);pv.Layout.Indent.prototype.defaults=(new pv.Layout.Indent).extend(pv.Layout.Hierarchy.prototype.defaults).depth(15).breadth(15); pv.Layout.Indent.prototype.buildImplied=function(b){function c(i,j,l){i.x=g+l++*f;i.y=h+j++*d;i.midAngle=0;for(i=i.firstChild;i;i=i.nextSibling)j=c(i,j,l);return j}if(!pv.Layout.Hierarchy.prototype.buildImplied.call(this,b)){var d=b.breadth,f=b.depth,g=0,h=0;c(b.nodes[0],1,1)}};pv.Layout.Pack=function(){pv.Layout.Hierarchy.call(this);this.node.radius(function(b){return b.radius}).strokeStyle("rgb(31, 119, 180)").fillStyle("rgba(31, 119, 180, .25)");this.label.textAlign("center");delete this.link}; pv.Layout.Pack.prototype=pv.extend(pv.Layout.Hierarchy).property("spacing",Number).property("order",String);pv.Layout.Pack.prototype.defaults=(new pv.Layout.Pack).extend(pv.Layout.Hierarchy.prototype.defaults).spacing(1).order("ascending");pv.Layout.Pack.prototype.$radius=function(){return 1};pv.Layout.Pack.prototype.size=function(b){this.$radius=typeof b=="function"?function(){return Math.sqrt(b.apply(this,arguments))}:(b=Math.sqrt(b),function(){return b});return this}; pv.Layout.Pack.prototype.buildImplied=function(b){function c(o){var n=pv.Mark.stack;n.unshift(null);for(var m=0,r=o.length;m0.0010}var u=Infinity,x=-Infinity,t=Infinity,p=-Infinity,v,w,y,z,C;v=o[0];v.x=-v.radius;v.y=0;n(v);if(o.length>1){w=o[1];w.x=w.radius;w.y=0;n(w);if(o.length>2){y=o[2];g(v,w,y);n(y);m(v,y);v.p= y;m(y,w);w=v.n;for(var A=3;A0){r(v,z);w=z;A--}else if(D<0){r(z,w);v=z;A--}}}}v=(u+x)/2;w=(t+p)/2;for(A=y=0;Ao.min){o.sim.step(); q=true}q&&d.render()},42)}else for(l=0;lg)g=j;i.size=i.firstChild?pv.sum(i.childNodes,function(l){return l.size}):c.$size.apply(c,(f[0]=i,f))});f.shift();switch(b.order){case "ascending":d.sort(function(i,j){return i.size-j.size});break;case "descending":d.sort(function(i,j){return j.size-i.size});break}var h=1/g;d.minBreadth=0;d.breadth= 0.5;d.maxBreadth=1;d.visitBefore(function(i){for(var j=i.minBreadth,l=i.maxBreadth-j,k=i.firstChild;k;k=k.nextSibling){k.minBreadth=j;k.maxBreadth=j+=k.size/i.size*l;k.breadth=(j+k.minBreadth)/2}});d.visitAfter(function(i,j){i.minDepth=(j-1)*h;i.maxDepth=i.depth=j*h});pv.Layout.Hierarchy.NodeLink.buildImplied.call(this,b)}};pv.Layout.Partition.Fill=function(){pv.Layout.Partition.call(this);pv.Layout.Hierarchy.Fill.constructor.call(this)};pv.Layout.Partition.Fill.prototype=pv.extend(pv.Layout.Partition); pv.Layout.Partition.Fill.prototype.buildImplied=function(b){pv.Layout.Partition.prototype.buildImplied.call(this,b)||pv.Layout.Hierarchy.Fill.buildImplied.call(this,b)};pv.Layout.Arc=function(){pv.Layout.Network.call(this);var b,c,d,f=this.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.directed;b=g.orient=="radial"?"linear":"polar";d=g.orient=="right"||g.orient=="top"};this.link.data(function(g){var h=g.sourceNode;g=g.targetNode;return d!=(c||h.breadth>1)*f:null}).bottom(function(l,k){return d=="mirror"?k&1?null:(k+1>>1)*-f:(k&1||-1)*(k+1>>1)*f}).fillStyle(function(l,k){return(k&1?h:i)((k>>1)+1)});this.band.add=function(l){return b.add(pv.Panel).extend(c).add(l).extend(this)}};pv.Layout.Horizon.prototype=pv.extend(pv.Layout).property("bands",Number).property("mode",String).property("backgroundStyle",pv.color).property("positiveStyle",pv.color).property("negativeStyle",pv.color); pv.Layout.Horizon.prototype.defaults=(new pv.Layout.Horizon).extend(pv.Layout.prototype.defaults).bands(2).mode("offset").backgroundStyle("white").positiveStyle("#1f77b4").negativeStyle("#d62728"); pv.Layout.Rollup=function(){pv.Layout.Network.call(this);var b=this,c,d,f=b.buildImplied;this.buildImplied=function(g){f.call(this,g);c=g.$rollup.nodes;d=g.$rollup.links};this.node.data(function(){return c}).size(function(g){return g.nodes.length*20});this.link.interpolate("polar").eccentricity(0.8);this.link.add=function(g){return b.add(pv.Panel).data(function(){return d}).add(g).extend(this)}};pv.Layout.Rollup.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean); pv.Layout.Rollup.prototype.x=function(b){this.$x=pv.functor(b);return this};pv.Layout.Rollup.prototype.y=function(b){this.$y=pv.functor(b);return this}; pv.Layout.Rollup.prototype.buildImplied=function(b){function c(r){return i[r]+","+j[r]}if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var d=b.nodes,f=b.links,g=b.directed,h=d.length,i=[],j=[],l=0,k={},q={},o=pv.Mark.stack,n={parent:this};o.unshift(null);for(var m=0;ml.index?l.index+","+d.index:d.index+","+l.index;(o=q[h])||(o=q[h]={sourceNode:d,targetNode:l,linkValue:0,links:[]});o.links.push(f[m]);o.linkValue+=f[m].linkValue}b.$rollup={nodes:pv.values(k),links:pv.values(q)}}}; pv.Layout.Matrix=function(){pv.Layout.Network.call(this);var b,c,d,f,g,h=this.buildImplied;this.buildImplied=function(i){h.call(this,i);b=i.nodes.length;c=i.width/b;d=i.height/b;f=i.$matrix.labels;g=i.$matrix.pairs};this.link.data(function(){return g}).left(function(){return c*(this.index%b)}).top(function(){return d*Math.floor(this.index/b)}).width(function(){return c}).height(function(){return d}).lineWidth(1.5).strokeStyle("#fff").fillStyle(function(i){return i.linkValue?"#555":"#eee"}).parent= this;delete this.link.add;this.label.data(function(){return f}).left(function(){return this.index&1?c*((this.index>>1)+0.5):null}).top(function(){return this.index&1?null:d*((this.index>>1)+0.5)}).textMargin(4).textAlign(function(){return this.index&1?"left":"right"}).textAngle(function(){return this.index&1?-Math.PI/2:0});delete this.node};pv.Layout.Matrix.prototype=pv.extend(pv.Layout.Network).property("directed",Boolean);pv.Layout.Matrix.prototype.sort=function(b){this.$sort=b;return this}; pv.Layout.Matrix.prototype.buildImplied=function(b){if(!pv.Layout.Network.prototype.buildImplied.call(this,b)){var c=b.nodes,d=b.links,f=this.$sort,g=c.length,h=pv.range(g),i=[],j=[],l={};b.$matrix={labels:i,pairs:j};f&&h.sort(function(m,r){return f(c[m],c[r])});for(var k=0;kl)k=null;if(g){if(k&&g.scene==k.scene&&g.index==k.index)return;pv.Mark.dispatch("unpoint",g.scene,g.index)}if(g=k){pv.Mark.dispatch("point",k.scene,k.index);pv.listen(this.root.canvas(),"mouseout",f)}}function f(k){if(g&&!pv.ancestor(this,k.relatedTarget)){pv.Mark.dispatch("unpoint",g.scene,g.index);g=null}}var g,h=null,i=1,j=1,l=arguments.length?b*b:900;d.collapse=function(k){if(arguments.length){h=String(k);switch(h){case "y":i= 1;j=0;break;case "x":i=0;j=1;break;default:j=i=1;break}return d}return h};return d}; pv.Behavior.select=function(){function b(j){g=this.index;f=this.scene;i=this.mouse();h=j;h.x=i.x;h.y=i.y;h.dx=h.dy=0;pv.Mark.dispatch("selectstart",f,g)}function c(){if(f){f.mark.context(f,g,function(){var j=this.mouse();h.x=Math.max(0,Math.min(i.x,j.x));h.y=Math.max(0,Math.min(i.y,j.y));h.dx=Math.min(this.width(),Math.max(j.x,i.x))-h.x;h.dy=Math.min(this.height(),Math.max(j.y,i.y))-h.y;this.render()});pv.Mark.dispatch("select",f,g)}}function d(){if(f){pv.Mark.dispatch("selectend",f,g);f=null}}var f, g,h,i;pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b}; pv.Behavior.resize=function(b){function c(l){h=this.index;g=this.scene;j=this.mouse();i=l;switch(b){case "left":j.x=i.x+i.dx;break;case "right":j.x=i.x;break;case "top":j.y=i.y+i.dy;break;case "bottom":j.y=i.y;break}pv.Mark.dispatch("resizestart",g,h)}function d(){if(g){g.mark.context(g,h,function(){var l=this.mouse();i.x=Math.max(0,Math.min(j.x,l.x));i.y=Math.max(0,Math.min(j.y,l.y));i.dx=Math.min(this.parent.width(),Math.max(l.x,j.x))-i.x;i.dy=Math.min(this.parent.height(),Math.max(l.y,j.y))-i.y; this.render()});pv.Mark.dispatch("resize",g,h)}}function f(){if(g){pv.Mark.dispatch("resizeend",g,h);g=null}}var g,h,i,j;pv.listen(window,"mousemove",d);pv.listen(window,"mouseup",f);return c}; pv.Behavior.pan=function(){function b(){g=this.index;f=this.scene;i=pv.vector(pv.event.pageX,pv.event.pageY);h=this.transform();j=1/(h.k*this.scale);if(l)l={x:(1-h.k)*this.width(),y:(1-h.k)*this.height()}}function c(){if(f){f.mark.context(f,g,function(){var k=h.translate((pv.event.pageX-i.x)*j,(pv.event.pageY-i.y)*j);if(l){k.x=Math.max(l.x,Math.min(0,k.x));k.y=Math.max(l.y,Math.min(0,k.y))}this.transform(k).render()});pv.Mark.dispatch("pan",f,g)}}function d(){f=null}var f,g,h,i,j,l;b.bound=function(k){if(arguments.length){l= Boolean(k);return this}return Boolean(l)};pv.listen(window,"mousemove",c);pv.listen(window,"mouseup",d);return b}; pv.Behavior.zoom=function(b){function c(){var f=this.mouse(),g=pv.event.wheel*b;f=this.transform().translate(f.x,f.y).scale(g<0?1E3/(1E3-g):(1E3+g)/1E3).translate(-f.x,-f.y);if(d){f.k=Math.max(1,f.k);f.x=Math.max((1-f.k)*this.width(),Math.min(0,f.x));f.y=Math.max((1-f.k)*this.height(),Math.min(0,f.y))}this.transform(f).render();pv.Mark.dispatch("zoom",this.scene,this.index)}var d;arguments.length||(b=1/48);c.bound=function(f){if(arguments.length){d=Boolean(f);return this}return Boolean(d)};return c}; pv.Geo=function(){}; pv.Geo.projections={mercator:{project:function(b){return{x:b.lng/180,y:b.lat>85?1:b.lat<-85?-1:Math.log(Math.tan(Math.PI/4+pv.radians(b.lat)/2))/Math.PI}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(2*Math.atan(Math.exp(b.y*Math.PI))-Math.PI/2)}}},"gall-peters":{project:function(b){return{x:b.lng/180,y:Math.sin(pv.radians(b.lat))}},invert:function(b){return{lng:b.x*180,lat:pv.degrees(Math.asin(b.y))}}},sinusoidal:{project:function(b){return{x:pv.radians(b.lng)*Math.cos(pv.radians(b.lat))/Math.PI, y:b.lat/90}},invert:function(b){return{lng:pv.degrees(b.x*Math.PI/Math.cos(b.y*Math.PI/2)),lat:b.y*90}}},aitoff:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat);var d=Math.acos(Math.cos(b)*Math.cos(c/2));return{x:2*(d?Math.cos(b)*Math.sin(c/2)*d/Math.sin(d):0)/Math.PI,y:2*(d?Math.sin(b)*d/Math.sin(d):0)/Math.PI}},invert:function(b){var c=b.y*Math.PI/2;return{lng:pv.degrees(b.x*Math.PI/2/Math.cos(c)),lat:pv.degrees(c)}}},hammer:{project:function(b){var c=pv.radians(b.lng);b=pv.radians(b.lat); var d=Math.sqrt(1+Math.cos(b)*Math.cos(c/2));return{x:2*Math.SQRT2*Math.cos(b)*Math.sin(c/2)/d/3,y:Math.SQRT2*Math.sin(b)/d/1.5}},invert:function(b){var c=b.x*3;b=b.y*1.5;var d=Math.sqrt(1-c*c/16-b*b/4);return{lng:pv.degrees(2*Math.atan2(d*c,2*(2*d*d-1))),lat:pv.degrees(Math.asin(d*b))}}},identity:{project:function(b){return{x:b.lng/180,y:b.lat/90}},invert:function(b){return{lng:b.x*180,lat:b.y*90}}}}; pv.Geo.scale=function(b){function c(m){if(!o||m.lng!=o.lng||m.lat!=o.lat){o=m;m=d(m);n={x:l(m.x),y:k(m.y)}}return n}function d(m){return j.project({lng:m.lng-q.lng,lat:m.lat})}function f(m){m=j.invert(m);m.lng+=q.lng;return m}var g={x:0,y:0},h={x:1,y:1},i=[],j=pv.Geo.projections.identity,l=pv.Scale.linear(-1,1).range(0,1),k=pv.Scale.linear(-1,1).range(1,0),q={lng:0,lat:0},o,n;c.x=function(m){return c(m).x};c.y=function(m){return c(m).y};c.ticks={lng:function(m){var r;if(i.length>1){var s=pv.Scale.linear(); if(m==undefined)m=10;r=s.domain(i,function(u){return u.lat}).ticks(m);m=s.domain(i,function(u){return u.lng}).ticks(m)}else{r=pv.range(-80,81,10);m=pv.range(-180,181,10)}return m.map(function(u){return r.map(function(x){return{lat:x,lng:u}})})},lat:function(m){return pv.transpose(c.ticks.lng(m))}};c.invert=function(m){return f({x:l.invert(m.x),y:k.invert(m.y)})};c.domain=function(m,r){if(arguments.length){i=m instanceof Array?arguments.length>1?pv.map(m,r):m:Array.prototype.slice.call(arguments); if(i.length>1){var s=i.map(function(x){return x.lng}),u=i.map(function(x){return x.lat});q={lng:(pv.max(s)+pv.min(s))/2,lat:(pv.max(u)+pv.min(u))/2};s=i.map(d);l.domain(s,function(x){return x.x});k.domain(s,function(x){return x.y})}else{q={lng:0,lat:0};l.domain(-1,1);k.domain(-1,1)}o=null;return this}return i};c.range=function(m,r){if(arguments.length){if(typeof m=="object"){g={x:Number(m.x),y:Number(m.y)};h={x:Number(r.x),y:Number(r.y)}}else{g={x:0,y:0};h={x:Number(m),y:Number(r)}}l.range(g.x,h.x); k.range(h.y,g.y);o=null;return this}return[g,h]};c.projection=function(m){if(arguments.length){j=typeof m=="string"?pv.Geo.projections[m]||pv.Geo.projections.identity:m;return this.domain(i)}return m};c.by=function(m){function r(){return c(m.apply(this,arguments))}for(var s in c)r[s]=c[s];return r};arguments.length&&c.projection(b);return c}; ganglia-web-3.6.1/js/rickshaw.js000066400000000000000000001707111231750357400165010ustar00rootroot00000000000000Rickshaw = { namespace: function(namespace, obj) { var parts = namespace.split('.'); // for rudimentary compatibility w/ node var root = typeof global != 'undefined' ? global : window; var parent = root.Rickshaw; for(var i = 1, length = parts.length; i < length; i++) { currentPart = parts[i]; parent[currentPart] = parent[currentPart] || {}; parent = parent[currentPart]; } return parent; }, keys: function(obj) { var keys = []; for (var key in obj) keys.push(key); return keys; }, extend: function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; } }; /* Adapted from https://github.com/Jakobo/PTClass */ /* Copyright (c) 2005-2010 Sam Stephenson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Based on Alex Arnell's inheritance implementation. */ /** section: Language * class Class * * Manages Prototype's class-based OOP system. * * Refer to Prototype's web site for a [tutorial on classes and * inheritance](http://prototypejs.org/learn/class-inheritance). **/ (function(globalContext) { /* ------------------------------------ */ /* Import from object.js */ /* ------------------------------------ */ var _toString = Object.prototype.toString, NULL_TYPE = 'Null', UNDEFINED_TYPE = 'Undefined', BOOLEAN_TYPE = 'Boolean', NUMBER_TYPE = 'Number', STRING_TYPE = 'String', OBJECT_TYPE = 'Object', FUNCTION_CLASS = '[object Function]'; function isFunction(object) { return _toString.call(object) === FUNCTION_CLASS; } function extend(destination, source) { for (var property in source) if (source.hasOwnProperty(property)) // modify protect primitive slaughter destination[property] = source[property]; return destination; } function keys(object) { if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } var results = []; for (var property in object) { if (object.hasOwnProperty(property)) { results.push(property); } } return results; } function Type(o) { switch(o) { case null: return NULL_TYPE; case (void 0): return UNDEFINED_TYPE; } var type = typeof o; switch(type) { case 'boolean': return BOOLEAN_TYPE; case 'number': return NUMBER_TYPE; case 'string': return STRING_TYPE; } return OBJECT_TYPE; } function isUndefined(object) { return typeof object === "undefined"; } /* ------------------------------------ */ /* Import from Function.js */ /* ------------------------------------ */ var slice = Array.prototype.slice; function argumentNames(fn) { var names = fn.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') .replace(/\s+/g, '').split(','); return names.length == 1 && !names[0] ? [] : names; } function wrap(fn, wrapper) { var __method = fn; return function() { var a = update([bind(__method, this)], arguments); return wrapper.apply(this, a); } } function update(array, args) { var arrayLength = array.length, length = args.length; while (length--) array[arrayLength + length] = args[length]; return array; } function merge(array, args) { array = slice.call(array, 0); return update(array, args); } function bind(fn, context) { if (arguments.length < 2 && isUndefined(arguments[0])) return this; var __method = fn, args = slice.call(arguments, 2); return function() { var a = merge(args, arguments); return __method.apply(context, a); } } /* ------------------------------------ */ /* Import from Prototype.js */ /* ------------------------------------ */ var emptyFunction = function(){}; var Class = (function() { // Some versions of JScript fail to enumerate over properties, names of which // correspond to non-enumerable properties in the prototype chain var IS_DONTENUM_BUGGY = (function(){ for (var p in { toString: 1 }) { // check actual property name, so that it works with augmented Object.prototype if (p === 'toString') return false; } return true; })(); function subclass() {}; function create() { var parent = null, properties = [].slice.apply(arguments); if (isFunction(properties[0])) parent = properties.shift(); function klass() { this.initialize.apply(this, arguments); } extend(klass, Class.Methods); klass.superclass = parent; klass.subclasses = []; if (parent) { subclass.prototype = parent.prototype; klass.prototype = new subclass; try { parent.subclasses.push(klass) } catch(e) {} } for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) klass.prototype.initialize = emptyFunction; klass.prototype.constructor = klass; return klass; } function addMethods(source) { var ancestor = this.superclass && this.superclass.prototype, properties = keys(source); // IE6 doesn't enumerate `toString` and `valueOf` (among other built-in `Object.prototype`) properties, // Force copy if they're not Object.prototype ones. // Do not copy other Object.prototype.* for performance reasons if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) properties.push("valueOf"); } for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && isFunction(value) && argumentNames(value)[0] == "$super") { var method = value; value = wrap((function(m) { return function() { return ancestor[m].apply(this, arguments); }; })(property), method); value.valueOf = bind(method.valueOf, method); value.toString = bind(method.toString, method); } this.prototype[property] = value; } return this; } return { create: create, Methods: { addMethods: addMethods } }; })(); if (globalContext.exports) { globalContext.exports.Class = Class; } else { globalContext.Class = Class; } })(Rickshaw); Rickshaw.namespace('Rickshaw.Compat.ClassList'); Rickshaw.Compat.ClassList = function() { /* adapted from http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) { (function (view) { "use strict"; var classListProp = "classList" , protoProp = "prototype" , elemCtrProto = (view.HTMLElement || view.Element)[protoProp] , objCtr = Object , strTrim = String[protoProp].trim || function () { return this.replace(/^\s+|\s+$/g, ""); } , arrIndexOf = Array[protoProp].indexOf || function (item) { var i = 0 , len = this.length ; for (; i < len; i++) { if (i in this && this[i] === item) { return i; } } return -1; } // Vendors: please allow content code to instantiate DOMExceptions , DOMEx = function (type, message) { this.name = type; this.code = DOMException[type]; this.message = message; } , checkTokenAndGetIndex = function (classList, token) { if (token === "") { throw new DOMEx( "SYNTAX_ERR" , "An invalid or illegal string was specified" ); } if (/\s/.test(token)) { throw new DOMEx( "INVALID_CHARACTER_ERR" , "String contains an invalid character" ); } return arrIndexOf.call(classList, token); } , ClassList = function (elem) { var trimmedClasses = strTrim.call(elem.className) , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] , i = 0 , len = classes.length ; for (; i < len; i++) { this.push(classes[i]); } this._updateClassName = function () { elem.className = this.toString(); }; } , classListProto = ClassList[protoProp] = [] , classListGetter = function () { return new ClassList(this); } ; // Most DOMException implementations don't allow calling DOMException's toString() // on non-DOMExceptions. Error's toString() is sufficient here. DOMEx[protoProp] = Error[protoProp]; classListProto.item = function (i) { return this[i] || null; }; classListProto.contains = function (token) { token += ""; return checkTokenAndGetIndex(this, token) !== -1; }; classListProto.add = function (token) { token += ""; if (checkTokenAndGetIndex(this, token) === -1) { this.push(token); this._updateClassName(); } }; classListProto.remove = function (token) { token += ""; var index = checkTokenAndGetIndex(this, token); if (index !== -1) { this.splice(index, 1); this._updateClassName(); } }; classListProto.toggle = function (token) { token += ""; if (checkTokenAndGetIndex(this, token) === -1) { this.add(token); } else { this.remove(token); } }; classListProto.toString = function () { return this.join(" "); }; if (objCtr.defineProperty) { var classListPropDesc = { get: classListGetter , enumerable: true , configurable: true }; try { objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } catch (ex) { // IE 8 doesn't support enumerable:true if (ex.number === -0x7FF5EC54) { classListPropDesc.enumerable = false; objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } } } else if (objCtr[protoProp].__defineGetter__) { elemCtrProto.__defineGetter__(classListProp, classListGetter); } }(self)); } }; if ( (typeof RICKSHAW_NO_COMPAT !== "undefined" && !RICKSHAW_NO_COMPAT) || typeof RICKSHAW_NO_COMPAT === "undefined") { new Rickshaw.Compat.ClassList(); } Rickshaw.namespace('Rickshaw.Graph'); Rickshaw.Graph = function(args) { this.element = args.element; this.series = args.series; this.defaults = { interpolation: 'cardinal', offset: 'zero', min: undefined, max: undefined, }; Rickshaw.keys(this.defaults).forEach( function(k) { this[k] = args[k] || this.defaults[k]; }, this ); this.window = {}; this.updateCallbacks = []; var self = this; this.initialize = function(args) { this.validateSeries(args.series); this.series.active = function() { return self.series.filter( function(s) { return !s.disabled } ) }; this.setSize({ width: args.width, height: args.height }); this.element.classList.add('rickshaw_graph'); this.vis = d3.select(this.element) .append("svg:svg") .attr('width', this.width) .attr('height', this.height); var renderers = [ Rickshaw.Graph.Renderer.Stack, Rickshaw.Graph.Renderer.Line, Rickshaw.Graph.Renderer.Bar, Rickshaw.Graph.Renderer.Area, Rickshaw.Graph.Renderer.ScatterPlot ]; renderers.forEach( function(r) { if (!r) return; self.registerRenderer(new r( { graph: self } )); } ); this.setRenderer(args.renderer || 'stack', args); this.discoverRange(); }; this.validateSeries = function(series) { if (!(series instanceof Array) && !(series instanceof Rickshaw.Series)) { var seriesSignature = Object.prototype.toString.apply(series); throw "series is not an array: " + seriesSignature; } var pointsCount; series.forEach( function(s) { if (!(s instanceof Object)) { throw "series element is not an object: " + s; } if (!(s.data)) { throw "series has no data: " + JSON.stringify(s); } if (!(s.data instanceof Array)) { throw "series data is not an array: " + JSON.stringify(s.data); } pointsCount = pointsCount || s.data.length; if (pointsCount && s.data.length != pointsCount) { throw "series cannot have differing numbers of points: " + pointsCount + " vs " + s.data.length + "; see Rickshaw.Series.zeroFill()"; } var dataTypeX = typeof s.data[0].x; var dataTypeY = typeof s.data[0].y; if (dataTypeX != 'number' || dataTypeY != 'number') { throw "x and y properties of points should be numbers instead of " + dataTypeX + " and " + dataTypeY; } } ); }; this.dataDomain = function() { // take from the first series var data = this.series[0].data; return [ data[0].x, data.slice(-1).shift().x ]; }; this.discoverRange = function() { var domain = this.renderer.domain(); this.x = d3.scale.linear().domain(domain.x).range([0, this.width]); this.y = d3.scale.linear().domain(domain.y).range([this.height, 0]); this.y.magnitude = d3.scale.linear().domain(domain.y).range([0, this.height]); }; this.render = function() { var stackedData = this.stackData(); this.discoverRange(); this.renderer.render(); this.updateCallbacks.forEach( function(callback) { callback(); } ); }; this.update = this.render; this.stackData = function() { var data = this.series.active() .map( function(d) { return d.data } ) .map( function(d) { return d.filter( function(d) { return this._slice(d) }, this ) }, this); this.stackData.hooks.data.forEach( function(entry) { data = entry.f.apply(self, [data]); } ); var layout = d3.layout.stack(); layout.offset( self.offset ); var stackedData = layout(data); this.stackData.hooks.after.forEach( function(entry) { stackedData = entry.f.apply(self, [data]); } ); var i = 0; this.series.forEach( function(series) { if (series.disabled) return; series.stack = stackedData[i++]; } ); this.stackedData = stackedData; return stackedData; }; this.stackData.hooks = { data: [], after: [] }; this._slice = function(d) { if (this.window.xMin || this.window.xMax) { var isInRange = true; if (this.window.xMin && d.x < this.window.xMin) isInRange = false; if (this.window.xMax && d.x > this.window.xMax) isInRange = false; return isInRange; } return true; }; this.onUpdate = function(callback) { this.updateCallbacks.push(callback); }; this.registerRenderer = function(renderer) { this._renderers = this._renderers || {}; this._renderers[renderer.name] = renderer; }; this.configure = function(args) { if (args.width || args.height) { this.setSize(args); } Rickshaw.keys(this.defaults).forEach( function(k) { this[k] = args[k] || this.defaults[k]; }, this ); this.setRenderer(args.renderer || this.renderer.name, args); }; this.setRenderer = function(name, args) { if (!this._renderers[name]) { throw "couldn't find renderer " + name; } this.renderer = this._renderers[name]; if (typeof args == 'object') { this.renderer.configure(args); } }; this.setSize = function(args) { args = args || {}; if (typeof window !== undefined) { var style = window.getComputedStyle(this.element, null); var elementWidth = parseInt(style.getPropertyValue('width')); var elementHeight = parseInt(style.getPropertyValue('height')); } this.width = args.width || elementWidth || 400; this.height = args.height || elementHeight || 250; this.vis && this.vis .attr('width', this.width) .attr('height', this.height); } this.initialize(args); }; Rickshaw.namespace('Rickshaw.Fixtures.Color'); Rickshaw.Fixtures.Color = function() { this.schemes = {}; this.schemes.spectrum14 = [ '#ecb796', '#dc8f70', '#b2a470', '#92875a', '#716c49', '#d2ed82', '#bbe468', '#a1d05d', '#e7cbe6', '#d8aad6', '#a888c2', '#9dc2d3', '#649eb9', '#387aa3' ].reverse(); this.schemes.spectrum2000 = [ '#57306f', '#514c76', '#646583', '#738394', '#6b9c7d', '#84b665', '#a7ca50', '#bfe746', '#e2f528', '#fff726', '#ecdd00', '#d4b11d', '#de8800', '#de4800', '#c91515', '#9a0000', '#7b0429', '#580839', '#31082b' ]; this.schemes.spectrum2001 = [ '#2f243f', '#3c2c55', '#4a3768', '#565270', '#6b6b7c', '#72957f', '#86ad6e', '#a1bc5e', '#b8d954', '#d3e04e', '#ccad2a', '#cc8412', '#c1521d', '#ad3821', '#8a1010', '#681717', '#531e1e', '#3d1818', '#320a1b' ]; this.schemes.classic9 = [ '#423d4f', '#4a6860', '#848f39', '#a2b73c', '#ddcb53', '#c5a32f', '#7d5836', '#963b20', '#7c2626', '#491d37', '#2f254a' ].reverse(); this.schemes.httpStatus = { 503: '#ea5029', 502: '#d23f14', 500: '#bf3613', 410: '#efacea', 409: '#e291dc', 403: '#f457e8', 408: '#e121d2', 401: '#b92dae', 405: '#f47ceb', 404: '#a82a9f', 400: '#b263c6', 301: '#6fa024', 302: '#87c32b', 307: '#a0d84c', 304: '#28b55c', 200: '#1a4f74', 206: '#27839f', 201: '#52adc9', 202: '#7c979f', 203: '#a5b8bd', 204: '#c1cdd1' }; this.schemes.colorwheel = [ '#b5b6a9', '#858772', '#785f43', '#96557e', '#4682b4', '#65b9ac', '#73c03a', '#cb513a' ].reverse(); this.schemes.cool = [ '#5e9d2f', '#73c03a', '#4682b4', '#7bc3b8', '#a9884e', '#c1b266', '#a47493', '#c09fb5' ]; this.schemes.munin = [ '#00cc00', '#0066b3', '#ff8000', '#ffcc00', '#330099', '#990099', '#ccff00', '#ff0000', '#808080', '#008f00', '#00487d', '#b35a00', '#b38f00', '#6b006b', '#8fb300', '#b30000', '#bebebe', '#80ff80', '#80c9ff', '#ffc080', '#ffe680', '#aa80ff', '#ee00cc', '#ff8080', '#666600', '#ffbfff', '#00ffcc', '#cc6699', '#999900' ]; }; Rickshaw.namespace('Rickshaw.Fixtures.RandomData'); Rickshaw.Fixtures.RandomData = function(timeInterval) { var addData; timeInterval = timeInterval || 1; var lastRandomValue = 200; var timeBase = Math.floor(new Date().getTime() / 1000); this.addData = function(data) { var randomValue = Math.random() * 100 + 15 + lastRandomValue; var index = data[0].length; var counter = 1; data.forEach( function(series) { var randomVariance = Math.random() * 20; var v = randomValue / 25 + counter++ + (Math.cos((index * counter * 11) / 960) + 2) * 15 + (Math.cos(index / 7) + 2) * 7 + (Math.cos(index / 17) + 2) * 1; series.push( { x: (index * timeInterval) + timeBase, y: v + randomVariance } ); } ); lastRandomValue = randomValue * .85; } }; Rickshaw.namespace('Rickshaw.Fixtures.Time'); Rickshaw.Fixtures.Time = function() { var tzOffset = new Date().getTimezoneOffset() * 60; var self = this; this.months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; this.units = [ { name: 'decade', seconds: 86400 * 365.25 * 10, formatter: function(d) { return (parseInt(d.getUTCFullYear() / 10) * 10) } }, { name: 'year', seconds: 86400 * 365.25, formatter: function(d) { return d.getUTCFullYear() } }, { name: 'month', seconds: 86400 * 30.5, formatter: function(d) { return self.months[d.getUTCMonth()] } }, { name: 'week', seconds: 86400 * 7, formatter: function(d) { return self.formatDate(d) } }, { name: 'day', seconds: 86400, formatter: function(d) { return d.getUTCDate() } }, { name: '6 hour', seconds: 3600 * 6, formatter: function(d) { return self.formatTime(d) } }, { name: 'hour', seconds: 3600, formatter: function(d) { return self.formatTime(d) } }, { name: '15 minute', seconds: 60 * 15, formatter: function(d) { return self.formatTime(d) } }, { name: 'minute', seconds: 60, formatter: function(d) { return d.getUTCMinutes() } }, { name: '15 second', seconds: 15, formatter: function(d) { return d.getUTCSeconds() + 's' } }, { name: 'second', seconds: 1, formatter: function(d) { return d.getUTCSeconds() + 's' } } ]; this.unit = function(unitName) { return this.units.filter( function(unit) { return unitName == unit.name } ).shift(); }; this.formatDate = function(d) { return d.toUTCString().match(/, (\w+ \w+ \w+)/)[1]; }; this.formatTime = function(d) { return d.toUTCString().match(/(\d+:\d+):/)[1]; }; this.ceil = function(time, unit) { if (unit.name == 'month') { var nearFuture = new Date((time + unit.seconds - 1) * 1000); return new Date(nearFuture.getUTCFullYear(), nearFuture.getUTCMonth() + 1, 1, 0, 0, 0, 0).getTime() / 1000; } if (unit.name == 'year') { var nearFuture = new Date((time + unit.seconds - 1) * 1000); return new Date(nearFuture.getUTCFullYear(), 1, 1, 0, 0, 0, 0).getTime() / 1000; } return Math.ceil(time / unit.seconds) * unit.seconds; }; }; Rickshaw.namespace('Rickshaw.Fixtures.Number'); Rickshaw.Fixtures.Number.formatKMBT = function(y) { if (y >= 1000000000000) { return y / 1000000000000 + "T" } else if (y >= 1000000000) { return y / 1000000000 + "B" } else if (y >= 1000000) { return y / 1000000 + "M" } else if (y >= 1000) { return y / 1000 + "K" } else if (y < 1 && y > 0) { return y.toFixed(2) } else if (y == 0) { return '' } else { return y } }; Rickshaw.Fixtures.Number.formatBase1024KMGTP = function(y) { if (y >= 1125899906842624) { return y / 1125899906842624 + "P" } else if (y >= 1099511627776){ return y / 1099511627776 + "T" } else if (y >= 1073741824) { return y / 1073741824 + "G" } else if (y >= 1048576) { return y / 1048576 + "M" } else if (y >= 1024) { return y / 1024 + "K" } else if (y < 1 && y > 0) { return y.toFixed(2) } else if (y == 0) { return '' } else { return y } }; Rickshaw.namespace("Rickshaw.Color.Palette"); Rickshaw.Color.Palette = function(args) { var color = new Rickshaw.Fixtures.Color(); args = args || {}; this.schemes = {}; this.scheme = color.schemes[args.scheme] || args.scheme || color.schemes.colorwheel; this.runningIndex = 0; this.generatorIndex = 0; if (args.interpolatedStopCount) { var schemeCount = this.scheme.length - 1; var i, j, scheme = []; for (i = 0; i < schemeCount; i++) { scheme.push(this.scheme[i]); var generator = d3.interpolateHsl(this.scheme[i], this.scheme[i + 1]); for (j = 1; j < args.interpolatedStopCount; j++) { scheme.push(generator((1 / args.interpolatedStopCount) * j)); } } scheme.push(this.scheme[this.scheme.length - 1]); this.scheme = scheme; } this.rotateCount = this.scheme.length; this.color = function(key) { return this.scheme[key] || this.scheme[this.runningIndex++] || this.interpolateColor() || '#808080'; }; this.interpolateColor = function() { var color; if (this.generatorIndex == this.rotateCount * 2 - 1) { color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[0])(0.5); this.generatorIndex = 0; this.rotateCount *= 2; } else { color = d3.interpolateHsl(this.scheme[this.generatorIndex], this.scheme[this.generatorIndex + 1])(0.5); this.generatorIndex++; } this.scheme.push(color); return color; }; }; Rickshaw.namespace('Graph.Ajax'); Rickshaw.Graph.Ajax = function(args) { var self = this; this.dataURL = args.dataURL; $.ajax( { url: this.dataURL, complete: function(response, status) { if (status === 'error') { console.log("error loading dataURL: " + this.dataURL); } var data = JSON.parse(response.responseText); if (typeof args.onData === 'function') { var processedData = args.onData(data); data = processedData; } if (args.series) { args.series.forEach( function(s) { var seriesKey = s.key || s.name; if (!seriesKey) throw "series needs a key or a name"; data.forEach( function(d) { var dataKey = d.key || d.name; if (!dataKey) throw "data needs a key or a name"; if (seriesKey == dataKey) { var properties = ['color', 'name', 'data']; properties.forEach( function(p) { s[p] = s[p] || d[p]; } ); } } ); } ); } else { args.series = data; } self.graph = new Rickshaw.Graph(args); self.graph.render(); if (typeof args.onComplete == 'function') { args.onComplete(self); } } } ); }; Rickshaw.namespace('Rickshaw.Graph.Annotate'); Rickshaw.Graph.Annotate = function(args) { var graph = this.graph = args.graph; this.elements = { timeline: args.element }; var self = this; this.data = {}; this.elements.timeline.classList.add('rickshaw_annotation_timeline'); this.add = function(time, content) { self.data[time] = self.data[time] || {'boxes': []}; self.data[time].boxes.push({content: content}); }; this.update = function() { Rickshaw.keys(self.data).forEach( function(time) { var annotation = self.data[time]; var left = self.graph.x(time); if (left < 0 || left > self.graph.x.range()[1]) { if (annotation.element) { annotation.element.style.display = 'none'; } return; } if (!annotation.element) { var element = annotation.element = document.createElement('div'); element.classList.add('annotation'); this.elements.timeline.appendChild(element); element.addEventListener('click', function(e) { element.classList.toggle('active'); annotation.line.classList.toggle('active'); }, false); } annotation.element.style.left = left + 'px'; annotation.element.style.display = 'block'; annotation.boxes.forEach( function(box) { var element = box.element; if (!element) { element = box.element = document.createElement('div'); element.classList.add('content'); element.innerHTML = box.content; annotation.element.appendChild(element); annotation.line = document.createElement('div'); annotation.line.classList.add('annotation_line'); self.graph.element.appendChild(annotation.line); } annotation.line.style.left = left + 'px'; } ); }, this ); }; this.graph.onUpdate( function() { self.update() } ); }; Rickshaw.namespace('Rickshaw.Graph.Axis.Time'); Rickshaw.Graph.Axis.Time = function(args) { var self = this; this.graph = args.graph; this.elements = []; this.ticksTreatment = args.ticksTreatment || 'plain'; this.fixedTimeUnit = args.timeUnit; var time = new Rickshaw.Fixtures.Time(); this.appropriateTimeUnit = function() { var unit; var units = time.units; var domain = this.graph.x.domain(); var rangeSeconds = domain[1] - domain[0]; units.forEach( function(u) { if (Math.floor(rangeSeconds / u.seconds) >= 2) { unit = unit || u; } } ); return (unit || time.units[time.units.length - 1]); }; this.tickOffsets = function() { var domain = this.graph.x.domain(); var unit = this.fixedTimeUnit || this.appropriateTimeUnit(); var count = Math.ceil((domain[1] - domain[0]) / unit.seconds); var runningTick = domain[0]; var offsets = []; for (var i = 0; i < count; i++) { tickValue = time.ceil(runningTick, unit); runningTick = tickValue + unit.seconds / 2; offsets.push( { value: tickValue, unit: unit } ); } return offsets; }; this.render = function() { this.elements.forEach( function(e) { e.parentNode.removeChild(e); } ); this.elements = []; var offsets = this.tickOffsets(); offsets.forEach( function(o) { if (self.graph.x(o.value) > self.graph.x.range()[1]) return; var element = document.createElement('div'); element.style.left = self.graph.x(o.value) + 'px'; element.classList.add('x_tick'); element.classList.add(self.ticksTreatment); var title = document.createElement('div'); title.classList.add('title'); title.innerHTML = o.unit.formatter(new Date(o.value * 1000)); element.appendChild(title); self.graph.element.appendChild(element); self.elements.push(element); } ); }; this.graph.onUpdate( function() { self.render() } ); }; Rickshaw.namespace('Rickshaw.Graph.Axis.Y'); Rickshaw.Graph.Axis.Y = function(args) { var self = this; var berthRate = 0.10; this.initialize = function(args) { this.graph = args.graph; this.orientation = args.orientation || 'right'; var pixelsPerTick = args.pixelsPerTick || 75; this.ticks = args.ticks || Math.floor(this.graph.height / pixelsPerTick); this.tickSize = args.tickSize || 4; this.ticksTreatment = args.ticksTreatment || 'plain'; if (args.element) { this.element = args.element; this.vis = d3.select(args.element) .append("svg:svg") .attr('class', 'rickshaw_graph y_axis'); this.element = this.vis[0][0]; this.element.style.position = 'relative'; this.setSize({ width: args.width, height: args.height }); } else { this.vis = this.graph.vis; } this.graph.onUpdate( function() { self.render() } ); }; this.setSize = function(args) { args = args || {}; if (!this.element) return; if (typeof window !== 'undefined') { var style = window.getComputedStyle(this.element.parentNode, null); var elementWidth = parseInt(style.getPropertyValue('width')); if (!args.auto) { var elementHeight = parseInt(style.getPropertyValue('height')); } } this.width = args.width || elementWidth || this.graph.width * berthRate; this.height = args.height || elementHeight || this.graph.height; this.vis .attr('width', this.width) .attr('height', this.height * (1 + berthRate)); var berth = this.height * berthRate; this.element.style.top = -1 * berth + 'px'; }; this.render = function() { if (this.graph.height !== this._renderHeight) this.setSize({ auto: true }); var axis = d3.svg.axis().scale(this.graph.y).orient(this.orientation); axis.tickFormat( args.tickFormat || function(y) { return y } ); if (this.orientation == 'left') { var berth = this.height * berthRate; var transform = 'translate(' + this.width + ', ' + berth + ')'; } if (this.element) { this.vis.selectAll('*').remove(); } this.vis .append("svg:g") .attr("class", ["y_ticks", this.ticksTreatment].join(" ")) .attr("transform", transform) .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize)) var gridSize = (this.orientation == 'right' ? 1 : -1) * this.graph.width; this.graph.vis .append("svg:g") .attr("class", "y_grid") .call(axis.ticks(this.ticks).tickSubdivide(0).tickSize(gridSize)); this._renderHeight = this.graph.height; }; this.initialize(args); }; Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Highlight'); Rickshaw.Graph.Behavior.Series.Highlight = function(args) { this.graph = args.graph; this.legend = args.legend; var self = this; var colorSafe = {}; this.addHighlightEvents = function (l) { l.element.addEventListener( 'mouseover', function(e) { self.legend.lines.forEach( function(line) { if (l === line) return; colorSafe[line.series.name] = colorSafe[line.series.name] || line.series.color; line.series.color = d3.interpolateRgb(line.series.color, d3.rgb('#d8d8d8'))(0.8).toString(); } ); self.graph.update(); }, false ); l.element.addEventListener( 'mouseout', function(e) { self.legend.lines.forEach( function(line) { if (colorSafe[line.series.name]) { line.series.color = colorSafe[line.series.name]; } } ); self.graph.update(); }, false ); }; if (this.legend) { this.legend.lines.forEach( function(l) { self.addHighlightEvents(l); } ); } }; Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Order'); Rickshaw.Graph.Behavior.Series.Order = function(args) { this.graph = args.graph; this.legend = args.legend; var self = this; $(function() { $(self.legend.list).sortable( { containment: 'parent', tolerance: 'pointer', update: function( event, ui ) { var series = []; $(self.legend.list).find('li').each( function(index, item) { if (!item.series) return; series.push(item.series); } ); for (var i = self.graph.series.length - 1; i >= 0; i--) { self.graph.series[i] = series.shift(); } self.graph.update(); } } ); $(self.legend.list).disableSelection(); }); //hack to make jquery-ui sortable behave this.graph.onUpdate( function() { var h = window.getComputedStyle(self.legend.element).height; self.legend.element.style.height = h; } ); }; Rickshaw.namespace('Rickshaw.Graph.Behavior.Series.Toggle'); Rickshaw.Graph.Behavior.Series.Toggle = function(args) { this.graph = args.graph; this.legend = args.legend; var self = this; this.addAnchor = function(line) { var anchor = document.createElement('a'); anchor.innerHTML = '✔'; anchor.classList.add('action'); line.element.insertBefore(anchor, line.element.firstChild); anchor.onclick = function(e) { if (line.series.disabled) { line.series.enable(); line.element.classList.remove('disabled'); } else { line.series.disable(); line.element.classList.add('disabled'); } } var label = line.element.getElementsByTagName('span')[0]; label.onclick = function(e){ var disableAllOtherLines = line.series.disabled; if ( ! disableAllOtherLines ) { for ( var i = 0; i < self.legend.lines.length; i++ ) { var l = self.legend.lines[i]; if ( line.series === l.series ) { // noop } else if ( l.series.disabled ) { // noop } else { disableAllOtherLines = true; break; } } } // show all or none if ( disableAllOtherLines ) { // these must happen first or else we try ( and probably fail ) to make a no line graph line.series.enable(); line.element.classList.remove('disabled'); self.legend.lines.forEach(function(l){ if ( line.series === l.series ) { // noop } else { l.series.disable(); l.element.classList.add('disabled'); } }); } else { self.legend.lines.forEach(function(l){ l.series.enable(); l.element.classList.remove('disabled'); }); } }; }; if (this.legend) { $(this.legend.list).sortable( { start: function(event, ui) { ui.item.bind('no.onclick', function(event) { event.preventDefault(); } ); }, stop: function(event, ui) { setTimeout(function(){ ui.item.unbind('no.onclick'); }, 250); } }) this.legend.lines.forEach( function(l) { self.addAnchor(l); } ); } this._addBehavior = function() { this.graph.series.forEach( function(s) { s.disable = function() { if (self.graph.series.length <= 1) { throw('only one series left'); } s.disabled = true; self.graph.update(); }; s.enable = function() { s.disabled = false; self.graph.update(); }; } ); }; this._addBehavior(); this.updateBehaviour = function () { this._addBehavior() }; }; Rickshaw.namespace('Rickshaw.Graph.HoverDetail'); Rickshaw.Graph.HoverDetail = Rickshaw.Class.create({ initialize: function(args) { var graph = this.graph = args.graph; this.xFormatter = args.xFormatter || function(x) { return new Date( x * 1000 ).toUTCString(); }; this.yFormatter = args.yFormatter || function(y) { return y.toFixed(2); }; var element = this.element = document.createElement('div'); element.className = 'detail'; this.visible = true; graph.element.appendChild(element); this.lastEvent = null; this._addListeners(); this.onShow = args.onShow; this.onHide = args.onHide; this.onRender = args.onRender; this.formatter = args.formatter || this.formatter; }, formatter: function(series, x, y, formattedX, formattedY, d) { return series.name + ': ' + formattedY; }, update: function(e) { e = e || this.lastEvent; if (!e) return; this.lastEvent = e; if (e.target.nodeName != 'path' && e.target.nodeName != 'svg') return; var graph = this.graph; var eventX = e.offsetX || e.layerX; var eventY = e.offsetY || e.layerY; var domainX = graph.x.invert(eventX); var stackedData = graph.stackedData; var topSeriesData = stackedData.slice(-1).shift(); var domainIndexScale = d3.scale.linear() .domain([topSeriesData[0].x, topSeriesData.slice(-1).shift().x]) .range([0, topSeriesData.length]); var approximateIndex = Math.floor(domainIndexScale(domainX)); var dataIndex = Math.min(approximateIndex || 0, stackedData[0].length - 1); for (var i = approximateIndex; i < stackedData[0].length - 1;) { if (!stackedData[0][i] || !stackedData[0][i + 1]) { break; } if (stackedData[0][i].x <= domainX && stackedData[0][i + 1].x > domainX) { dataIndex = i; break; } if (stackedData[0][i + 1] < domainX) { i++ } else { i-- } } var domainX = stackedData[0][dataIndex].x; var formattedXValue = this.xFormatter(domainX); var graphX = graph.x(domainX); var order = 0; var detail = graph.series.active() .map( function(s) { return { order: order++, series: s, name: s.name, value: s.stack[dataIndex] } } ); var activeItem; var sortFn = function(a, b) { return (a.value.y0 + a.value.y) - (b.value.y0 + b.value.y); }; var domainMouseY = graph.y.magnitude.invert(graph.element.offsetHeight - eventY); detail.sort(sortFn).forEach( function(d) { d.formattedYValue = (this.yFormatter.constructor == Array) ? this.yFormatter[detail.indexOf(d)](d.value.y) : this.yFormatter(d.value.y); d.graphX = graphX; d.graphY = graph.y(d.value.y0 + d.value.y); if (domainMouseY > d.value.y0 && domainMouseY < d.value.y0 + d.value.y && !activeItem) { activeItem = d; d.active = true; } }, this ); this.element.innerHTML = ''; this.element.style.left = graph.x(domainX) + 'px'; if (this.visible) { this.render( { detail: detail, domainX: domainX, formattedXValue: formattedXValue, mouseX: eventX, mouseY: eventY } ); } }, hide: function() { this.visible = false; this.element.classList.add('inactive'); if (typeof this.onHide == 'function') { this.onHide(); } }, show: function() { this.visible = true; this.element.classList.remove('inactive'); if (typeof this.onShow == 'function') { this.onShow(); } }, render: function(args) { var detail = args.detail; var domainX = args.domainX; var mouseX = args.mouseX; var mouseY = args.mouseY; var formattedXValue = args.formattedXValue; var xLabel = document.createElement('div'); xLabel.className = 'x_label'; xLabel.innerHTML = formattedXValue; this.element.appendChild(xLabel); detail.forEach( function(d) { var item = document.createElement('div'); item.className = 'item'; item.innerHTML = this.formatter(d.series, domainX, d.value.y, formattedXValue, d.formattedYValue, d); item.style.top = this.graph.y(d.value.y0 + d.value.y) + 'px'; this.element.appendChild(item); var dot = document.createElement('div'); dot.className = 'dot'; dot.style.top = item.style.top; dot.style.borderColor = d.series.color; this.element.appendChild(dot); if (d.active) { item.className = 'item active'; dot.className = 'dot active'; } }, this ); this.show(); if (typeof this.onRender == 'function') { this.onRender(args); } }, _addListeners: function() { this.graph.element.addEventListener( 'mousemove', function(e) { this.visible = true; this.update(e) }.bind(this), false ); this.graph.onUpdate( function() { this.update() }.bind(this) ); this.graph.element.addEventListener( 'mouseout', function(e) { if (e.relatedTarget && !(e.relatedTarget.compareDocumentPosition(this.graph.element) & Node.DOCUMENT_POSITION_CONTAINS)) { this.hide(); } }.bind(this), false ); } }); Rickshaw.namespace('Rickshaw.Graph.JSONP'); Rickshaw.Graph.JSONP = function(args) { var self = this; this.dataURL = args.dataURL; $.ajax( { url: this.dataURL, dataType: 'jsonp', success: function(data, status, response) { if (status === 'error') { console.log("error loading dataURL: " + this.dataURL); } if (typeof args.onData === 'function') { var processedData = args.onData(data); data = processedData; } if (args.series) { args.series.forEach( function(s) { var seriesKey = s.key || s.name; if (!seriesKey) throw "series needs a key or a name"; data.forEach( function(d) { var dataKey = d.key || d.name; if (!dataKey) throw "data needs a key or a name"; if (seriesKey == dataKey) { var properties = ['color', 'name', 'data']; properties.forEach( function(p) { s[p] = s[p] || d[p]; } ); } } ); } ); } else { args.series = data; } self.graph = new Rickshaw.Graph(args); self.graph.render(); if (typeof args.onComplete == 'function') { args.onComplete(self); } } } ); }; Rickshaw.namespace('Rickshaw.Graph.Legend'); Rickshaw.Graph.Legend = function(args) { var element = this.element = args.element; var graph = this.graph = args.graph; var self = this; element.classList.add('rickshaw_legend'); var list = this.list = document.createElement('ul'); element.appendChild(list); var series = graph.series .map( function(s) { return s } ) .reverse(); this.lines = []; this.addLine = function (series) { var line = document.createElement('li'); line.className = 'line'; var swatch = document.createElement('div'); swatch.className = 'swatch'; swatch.style.backgroundColor = series.color; line.appendChild(swatch); var label = document.createElement('span'); label.className = 'label'; label.innerHTML = series.name; line.appendChild(label); list.appendChild(line); line.series = series; if (series.noLegend) { line.style.display = 'none'; } var _line = { element: line, series: series }; if (self.shelving) { self.shelving.addAnchor(_line); self.shelving.updateBehaviour(); } if (self.highlighter) { self.highlighter.addHighlightEvents(_line); } self.lines.push(_line); }; series.forEach( function(s) { self.addLine(s); } ); graph.onUpdate( function() {} ); }; Rickshaw.namespace('Rickshaw.Graph.RangeSlider'); Rickshaw.Graph.RangeSlider = function(args) { var element = this.element = args.element; var graph = this.graph = args.graph; $( function() { $(element).slider( { range: true, min: graph.dataDomain()[0], max: graph.dataDomain()[1], values: [ graph.dataDomain()[0], graph.dataDomain()[1], ], slide: function( event, ui ) { graph.window.xMin = ui.values[0]; graph.window.xMax = ui.values[1]; graph.update(); // if we're at an extreme, stick there if (graph.dataDomain()[0] == ui.values[0]) { graph.window.xMin = undefined; } if (graph.dataDomain()[1] == ui.values[1]) { graph.window.xMax = undefined; } } } ); } ); element[0].style.width = graph.width + 'px'; graph.onUpdate( function() { var values = $(element).slider('option', 'values'); $(element).slider('option', 'min', graph.dataDomain()[0]); $(element).slider('option', 'max', graph.dataDomain()[1]); if (graph.window.xMin == undefined) { values[0] = graph.dataDomain()[0]; } if (graph.window.xMax == undefined) { values[1] = graph.dataDomain()[1]; } $(element).slider('option', 'values', values); } ); }; Rickshaw.namespace("Rickshaw.Graph.Renderer"); Rickshaw.Graph.Renderer = Rickshaw.Class.create( { initialize: function(args) { this.graph = args.graph; this.tension = args.tension || this.tension; this.graph.unstacker = this.graph.unstacker || new Rickshaw.Graph.Unstacker( { graph: this.graph } ); this.configure(args); }, seriesPathFactory: function() { //implement in subclass }, seriesStrokeFactory: function() { // implement in subclass }, defaults: function() { return { tension: 0.8, strokeWidth: 2, unstack: true, padding: { top: 0.01, right: 0, bottom: 0.01, left: 0 }, stroke: false, fill: false }; }, domain: function() { var values = []; var stackedData = this.graph.stackedData || this.graph.stackData(); var topSeriesData = this.unstack ? stackedData : [ stackedData.slice(-1).shift() ]; topSeriesData.forEach( function(series) { series.forEach( function(d) { values.push( d.y + d.y0 ); } ); } ); var xMin = stackedData[0][0].x; var xMax = stackedData[0][ stackedData[0].length - 1 ].x; xMin -= (xMax - xMin) * this.padding.left; xMax += (xMax - xMin) * this.padding.right; var yMin = this.graph.min === 'auto' ? d3.min( values ) : this.graph.min || 0; var yMax = this.graph.max || d3.max( values ); if (this.graph.min === 'auto' || yMin < 0) { yMin -= (yMax - yMin) * this.padding.bottom; } if (this.graph.max === undefined) { yMax += (yMax - yMin) * this.padding.top; } return { x: [xMin, xMax], y: [yMin, yMax] }; }, render: function() { var graph = this.graph; graph.vis.selectAll('*').remove(); var nodes = graph.vis.selectAll("path") .data(this.graph.stackedData) .enter().append("svg:path") .attr("d", this.seriesPathFactory()); var i = 0; graph.series.forEach( function(series) { if (series.disabled) return; series.path = nodes[0][i++]; this._styleSeries(series); }, this ); }, _styleSeries: function(series) { var fill = this.fill ? series.color : 'none'; var stroke = this.stroke ? series.color : 'none'; series.path.setAttribute('fill', fill); series.path.setAttribute('stroke', stroke); series.path.setAttribute('stroke-width', this.strokeWidth); series.path.setAttribute('class', series.className); }, configure: function(args) { args = args || {}; Rickshaw.keys(this.defaults()).forEach( function(key) { if (!args.hasOwnProperty(key)) { this[key] = this[key] || this.graph[key] || this.defaults()[key]; return; } if (typeof this.defaults()[key] == 'object') { Rickshaw.keys(this.defaults()[key]).forEach( function(k) { this[key][k] = args[key][k] !== undefined ? args[key][k] : this[key][k] !== undefined ? this[key][k] : this.defaults()[key][k]; }, this ); } else { this[key] = args[key] !== undefined ? args[key] : this[key] !== undefined ? this[key] : this.graph[key] !== undefined ? this.graph[key] : this.defaults()[key]; } }, this ); }, setStrokeWidth: function(strokeWidth) { if (strokeWidth !== undefined) { this.strokeWidth = strokeWidth; } }, setTension: function(tension) { if (tension !== undefined) { this.tension = tension; } } } ); Rickshaw.namespace('Rickshaw.Graph.Renderer.Line'); Rickshaw.Graph.Renderer.Line = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { name: 'line', defaults: function($super) { return Rickshaw.extend( $super(), { unstack: true, fill: false, stroke: true } ); }, seriesPathFactory: function() { var graph = this.graph; return d3.svg.line() .x( function(d) { return graph.x(d.x) } ) .y( function(d) { return graph.y(d.y) } ) .interpolate(this.graph.interpolation).tension(this.tension); } } ); Rickshaw.namespace('Rickshaw.Graph.Renderer.Stack'); Rickshaw.Graph.Renderer.Stack = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { name: 'stack', defaults: function($super) { return Rickshaw.extend( $super(), { fill: true, stroke: false, unstack: false, } ); }, seriesPathFactory: function() { var graph = this.graph; return d3.svg.area() .x( function(d) { return graph.x(d.x) } ) .y0( function(d) { return graph.y(d.y0) } ) .y1( function(d) { return graph.y(d.y + d.y0) } ) .interpolate(this.graph.interpolation).tension(this.tension); } } ); Rickshaw.namespace('Rickshaw.Graph.Renderer.Bar'); Rickshaw.Graph.Renderer.Bar = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { name: 'bar', defaults: function($super) { var defaults = Rickshaw.extend( $super(), { gapSize: 0.05, unstack: false, } ); delete defaults.tension; return defaults; }, initialize: function($super, args) { args = args || {}; this.gapSize = args.gapSize || this.gapSize; $super(args); }, domain: function($super) { var domain = $super(); var frequentInterval = this._frequentInterval(); domain.x[1] += parseInt(frequentInterval.magnitude); return domain; }, barWidth: function() { var stackedData = this.graph.stackedData || this.graph.stackData(); var data = stackedData.slice(-1).shift(); var frequentInterval = this._frequentInterval(); var barWidth = this.graph.x(data[0].x + frequentInterval.magnitude * (1 - this.gapSize)); return barWidth; }, render: function() { var graph = this.graph; graph.vis.selectAll('*').remove(); var barWidth = this.barWidth(); var barXOffset = 0; var activeSeriesCount = graph.series.filter( function(s) { return !s.disabled; } ).length; var seriesBarWidth = this.unstack ? barWidth / activeSeriesCount : barWidth; graph.series.forEach( function(series) { if (series.disabled) return; var nodes = graph.vis.selectAll("path") .data(series.stack) .enter().append("svg:rect") .attr("x", function(d) { return graph.x(d.x) + barXOffset }) .attr("y", function(d) { return graph.y(d.y0 + d.y) }) .attr("width", seriesBarWidth) .attr("height", function(d) { return graph.y.magnitude(d.y) }); Array.prototype.forEach.call(nodes[0], function(n) { n.setAttribute('fill', series.color); } ); if (this.unstack) barXOffset += seriesBarWidth; }, this ); }, _frequentInterval: function() { var stackedData = this.graph.stackedData || this.graph.stackData(); var data = stackedData.slice(-1).shift(); var intervalCounts = {}; for (var i = 0; i < data.length - 1; i++) { var interval = data[i + 1].x - data[i].x; intervalCounts[interval] = intervalCounts[interval] || 0; intervalCounts[interval]++; } var frequentInterval = { count: 0 }; Rickshaw.keys(intervalCounts).forEach( function(i) { if (frequentInterval.count < intervalCounts[i]) { frequentInterval = { count: intervalCounts[i], magnitude: i }; } } ); this._frequentInterval = function() { return frequentInterval }; return frequentInterval; } } ); Rickshaw.namespace('Rickshaw.Graph.Renderer.Area'); Rickshaw.Graph.Renderer.Area = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { name: 'area', defaults: function($super) { return Rickshaw.extend( $super(), { unstack: false, fill: false, stroke: false } ); }, seriesPathFactory: function() { var graph = this.graph; return d3.svg.area() .x( function(d) { return graph.x(d.x) } ) .y0( function(d) { return graph.y(d.y0) } ) .y1( function(d) { return graph.y(d.y + d.y0) } ) .interpolate(graph.interpolation).tension(this.tension); }, seriesStrokeFactory: function() { var graph = this.graph; return d3.svg.line() .x( function(d) { return graph.x(d.x) } ) .y( function(d) { return graph.y(d.y + d.y0) } ) .interpolate(graph.interpolation).tension(this.tension); }, render: function() { var graph = this.graph; graph.vis.selectAll('*').remove(); var nodes = graph.vis.selectAll("path") .data(this.graph.stackedData) .enter().insert("svg:g", 'g'); nodes.append("svg:path") .attr("d", this.seriesPathFactory()) .attr("class", 'area'); if (this.stroke) { nodes.append("svg:path") .attr("d", this.seriesStrokeFactory()) .attr("class", 'line'); } var i = 0; graph.series.forEach( function(series) { if (series.disabled) return; series.path = nodes[0][i++]; this._styleSeries(series); }, this ); }, _styleSeries: function(series) { if (!series.path) return; d3.select(series.path).select('.area') .attr('fill', series.color); if (this.stroke) { d3.select(series.path).select('.line') .attr('fill', 'none') .attr('stroke', series.stroke || d3.interpolateRgb(series.color, 'black')(0.125)) .attr('stroke-width', this.strokeWidth); } if (series.className) { series.path.setAttribute('class', series.className); } } } ); Rickshaw.namespace('Rickshaw.Graph.Renderer.ScatterPlot'); Rickshaw.Graph.Renderer.ScatterPlot = Rickshaw.Class.create( Rickshaw.Graph.Renderer, { name: 'scatterplot', defaults: function($super) { return Rickshaw.extend( $super(), { unstack: true, fill: true, stroke: false, padding:{ top: 0.01, right: 0.01, bottom: 0.01, left: 0.01 }, dotSize: 4 } ); }, initialize: function($super, args) { $super(args); }, render: function() { var graph = this.graph; graph.vis.selectAll('*').remove(); graph.series.forEach( function(series) { if (series.disabled) return; var nodes = graph.vis.selectAll("path") .data(series.stack) .enter().append("svg:circle") .attr("cx", function(d) { return graph.x(d.x) }) .attr("cy", function(d) { return graph.y(d.y) }) .attr("r", function(d) { return ("r" in d) ? d.r : graph.renderer.dotSize}); Array.prototype.forEach.call(nodes[0], function(n) { n.setAttribute('fill', series.color); } ); }, this ); } } ); Rickshaw.namespace('Rickshaw.Graph.Smoother'); Rickshaw.Graph.Smoother = function(args) { this.graph = args.graph; this.element = args.element; var self = this; this.aggregationScale = 1; if (this.element) { $( function() { $(self.element).slider( { min: 1, max: 100, slide: function( event, ui ) { self.setScale(ui.value); self.graph.update(); } } ); } ); } self.graph.stackData.hooks.data.push( { name: 'smoother', orderPosition: 50, f: function(data) { var aggregatedData = []; data.forEach( function(seriesData) { var aggregatedSeriesData = []; while (seriesData.length) { var avgX = 0, avgY = 0; var slice = seriesData.splice(0, self.aggregationScale); slice.forEach( function(d) { avgX += d.x / slice.length; avgY += d.y / slice.length; } ); aggregatedSeriesData.push( { x: avgX, y: avgY } ); } aggregatedData.push(aggregatedSeriesData); } ); return aggregatedData; } } ); this.setScale = function(scale) { if (scale < 1) { throw "scale out of range: " + scale; } this.aggregationScale = scale; this.graph.update(); } }; Rickshaw.namespace('Rickshaw.Graph.Unstacker'); Rickshaw.Graph.Unstacker = function(args) { this.graph = args.graph; var self = this; this.graph.stackData.hooks.after.push( { name: 'unstacker', f: function(data) { if (!self.graph.renderer.unstack) return data; data.forEach( function(seriesData) { seriesData.forEach( function(d) { d.y0 = 0; } ); } ); return data; } } ); }; Rickshaw.namespace('Rickshaw.Series'); Rickshaw.Series = Rickshaw.Class.create( Array, { initialize: function (data, palette, options) { options = options || {} this.palette = new Rickshaw.Color.Palette(palette); this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase; if (data && (typeof(data) == "object") && (data instanceof Array)) { data.forEach( function(item) { this.addItem(item) }, this ); } }, addItem: function(item) { if (typeof(item.name) === 'undefined') { throw('addItem() needs a name'); } item.color = (item.color || this.palette.color(item.name)); item.data = (item.data || []); // backfill, if necessary if ((item.data.length == 0) && this.length && (this.getIndex() > 0)) { this[0].data.forEach( function(plot) { item.data.push({ x: plot.x, y: 0 }); } ); } else if (item.data.length == 0) { item.data.push({ x: this.timeBase - (this.timeInterval || 0), y: 0 }); } this.push(item); if (this.legend) { this.legend.addLine(this.itemByName(item.name)); } }, addData: function(data) { var index = this.getIndex(); Rickshaw.keys(data).forEach( function(name) { if (! this.itemByName(name)) { this.addItem({ name: name }); } }, this ); this.forEach( function(item) { item.data.push({ x: (index * this.timeInterval || 1) + this.timeBase, y: (data[item.name] || 0) }); }, this ); }, getIndex: function () { return (this[0] && this[0].data && this[0].data.length) ? this[0].data.length : 0; }, itemByName: function(name) { for (var i = 0; i < this.length; i++) { if (this[i].name == name) return this[i]; } }, setTimeInterval: function(iv) { this.timeInterval = iv / 1000; }, setTimeBase: function (t) { this.timeBase = t; }, dump: function() { var data = { timeBase: this.timeBase, timeInterval: this.timeInterval, items: [], }; this.forEach( function(item) { var newItem = { color: item.color, name: item.name, data: [] }; item.data.forEach( function(plot) { newItem.data.push({ x: plot.x, y: plot.y }); } ); data.items.push(newItem); } ); return data; }, load: function(data) { if (data.timeInterval) { this.timeInterval = data.timeInterval; } if (data.timeBase) { this.timeBase = data.timeBase; } if (data.items) { data.items.forEach( function(item) { this.push(item); if (this.legend) { this.legend.addLine(this.itemByName(item.name)); } }, this ); } } } ); Rickshaw.Series.zeroFill = function(series) { var x; var i = 0; var data = series.map( function(s) { return s.data } ); while ( i < Math.max.apply(null, data.map( function(d) { return d.length } )) ) { x = Math.min.apply( null, data .filter(function(d) { return d[i] }) .map(function(d) { return d[i].x }) ); data.forEach( function(d) { if (!d[i] || d[i].x != x) { d.splice(i, 0, { x: x, y: 0 }); } } ); i++; } }; Rickshaw.namespace('Rickshaw.Series.FixedDuration'); Rickshaw.Series.FixedDuration = Rickshaw.Class.create(Rickshaw.Series, { initialize: function (data, palette, options) { var options = options || {} if (typeof(options.timeInterval) === 'undefined') { throw new Error('FixedDuration series requires timeInterval'); } if (typeof(options.maxDataPoints) === 'undefined') { throw new Error('FixedDuration series requires maxDataPoints'); } this.palette = new Rickshaw.Color.Palette(palette); this.timeBase = typeof(options.timeBase) === 'undefined' ? Math.floor(new Date().getTime() / 1000) : options.timeBase; this.setTimeInterval(options.timeInterval); if (this[0] && this[0].data && this[0].data.length) { this.currentSize = this[0].data.length; this.currentIndex = this[0].data.length; } else { this.currentSize = 0; this.currentIndex = 0; } this.maxDataPoints = options.maxDataPoints; if (data && (typeof(data) == "object") && (data instanceof Array)) { data.forEach( function (item) { this.addItem(item) }, this ); this.currentSize += 1; this.currentIndex += 1; } // reset timeBase for zero-filled values if needed this.timeBase -= (this.maxDataPoints - this.currentSize) * this.timeInterval; // zero-fill up to maxDataPoints size if we don't have that much data yet if ((typeof(this.maxDataPoints) !== 'undefined') && (this.currentSize < this.maxDataPoints)) { for (var i = this.maxDataPoints - this.currentSize - 1; i > 0; i--) { this.currentSize += 1; this.currentIndex += 1; this.forEach( function (item) { item.data.unshift({ x: ((i-1) * this.timeInterval || 1) + this.timeBase, y: 0, i: i }); }, this ); } } }, addData: function($super, data) { $super(data) this.currentSize += 1; this.currentIndex += 1; if (this.maxDataPoints !== undefined) { while (this.currentSize > this.maxDataPoints) { this.dropData(); } } }, dropData: function() { this.forEach(function(item) { item.data.splice(0, 1); } ); this.currentSize -= 1; }, getIndex: function () { return this.currentIndex; } } ); ganglia-web-3.6.1/js/rickshaw.min.js000066400000000000000000001006601231750357400172570ustar00rootroot00000000000000Rickshaw={namespace:function(a,b){var c=a.split("."),d=typeof global!="undefined"?global:window,e=d.Rickshaw;for(var f=1,g=c.length;f=this.window.xMax&&(b=!1),b}return!0},this.onUpdate=function(a){this.updateCallbacks.push(a)},this.registerRenderer=function(a){this._renderers=this._renderers||{},this._renderers[a.name]=a},this.setRenderer=function(a){if(!this._renderers[a])throw"couldn't find renderer "+a;this.renderer=this._renderers[a]},this.initialize(a)},Rickshaw.namespace("Rickshaw.Fixtures.Color"),Rickshaw.Fixtures.Color=function(){this.schemes={},this.schemes.spectrum14=["#ecb796","#dc8f70","#b2a470","#92875a","#716c49","#d2ed82","#bbe468","#a1d05d","#e7cbe6","#d8aad6","#a888c2","#9dc2d3","#649eb9","#387aa3"].reverse(),this.schemes.spectrum2000=["#57306f","#514c76","#646583","#738394","#6b9c7d","#84b665","#a7ca50","#bfe746","#e2f528","#fff726","#ecdd00","#d4b11d","#de8800","#de4800","#c91515","#9a0000","#7b0429","#580839","#31082b"],this.schemes.spectrum2001=["#2f243f","#3c2c55","#4a3768","#565270","#6b6b7c","#72957f","#86ad6e","#a1bc5e","#b8d954","#d3e04e","#ccad2a","#cc8412","#c1521d","#ad3821","#8a1010","#681717","#531e1e","#3d1818","#320a1b"],this.schemes.classic9=["#423d4f","#4a6860","#848f39","#a2b73c","#ddcb53","#c5a32f","#7d5836","#963b20","#7c2626","#491d37","#2f254a"].reverse(),this.schemes.httpStatus={503:"#ea5029",502:"#d23f14",500:"#bf3613",410:"#efacea",409:"#e291dc",403:"#f457e8",408:"#e121d2",401:"#b92dae",405:"#f47ceb",404:"#a82a9f",400:"#b263c6",301:"#6fa024",302:"#87c32b",307:"#a0d84c",304:"#28b55c",200:"#1a4f74",206:"#27839f",201:"#52adc9",202:"#7c979f",203:"#a5b8bd",204:"#c1cdd1"},this.schemes.colorwheel=["#b5b6a9","#858772","#785f43","#96557e","#4682b4","#65b9ac","#73c03a","#cb513a"].reverse(),this.schemes.cool=["#5e9d2f","#73c03a","#4682b4","#7bc3b8","#a9884e","#c1b266","#a47493","#c09fb5"],this.schemes.munin=["#00cc00","#0066b3","#ff8000","#ffcc00","#330099","#990099","#ccff00","#ff0000","#808080","#008f00","#00487d","#b35a00","#b38f00","#6b006b","#8fb300","#b30000","#bebebe","#80ff80","#80c9ff","#ffc080","#ffe680","#aa80ff","#ee00cc","#ff8080","#666600","#ffbfff","#00ffcc","#cc6699","#999900"]},Rickshaw.namespace("Rickshaw.Fixtures.RandomData"),Rickshaw.Fixtures.RandomData=function(a){var b;a=a||1;var c=200,d=Math.floor((new Date).getTime()/1e3);this.addData=function(b){var e=Math.random()*100+15+c,f=b[0].length,g=1;b.forEach(function(b){var c=Math.random()*20,h=e/25+g++ +(Math.cos(f*g*11/960)+2)*15+(Math.cos(f/7)+2)*7+(Math.cos(f/17)+2)*1;b.push({x:f*a+d,y:h+c})}),c=e*.85}},Rickshaw.namespace("Rickshaw.Fixtures.Time"),Rickshaw.Fixtures.Time=function(){var a=(new Date).getTimezoneOffset()*60,b=this;this.months=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],this.units=[{name:"decade",seconds:315576e3,formatter:function(a){return parseInt(a.getUTCFullYear()/10)*10}},{name:"year",seconds:31557600,formatter:function(a){return a.getUTCFullYear()}},{name:"month",seconds:2635200,formatter:function(a){return b.months[a.getUTCMonth()]}},{name:"week",seconds:604800,formatter:function(a){return b.formatDate(a)}},{name:"day",seconds:86400,formatter:function(a){return a.getUTCDate()}},{name:"6 hour",seconds:21600,formatter:function(a){return b.formatTime(a)}},{name:"hour",seconds:3600,formatter:function(a){return b.formatTime(a)}},{name:"15 minute",seconds:900,formatter:function(a){return b.formatTime(a)}},{name:"minute",seconds:60,formatter:function(a){return a.getUTCMinutes()}},{name:"15 second",seconds:15,formatter:function(a){return a.getUTCSeconds()+"s"}},{name:"second",seconds:1,formatter:function(a){return a.getUTCSeconds()+"s"}}],this.unit=function(a){return this.units.filter(function(b){return a==b.name}).shift()},this.formatDate=function(a){return a.toUTCString().match(/, (\w+ \w+ \w+)/)[1]},this.formatTime=function(a){return a.toUTCString().match(/(\d+:\d+):/)[1]},this.ceil=function(a,b){if(b.name=="month"){var c=new Date((a+b.seconds-1)*1e3);return(new Date(c.getUTCFullYear(),c.getUTCMonth()+1,1,0,0,0,0)).getTime()/1e3}if(b.name=="year"){var c=new Date((a+b.seconds-1)*1e3);return(new Date(c.getUTCFullYear(),1,1,0,0,0,0)).getTime()/1e3}return Math.ceil(a/b.seconds)*b.seconds}},Rickshaw.namespace("Rickshaw.Fixtures.Number"),Rickshaw.Fixtures.Number.formatKMBT=function(a){return a>=1e12?a/1e12+"T":a>=1e9?a/1e9+"B":a>=1e6?a/1e6+"M":a>=1e3?a/1e3+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.Fixtures.Number.formatBase1024KMGTP=function(a){return a>=0x4000000000000?a/0x4000000000000+"P":a>=1099511627776?a/1099511627776+"T":a>=1073741824?a/1073741824+"G":a>=1048576?a/1048576+"M":a>=1024?a/1024+"K":a<1&&a>0?a.toFixed(2):a==0?"":a},Rickshaw.namespace("Rickshaw.Color.Palette"),Rickshaw.Color.Palette=function(a){var b=new Rickshaw.Fixtures.Color;a=a||{},this.schemes={},this.scheme=b.schemes[a.scheme]||a.scheme||b.schemes.colorwheel,this.runningIndex=0,this.color=function(a){return this.scheme[a]||this.scheme[this.runningIndex++]||"#808080"}},Rickshaw.namespace("Graph.Ajax"),Rickshaw.Graph.Ajax=function(a){var b=this;this.dataURL=a.dataURL,$.ajax({url:this.dataURL,complete:function(c,d){d==="error"&&console.log("error loading dataURL: "+this.dataURL);var e=JSON.parse(c.responseText);if(typeof a.onData=="function"){var f=a.onData(e);e=f}a.series?a.series.forEach(function(a){var b=a.key||a.name;if(!b)throw"series needs a key or a name";e.forEach(function(c){var d=c.key||c.name;if(!d)throw"data needs a key or a name";if(b==d){var e=["color","name","data"];e.forEach(function(b){a[b]=a[b]||c[b]})}})}):a.series=e,b.graph=new Rickshaw.Graph(a),b.graph.render(),typeof a.onComplete=="function"&&a.onComplete(b)}})},Rickshaw.namespace("Rickshaw.Graph.Annotate"),Rickshaw.Graph.Annotate=function(a){var b=this.graph=a.graph;this.elements={timeline:a.element};var c=this;this.data={},this.elements.timeline.classList.add("rickshaw_annotation_timeline"),this.add=function(a,b){c.data[a]=c.data[a]||{boxes:[]},c.data[a].boxes.push({content:b})},this.update=function(){for(var a in c.data){var b=c.data[a],d=c.graph.x(a);if(d>c.graph.x.range()[1]){b.element&&(b.element.style.display="none");continue}if(!b.element){var e=b.element=document.createElement("div");e.classList.add("annotation"),this.elements.timeline.appendChild(e),e.addEventListener("click",function(a){e.classList.toggle("active"),b.line.classList.toggle("active")},!1)}b.element.style.left=d+"px",b.element.style.display="block",b.boxes.forEach(function(a){var e=a.element;e||(e=a.element=document.createElement("div"),e.classList.add("content"),e.innerHTML=a.content,b.element.appendChild(e),b.line=document.createElement("div"),b.line.classList.add("annotation_line"),c.graph.element.appendChild(b.line)),b.line.style.left=d+"px"})}},this.graph.onUpdate(function(){c.update()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Time"),Rickshaw.Graph.Axis.Time=function(a){var b=this;this.graph=a.graph,this.elements=[],this.ticksTreatment=a.ticksTreatment||"plain",this.fixedTimeUnit=a.timeUnit;var c=new Rickshaw.Fixtures.Time;this.appropriateTimeUnit=function(){var a,b=c.units,d=this.graph.x.domain(),e=d[1]-d[0];return b.forEach(function(b){Math.floor(e/b.seconds)>=2&&(a=a||b)}),a||c.units[c.units.length-1]},this.tickOffsets=function(){var a=this.graph.x.domain(),b=this.fixedTimeUnit||this.appropriateTimeUnit(),d=Math.ceil((a[1]-a[0])/b.seconds),e=a[0],f=[];for(var g=0;gb.graph.x.range()[1])return;var c=document.createElement("div");c.style.left=b.graph.x(a.value)+"px",c.classList.add("x_tick"),c.classList.add(b.ticksTreatment);var d=document.createElement("div");d.classList.add("title"),d.innerHTML=a.unit.formatter(new Date(a.value*1e3)),c.appendChild(d),b.graph.element.appendChild(c),b.elements.push(c)})},this.graph.onUpdate(function(){b.render()})},Rickshaw.namespace("Rickshaw.Graph.Axis.Y"),Rickshaw.Graph.Axis.Y=function(a){var b=this;this.graph=a.graph,this.orientation=a.orientation||"right";var c=75;this.ticks=a.ticks||Math.floor(this.graph.height/c),this.tickSize=a.tickSize||4,this.ticksTreatment=a.ticksTreatment||"plain";if(a.element){var d=.1;if(!a.width||!a.height)var e=window.getComputedStyle(a.element,null),f=parseInt(e.getPropertyValue("width")),g=parseInt(e.getPropertyValue("height"));this.width=a.width||f||this.graph.width*d,this.height=a.height||g||this.graph.height,this.vis=d3.select(a.element).append("svg:svg").attr("class","rickshaw_graph y_axis").attr("width",this.width).attr("height",this.height*(1+d)),this.element=this.vis[0][0],this.element.style.position="relative";var h=this.height*d;this.element.style.top=-1*h+"px",this.element.style.paddingTop=h+"px"}else this.vis=this.graph.vis;this.render=function(){var b=d3.svg.axis().scale(this.graph.y).orient(this.orientation);b.tickFormat(a.tickFormat||function(a){return a});if(this.orientation=="left")var c="translate("+this.width+", 0)";this.element&&this.vis.selectAll("*").remove(),this.vis.append("svg:g").attr("class",["y_ticks",this.ticksTreatment].join(" ")).attr("transform",c).call(b.ticks(this.ticks).tickSubdivide(0).tickSize(this.tickSize));var d=(this.orientation=="right"?1:-1)*this.graph.width;this.graph.vis.append("svg:g").attr("class","y_grid").call(b.ticks(this.ticks).tickSubdivide(0).tickSize(d))},this.graph.onUpdate(function(){b.render()})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Highlight"),Rickshaw.Graph.Behavior.Series.Highlight=function(a){this.graph=a.graph,this.legend=a.legend;var b=this,c={};this.addHighlightEvents=function(a){a.element.addEventListener("mouseover",function(d){b.legend.lines.forEach(function(b){if(a===b)return;c[b.series.name]=c[b.series.name]||b.series.color,b.series.color=d3.interpolateRgb(b.series.color,d3.rgb("#d8d8d8"))(.8).toString()}),b.graph.update()},!1),a.element.addEventListener("mouseout",function(a){b.legend.lines.forEach(function(a){c[a.series.name]&&(a.series.color=c[a.series.name])}),b.graph.update()},!1)},this.legend&&this.legend.lines.forEach(function(a){b.addHighlightEvents(a)})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Order"),Rickshaw.Graph.Behavior.Series.Order=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;$(function(){$(b.legend.list).sortable({containment:"parent",tolerance:"pointer",update:function(a,c){var d=[];$(b.legend.list).find("li").each(function(a,b){if(!b.series)return;d.push(b.series)});for(var e=b.graph.series.length-1;e>=0;e--)b.graph.series[e]=d.shift();b.graph.update()}}),$(b.legend.list).disableSelection()}),this.graph.onUpdate(function(){var a=window.getComputedStyle(b.legend.element).height;b.legend.element.style.height=a})},Rickshaw.namespace("Rickshaw.Graph.Behavior.Series.Toggle"),Rickshaw.Graph.Behavior.Series.Toggle=function(a){this.graph=a.graph,this.legend=a.legend;var b=this;this.addAnchor=function(a){var b=document.createElement("a");b.innerHTML="✔",b.classList.add("action"),a.element.insertBefore(b,a.element.firstChild),b.onclick=function(b){a.series.disabled?(a.series.enable(),a.element.classList.remove("disabled")):(a.series.disable(),a.element.classList.add("disabled"))}},this.legend&&this.legend.lines.forEach(function(a){b.addAnchor(a)}),this._addBehavior=function(){this.graph.series.forEach(function(a){a.disable=function(){if(b.graph.series.length<=1)throw"only one series left";a.disabled=!0,b.graph.update()},a.enable=function(){a.disabled=!1,b.graph.update()}})},this._addBehavior(),this.updateBehaviour=function(){this._addBehavior()}},Rickshaw.namespace("Rickshaw.Graph.HoverDetail"),Rickshaw.Graph.HoverDetail=function(a){var b=this.graph=a.graph,c=a.xFormatter||function(a){return(new Date(a*1e3)).toUTCString()},d=a.yFormatter||function(a){return a.toFixed(2)},e=this.element=document.createElement("div");e.className="detail",this.visible=!0,b.element.appendChild(e);var f=this;this.lastEvent=null,this.update=function(a){a=a||this.lastEvent;if(!a)return;this.lastEvent=a;if(a.target.nodeName!="path"&&a.target.nodeName!="svg")return;var c=a.offsetX||a.layerX,d=a.offsetY||a.layerY,e=b.x.invert(c),g=b.stackedData,h=g.slice(-1).shift(),i=d3.scale.linear().domain([h[0].x,h.slice(-1).shift().x]).range([0,h.length]),j=Math.floor(i(e)),k=j||0;for(var l=j;le){k=l;break}g[0][l+1]a.value.y0&&h0?this[0].data.forEach(function(b){a.data.push({x:b.x,y:0})}):a.data.length==0&&a.data.push({x:this.timeBase-(this.timeInterval||0),y:0}),this.push(a),this.legend&&this.legend.addLine(this.itemByName(a.name))},addData:function(a){var b=this.getIndex();Rickshaw.keys(a).forEach(function(a){this.itemByName(a)||this.addItem({name:a})},this),this.forEach(function(c){c.data.push({x:(b*this.timeInterval||1)+this.timeBase,y:a[c.name]||0})},this)},getIndex:function(){return this[0]&&this[0].data&&this[0].data.length?this[0].data.length:0},itemByName:function(a){for(var b=0;b0;d--)this.currentSize+=1,this .currentIndex+=1,this.forEach(function(a){a.data.unshift({x:((d-1)*this.timeInterval||1)+this.timeBase,y:0,i:d})},this)},addData:function($super,a){$super(a),this.currentSize+=1,this.currentIndex+=1;if(this.maxDataPoints!==undefined)while(this.currentSize>this.maxDataPoints)this.dropData()},dropData:function(){this.forEach(function(a){a.data.splice(0,1)}),this.currentSize-=1},getIndex:function(){return this.currentIndex}});ganglia-web-3.6.1/js/tasseo.js000066400000000000000000000166741231750357400161730ustar00rootroot00000000000000var graphs = []; // rickshaw objects var datum = []; // metric data var urls = []; // graphite urls var aliases = []; // alias strings // minutes of data in the live feed var period = (typeof period == 'undefined') ? 30 : period; // construct a url function constructUrl(index, period) { urls[index] = ganglia_url + '/graph.php?cs=-' + encodeURI(period) + '%20min&' + encodeURI(metrics[index].graph_args) + '&live=1'; } // build our graph objects function constructGraphs() { for (var i=0; i warning) { if (lastValue > critical) { graphs[j].series[0].color = '#d59295'; } else if (lastValue > warning) { graphs[j].series[0].color = '#f5cb56'; } else { graphs[j].series[0].color = '#afdab1'; } } else { if (lastValue < critical) { graphs[j].series[0].color = '#d59295'; } else if (lastValue < warning) { graphs[j].series[0].color = '#f5cb56'; } else { graphs[j].series[0].color = '#afdab1'; } } // we want to render immediately, i.e. // as soon as ajax completes // used for time period / pause view if (immediately) { updateGraphs(j); } j = null; values = null; }, i); } // we can wait until all data is gathered, i.e. // the live refresh should happen synchronously if (!immediately) { for (var i=0; i 0) { myDatum[0] = { x: d[0].datapoints[0][1], y: d[0].datapoints[0][0] || graphs[i].lastKnownValue || 0 }; for (var j=1; j' + metrics[i].unit + ''); } } else { $('.overlay-name' + i).text(aliases[i]) $('.overlay-number' + i).html('NF'); } } // add our containers for (var i=0; i

    '); } // build our graph objects constructGraphs(); // set our last known value at invocation Rickshaw.Graph.prototype.lastKnownValue = 0; // set our theme var myTheme = (typeof theme == 'undefined') ? 'default' : theme; if (myTheme === "dark") { enableNightMode(); } // initial load screen refreshData(); for (var i=0; i'); } else { $('.overlay-number' + i).html(''); } } // define our refresh and start interval var refreshInterval = (typeof refresh == 'undefined') ? 15000 : refresh; var refreshId = setInterval(refreshData, refreshInterval); // set our "live" interval hint $('#toolbar ul li.timepanel a.play').text(period + 'min'); // activate night mode function enableNightMode() { $('body').addClass('night'); $('div#title h1').addClass('night'); $('div#graph svg').css('opacity', '0.8'); $('div#overlay-name').addClass('night'); $('div#overlay-number').addClass('night'); $('div#toolbar ul li.timepanel').addClass('night'); } // deactivate night mode function disableNightMode() { $('body').removeClass('night'); $('div#title h1').removeClass('night'); $('div#graph svg').css('opacity', '1.0'); $('div#overlay-name').removeClass('night'); $('div#overlay-number').removeClass('night'); $('div#toolbar ul li.timepanel').removeClass('night'); } // activate night mode by click $('li.toggle-night a').click(function() { console.log(this); if ($('body').hasClass('night')) { disableNightMode(); } else { enableNightMode(); } }); // toggle number display $('li.toggle-nonum a').click(function() { $('div#overlay-number').toggleClass('nonum'); }); // time panel, pause live feed and show range $('#toolbar ul li.timepanel a.range').click(function() { var period = $(this).attr("title"); for (var n=0; n H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATx{t]U?{syn$}K_B8b[*58>@AbDbq ㌂ : UQy# (U*X)Ж>RBI}qo7M(ڦkusewϓ#֌gȣm“'WcYg86ΰ1ÊB r̰n̙^'Wɓ+ … Y`A>2ô'|: "p%Kǂ^f߾dfΜRN@gGv}P!I-بvbMMͽcAAϟ϶m۸{;#TL坘ߵ(6ZƊ^F,Xuov`ЕNֲdҘ>3ER9sfg(gT>,+VYhQHYRH$ Wrk]OכC}CeE徜&o\o\|rzw'D2ai 񑼼S0?Bo̙3‰'vHaCUSSwL6X<-HCpȡf 9cT^]Sqe;o=_:tH)E))%B a 0$v")$J *t6 \f̙8 )%/94 2Ȯn޳7prgz\ZZ2\v9l Ӈ%KJk2<ϧǕũorTvoQSru]|0T iReOb12 :Jo8{0Û!dZLieQ"8}-G4\F*~˼ !pD3=Cc{Cx_ 88.Q~~/q]/_ŗ.@gX/00M(1MA-4:Z Bg% k?.ӐFR^}6?Fv9 X8iTbK%!&:T.R}W@jO 42]0Sa(ֵ] |H)w$03jk'ȡpS/lnHˡ, HSnw-6PAzx59ZmҫijhVaͰn|4quPt0)SkEfQ3L)ս GeRJ|CJ1 L75 iJ:iak[L2;m~sr.CS,}s=-(yJn}ZUO;:iSk<_9By$Ԁi 'h>"E)is4֓ebI)=C&;b 8wu_Bp 8=.>=t|I<OBF}A6j(Lb75Ih}^A纸OiI&5 f;o" Z;iK8!3!$j&4=Eۇ,e3 zs6Jx£#IyH(p=p|hˊie`lVq+): $&q[ǖa{1 mdwuו'umH|ڦyY4G=>L{σh92?Zu9g4)eC*@L³{*oR3d(Ku)*n]r8}bbngjA!ԡCeP(q$X)O1xʣP"YP!q87@M]_P!y]h6mw rf$=agL,SXF/_e)s儬NM$ fᔹJ_>Ð >ղAAY/_8 !i k׮4ML1]M'gÆ }??*"3~RjeE2[4%y]۷n:d )9`˾cO?A,"'oI3hmk?l .͘7틦ODWc<}S}TWБ. `ÍY'6)۷uGg#d+BWj<'QvxP@6Bi)/khVʉ15@B.W"M!@ h':@X= `e.#X6-h /90K 2[V@Ajn.2r: I<*ι#T~:kBԽݣa#Ä40JоrHi $S<'M a*koCF煓&CfѐGH#\B!dlC4xMcTr(;@QKrh7rm:!D{6{(y *$7 7 8  LPkBWѺ/hE+g 3f^hh/U*ꪂk:t cGfqf&i+wiߎ>jIqw8G޹$6;>a'UWFkFҟ 328cWwyJ``熞Ӯϳa1İ.<N^TD6=`)|؎sV^L~''s8<>^9T/i9LPuӆ d'|;qrgrcicar@;SlqG{6SN]twoO-4^i1Jj6nδ<-Ӹ&=oٗ'9Qoϰ$XQ7ܞ*FW-dw!О/'KxfV/t3,h'*#%TE[E}s6Eq{Z9ca~oGՊ^iTݔ^.8'oz`ؘgx?(8ΰq Q0/H$3@;pɪg~O.-\W]uKĹK*~ٽax׭'?!wVk7Yruk=b>8t%TPSX!W6%I{Lw~''͚]PVXpL+?\^e;#[Q~oܹo$mUPjK7!z}d/^yi6eA^qx[ܻk/ D^ ßK}HXa&uSyEZ[s_^OW~u+YW\tKe[E1ud{UˎвR?n/o=/ #%K0o?Г۶ݰj3M.v`gL-s c_c|w#mdͽp?F.OeCzJ.zcij}9OZ'B;ƙg֭[?ض i& uphlDOMum?Lgԧ`:ƌ]?75wl]8PQzo)ٲ(9Z(vu5k1a&1 2e W`668qhmsP8qmhޫ{+ѺC ]#ʉBܗٙkK}v$7NY'MZ9R.]~gr6)F#>úA{MUvQ `U}o %QøP'T,Cngӹ};o8eٲ^0Q99t9VβkFIӾүGo6tI 0K&CMMisS?vq eoUzQsJ:ڿfPm2ba.%{| ?F7$hV7~ݱ!ĺ@; )K^!pi{*`9YY_>e_ֺq!& BZi&*Ss!ɷt^I&St:瑶LuF硩AzIENDB`ganglia-web-3.6.1/js/themes/apple/dot_for_ie.gif000066400000000000000000000000531231750357400215050ustar00rootroot00000000000000GIF89a!,D^;ganglia-web-3.6.1/js/themes/apple/style.css000066400000000000000000000076231231750357400205710ustar00rootroot00000000000000/* * jsTree apple theme 1.0 * Supported features: dots/no-dots, icons/no-icons, focused, loading * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search */ .jstree-apple > ul { background:url("bg.jpg") left top repeat; } .jstree-apple li, .jstree-apple ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; } .jstree-apple li { background-position:-90px 0; background-repeat:repeat-y; } .jstree-apple li.jstree-last { background:transparent; } .jstree-apple .jstree-open > ins { background-position:-72px 0; } .jstree-apple .jstree-closed > ins { background-position:-54px 0; } .jstree-apple .jstree-leaf > ins { background-position:-36px 0; } .jstree-apple a { border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px; text-shadow:1px 1px 1px white; } .jstree-apple .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 3px 0 1px; text-shadow:1px 1px 1px silver; } .jstree-apple .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 3px 0 1px; } .jstree-apple a .jstree-icon { background-position:-56px -20px; } .jstree-apple a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; } .jstree-apple.jstree-focused { background:white; } .jstree-apple .jstree-no-dots li, .jstree-apple .jstree-no-dots .jstree-leaf > ins { background:transparent; } .jstree-apple .jstree-no-dots .jstree-open > ins { background-position:-18px 0; } .jstree-apple .jstree-no-dots .jstree-closed > ins { background-position:0 0; } .jstree-apple .jstree-no-icons a .jstree-icon { display:none; } .jstree-apple .jstree-search { font-style:italic; } .jstree-apple .jstree-no-icons .jstree-checkbox { display:inline-block; } .jstree-apple .jstree-no-checkboxes .jstree-checkbox { display:none !important; } .jstree-apple .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; } .jstree-apple .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; } .jstree-apple .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; } .jstree-apple .jstree-checked > a > .checkbox:hover { background-position:-38px -37px; } .jstree-apple .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; } .jstree-apple .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; } #vakata-dragged.jstree-apple ins { background:transparent !important; } #vakata-dragged.jstree-apple .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; } #vakata-dragged.jstree-apple .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; } #jstree-marker.jstree-apple { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; } .jstree-apple a.jstree-search { color:aqua; } .jstree-apple .jstree-locked a { color:silver; cursor:default; } #vakata-contextmenu.jstree-apple-context, #vakata-contextmenu.jstree-apple-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; } #vakata-contextmenu.jstree-apple-context li { } #vakata-contextmenu.jstree-apple-context a { color:black; } #vakata-contextmenu.jstree-apple-context a:hover, #vakata-contextmenu.jstree-apple-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } #vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a, #vakata-contextmenu.jstree-apple-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; } #vakata-contextmenu.jstree-apple-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; } #vakata-contextmenu.jstree-apple-context li ul { margin-left:-4px; } /* TODO: IE6 support - the `>` selectors */ganglia-web-3.6.1/js/themes/apple/throbber.gif000066400000000000000000000034711231750357400212120ustar00rootroot00000000000000GIF89aFFFzzzXXX$$$666hhh! NETSCAPE2.0!Created with ajaxload.info! ,w  !DBAH¬aD@ ^AXP@"UQ# B\; 1 o:2$v@ $|,3 _# d53" s5 e!! ,v i@e9DAA/`ph$Ca%@ pHxFuSx# .݄YfL_" p 3BW ]|L \6{|z87[7!! ,x  e9DE"2r,qPj`8@8bH, *0- mFW9LPE3+ (B"  f{*BW_/ @_$~Kr7Ar7!! ,v 4e9!H"* Q/@-4ép4R+-pȧ`P(6᠝U/  *,)(+/]"lO/*Ak K]A~666!! ,l ie9"* -80H=N; TEqe UoK2_WZ݌V1jgWe@tuH//w`?f~#6#!! ,~ ,e9"* ; pR%#0` 'c(J@@/1i4`VBV u}"caNi/ ] ))-Lel  mi} me[+!! ,y Ie9"M6*¨"7E͖@G((L&pqj@Z %@wZ) pl( ԭqu*R&c `))( s_J>_\'Gm7$+!! ,w Ie9*, (*(B5[1 ZIah!GexzJ0e6@V|U4Dm%$͛p \Gx }@+| =+ 1- Ea5l)+!! ,y )䨞'AKڍ,E\(l&;5 5D03a0--ÃpH4V % i p[R"| #  6iZwcw*!! ,y )䨞,K*0 a;׋аY8b`4n ¨Bbbx,( Ƚ  % >  2*i* /:+$v*!! ,u )䨞l[$ Jq[q 3`Q[5:IX!0rAD8 CvHPfiiQAP@pC %D PQ46  iciNj0w )#!! ,y ). q ,G Jr(J8 C*B,&< h W~-`, ,>; 8RN<, <1T] c' qk$ @)#!;ganglia-web-3.6.1/js/themes/classic/000077500000000000000000000000001231750357400172275ustar00rootroot00000000000000ganglia-web-3.6.1/js/themes/classic/d.gif000066400000000000000000000056731231750357400201540ustar00rootroot00000000000000GIF89alH쥤靝ttz}rujmx=BN`eq6UYbKNU(/=_gw\`hZct[duZanX_k뜷ccWTFmC(6,Ӷ3YG""%:V=+)'K1lTv^11.jKs5:fnЂD Y%d=ڗBKPPb.h2alyt;{GR쑖\l{LjZo2̭׸@ڿlÜƚƚ̡$ܹRuCpoins y (Ɠ.ɖ1̙4۷qߺėӶݢYިcfީfhߪhjl߬kmѯټ٘WǦݛeޝhޞjߠlrӻ̱ ++++==IIkkͩ!,lH H*\ȰÇ!8Q"ň3jȱƊA:ِǓ(S:e˄.cl%̚,g2O`%ϡ@"-z(DeAP{R-VV b+٦>&-t-Zp4.ԯ'gݻ|֍4@j5\x?=Kw΅!'4[FqZVm#8?M۳ͻ Nn)ׯ~"0A| p"!Llgϝ#;?a?# g& j F+A%CB#;p;, 밣@sDf& `.F=!s? Fx!&dqGTp#<1a$Q&4`r%Xb CÁ@ OPS=7; hI9|QGvjǜ\@GHaWqg[?U2($+( =5LZeh:7\!j[AFXGqnq|inP–@ [=*{; !l dDbq[P>e$ [/[JNPnH?"2 BO\3ODH0C8TQ\裟-` ?@hG@@QJcЄ $@Һ%$bܬ*?B^U`ô 2Zbm{[XA"f8?0Q T"S]Fuq]&xtS];Te ]"QĺnW;?sq h׾U~`E8׬^l)FA:۹@LY~,tg:.0F;H%WJ[ҩh@.V",YDԂ,5Wjި:"0H[m0 d嘵@qry`kl#59Qs!ǯao( lTGrA;\ `Cdհ51q'BM1[[36@ q<W!u4 jа['$Dߘ5(.C08z!nY@B08" iA Hɯ yg>!P!zOB l0߶7Zp!Cplx]ƽ}lo[T'cCHq nDgM6m|5'y;ganglia-web-3.6.1/js/themes/classic/d.png000066400000000000000000000165571231750357400201760ustar00rootroot00000000000000PNG  IHDRlH[ pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxyTŽ?U`aA0.1x,"1 $O4&Q㑜Q1b4!JQ%0k3koR^fez`XιgzִPJ1NmcVSYd 6ȰhzώᄏW.3%'Nܿ,3ly%k;NyjÄ'J/1P5#a0/m&vaχ{^3uW]uaq#hRK:D#&3|Ip6MJ\Wm_yJ9n~dJ.J)>-Sz p$AL7[Ő%RRXXXH)R"@BM i|k"Zeŕ* -pGC>r*Rh9[G;6MG5<.n8(y]v]g;.d-n-iѳmvƏ  s1#U/|z~t$, q:t ]Jq'Qm7g Uy*Hibv}ID4!K-s hߴreV8*V؋Rv9zq,U.p=~"'_aY0xO!>F=CaIU8K4t]qlu 娴j*'QB!E>=MD1PϾ&T4r\/D3GUMUyUM8`[QM2.B{~ r`ow Rո*, /i.1 uu>(:Hœ&vtpMCLR嵙f=qtC- -κCG٭45-%1 NbI_+@ ur [)%c#H&ӵFla#Xؐ1xOLG9$`fz2s1*1 madVB Ϯëu>V8P`a{ZIYSw/܎OP{4O|ba|/K' NF,+{P Hw Dl:"DuH%\l:Ж&[uM5;FnGQ2^>1eBSZQJEcXIIN%tmC2 2,u]O!RnهY Im t_rqq$Ҡ,^uMŒisYdd{pO eS>_ܑqĥ>Q_S K+aHQM4g{0{%5va5.q9er1.0W3<8*u׋RS8^"͙34 "fS M%brl=x?+xWb=R-c 2Otqc)GH!5r;S=J\7b kROB]wS.Z}|T-k޳S =>}ki}*`vXD:V -f"hr!CvB͜+ 3')NkmZ2b_N6gl3p*m5.68ٓK3,99B9QCꨔH"[0u%J9W),-Ôƺ<~5^f;7gsjk ;=7h,o0⋦l eSE,r㗊<Ȝ+L&m}%u8랦j؎n]I$os7=R9֪Q.F 3}0âDnO- MZ̞ɡ✎][LΊn9X~Z9.YA{6lf׳)uDcj s-ܘWn#dX-GZq+*odf8?<.nCA%zs{uGT< smP.1T)y!TqN(.deݵܺ v濗RqorM u\f/&+\dEzX8`3̱pǗt.um=Argm2l,Vx/Yqor.:vy2j䧒p@J`Ȉqj>u&N eqNB`Һ)oZ81na G%s<v Q+Oz| 4kc; }9WSX0|1 v{?ߺl˹{SgzJ=j^2RyĽ(fMTUWOw/)%vz 0 ':^ט1UN/Bo-;k՞;v_U/=&3nݮiD\v_pTR?ԦM*SWT\! ߯~ҁ66' X1r+CJK7׿I(fr_:UŠ%ןrU僪oezq[jF 4a_,c={`o^r .zv`ga9khZlaӯ[4uG+}k<{7 A=Ѹ%W'mO8SJNTUQٳuomR} u֧U8ֿn\h&tiS#ޚ+\ ꫽;,zݬ oBT)8,| F;dĈKCÌѦKk?-ahmtD`PWmyɚلbH!VfΝvѬ V8jԢN?SUb#>Ò=^j3`@mnk5}d-,ѼhTC |5oޣ/qaø:N9uVigM q,}Ue_ROk nzTJq:^kڋ()1MBP]K]?=H7azda]-ʮ5kV=Zr 4}ǰaFugpi ͮUU8xpUtU'XB9AW{OB /?pcMy%|E?J}o!Ԩ4't@\ uJs<8`200JǬ IQqkT` ;+ۆHȶ?IENDB`ganglia-web-3.6.1/js/themes/classic/dot_for_ie.gif000066400000000000000000000000531231750357400220250ustar00rootroot00000000000000GIF89a!,D^;ganglia-web-3.6.1/js/themes/classic/style.css000066400000000000000000000116051231750357400211040ustar00rootroot00000000000000/* * jsTree classic theme 1.0 * Supported features: dots/no-dots, icons/no-icons, focused, loading * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search */ .jstree-classic li, .jstree-classic ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; } .jstree-classic li { background-position:-90px 0; background-repeat:repeat-y; } .jstree-classic li.jstree-last { background:transparent; } .jstree-classic .jstree-open > ins { background-position:-72px 0; } .jstree-classic .jstree-closed > ins { background-position:-54px 0; } .jstree-classic .jstree-leaf > ins { background-position:-36px 0; } .jstree-classic .jstree-hovered { background:#e7f4f9; border:1px solid #e7f4f9; padding:0 2px 0 1px; } .jstree-classic .jstree-clicked { background:navy; border:1px solid navy; padding:0 2px 0 1px; color:white; } .jstree-classic a .jstree-icon { background-position:-56px -19px; } .jstree-classic .jstree-open > a .jstree-icon { background-position:-56px -36px; } .jstree-classic a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; } .jstree-classic.jstree-focused { background:white; } .jstree-classic .jstree-no-dots li, .jstree-classic .jstree-no-dots .jstree-leaf > ins { background:transparent; } .jstree-classic .jstree-no-dots .jstree-open > ins { background-position:-18px 0; } .jstree-classic .jstree-no-dots .jstree-closed > ins { background-position:0 0; } .jstree-classic .jstree-no-icons a .jstree-icon { display:none; } .jstree-classic .jstree-search { font-style:italic; } .jstree-classic .jstree-no-icons .jstree-checkbox { display:inline-block; } .jstree-classic .jstree-no-checkboxes .jstree-checkbox { display:none !important; } .jstree-classic .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; } .jstree-classic .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; } .jstree-classic .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; } .jstree-classic .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; } .jstree-classic .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; } .jstree-classic .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; } #vakata-dragged.jstree-classic ins { background:transparent !important; } #vakata-dragged.jstree-classic .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; } #vakata-dragged.jstree-classic .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; } #jstree-marker.jstree-classic { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; } .jstree-classic a.jstree-search { color:aqua; } .jstree-classic .jstree-locked a { color:silver; cursor:default; } #vakata-contextmenu.jstree-classic-context, #vakata-contextmenu.jstree-classic-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; } #vakata-contextmenu.jstree-classic-context li { } #vakata-contextmenu.jstree-classic-context a { color:black; } #vakata-contextmenu.jstree-classic-context a:hover, #vakata-contextmenu.jstree-classic-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } #vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a, #vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; } #vakata-contextmenu.jstree-classic-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; } #vakata-contextmenu.jstree-classic-context li ul { margin-left:-4px; } /* IE6 BEGIN */ .jstree-classic li, .jstree-classic ins, #vakata-dragged.jstree-classic .jstree-invalid, #vakata-dragged.jstree-classic .jstree-ok, #jstree-marker.jstree-classic { _background-image:url("d.gif"); } .jstree-classic .jstree-open ins { _background-position:-72px 0; } .jstree-classic .jstree-closed ins { _background-position:-54px 0; } .jstree-classic .jstree-leaf ins { _background-position:-36px 0; } .jstree-classic .jstree-open a ins.jstree-icon { _background-position:-56px -36px; } .jstree-classic .jstree-closed a ins.jstree-icon { _background-position:-56px -19px; } .jstree-classic .jstree-leaf a ins.jstree-icon { _background-position:-56px -19px; } #vakata-contextmenu.jstree-classic-context ins { _display:none; } #vakata-contextmenu.jstree-classic-context li { _zoom:1; } .jstree-classic .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; } .jstree-classic .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; } .jstree-classic .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; } /* IE6 END */ganglia-web-3.6.1/js/themes/classic/throbber.gif000066400000000000000000000034711231750357400215320ustar00rootroot00000000000000GIF89aFFFzzzXXX$$$666hhh! NETSCAPE2.0!Created with ajaxload.info! ,w  !DBAH¬aD@ ^AXP@"UQ# B\; 1 o:2$v@ $|,3 _# d53" s5 e!! ,v i@e9DAA/`ph$Ca%@ pHxFuSx# .݄YfL_" p 3BW ]|L \6{|z87[7!! ,x  e9DE"2r,qPj`8@8bH, *0- mFW9LPE3+ (B"  f{*BW_/ @_$~Kr7Ar7!! ,v 4e9!H"* Q/@-4ép4R+-pȧ`P(6᠝U/  *,)(+/]"lO/*Ak K]A~666!! ,l ie9"* -80H=N; TEqe UoK2_WZ݌V1jgWe@tuH//w`?f~#6#!! ,~ ,e9"* ; pR%#0` 'c(J@@/1i4`VBV u}"caNi/ ] ))-Lel  mi} me[+!! ,y Ie9"M6*¨"7E͖@G((L&pqj@Z %@wZ) pl( ԭqu*R&c `))( s_J>_\'Gm7$+!! ,w Ie9*, (*(B5[1 ZIah!GexzJ0e6@V|U4Dm%$͛p \Gx }@+| =+ 1- Ea5l)+!! ,y )䨞'AKڍ,E\(l&;5 5D03a0--ÃpH4V % i p[R"| #  6iZwcw*!! ,y )䨞,K*0 a;׋аY8b`4n ¨Bbbx,( Ƚ  % >  2*i* /:+$v*!! ,u )䨞l[$ Jq[q 3`Q[5:IX!0rAD8 CvHPfiiQAP@pC %D PQ46  iciNj0w )#!! ,y ). q ,G Jr(J8 C*B,&< h W~-`, ,>; 8RN<, <1T] c' qk$ @)#!;ganglia-web-3.6.1/js/themes/default-rtl/000077500000000000000000000000001231750357400200315ustar00rootroot00000000000000ganglia-web-3.6.1/js/themes/default-rtl/d.gif000066400000000000000000000054701231750357400207510ustar00rootroot00000000000000GIF89aZH쥤uu~~JJMrujmx=BN`eq6GLWMR]UYb(/=_gw\`hZct[duZanX_k뜷ccWTFmC(6,Ӷ3YG""%:V=+)'K1lTv^11.jKs5:fnD Y%d=σڗBKPPb.h2alyt;{GR쑖\l{Zo2ǺסÜͧܡWįtɊȐřSglf˶ΘѾߺӶݢYߨcާcfީehߪhjl߬kmټ٘WǦݙbݛeޝhޞjߠlrͱӻ &&++55MMnn޸ͩYYY&&&%%%!,ZH H*\ȰÇ@'bʼn3jȱ#ʼnHɁ8Ĕ WdhB+O"'F}l*/gĉ@&$j!9v"$ZT'QU0rYcnzPُ z+uYdbmk~>Uy7/C}ZmX0I{oDecKL˘3k̹@ECGʔR}Ɨ|۸-;<]P&>x|i]>ҝѣy4Μ$q`}q@QitK,B pP / O;갳:s94s}=NcENb C8N91 0brG%p  ;︓Pwaǖut9F :A 0FqqAH$ BA@ H;N2lS ]1799hF sFD!) 9)¯@% 3N:N)PCpaE DnF]anE|WP& BH& +"pA%j? ̦I[Eoe(!C_F_la6@"A" !H2 {;#<3,ۤ #-`anhpagXl!P$aA t!HʈH#o[̣>2(_(}@= 0QDD?8Ўz9!a;r؇TNvfK,M;<;APR^xH6lQ:ڱÉcis{!AABvыB̰!pg&2JZr̤&7Y/FIJ_̂ )Pa St(BJQ▥e)Na U2/Ib"!(p Y8˄)R X8E.@ Y2H- [™E.n t MD'< PB.E) eXF9ω Ys x%/'h}l-- d M2af: D6թO"ԄONUx8e c@FBьfk:6mA 4A FCf[$ogN$ ;ganglia-web-3.6.1/js/themes/default-rtl/d.png000066400000000000000000000164431231750357400207720ustar00rootroot00000000000000PNG  IHDRZHv pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FNIDATxyU՝?{{t ɪѨA4Hpd ֨!5)LLFMMƒhʨqcHdrDVG7n7zn0اV{~m%" Cq&&G 3zÌgFXbveڴir(35 Gyd @9z f=zٲerBJ)?bk@n7oX`֫_o9sDrZ s z׀Q=#w( 3+y cbES1Xv7PPOM"k_=bt4eTUU Jp2dʕ}b1>l!mX~hBbrbX=5(:%]0tXb;k0G軘'҄GuEZιtɠ:eZ)'se;n=3zBoӡc cY6Z+(XJcvނݻ׶&m5R]6GpH 0-!#1[ @@YeceK=w)L0,_NͰdحXWo]AHk[;ՒJa'o_/ltd ðd&!hT& U)gku:6EUKDeKdNmH6m]` yͦ-$sdX6wm`^MuU "!">( }PBnG1e=x=}yOGg_/ #F;9K) 0,¶m0@0Fm %qӈZL9Q;(H;־ #x?kTT{6Uټk7wr)t`H7^ &0CnbL/p>Y_gY&4@@1SZw΄Ksbkt(.BMU(ks[7HDl(ݿS476\論 }DkB?tGi ` #b5Nv_&Lv7Zk0@k':7v5!mkzjΆ7o}WֆuX͉lմ&x&Sodδ9<¦wA<?*$0nbBq2/^U8: C|Gk1w5ڲ:'h@U-n |vi؅$|2Vuͫ귈E`k(c}'N?P~CQO'R~:dA4g1d tLGc q ؓH-h$juKI" 0;/Kj-;.SoxFbv&u Tn̙<7Hc׳o߹ÑSOQ-YAdy{>F-Ɂs>gAK;ؤmȽ x!YcNgx ؖbDz,W͸S87HbUh#GK4s(/e#drY C%B1az_0/ Hvs;?3/ض'Zg}25Ut&4E!6#&WO "neٖb8yl~gV )R6,DS& ܴە5#B; $F^CX9#KKFn׿hdcܼQHǶ8mWiDZzbHHe3'#D%?{l&""̜8[IeiP8N! 0ΘT۱ѶCaBH'l3Eq˿_?9'q "3O]݋rg9@c,3aY"'f#g R`JilKN3a;wWcccc-U/I{Pu7a0G0hv+ :։ðf9y'p9z쵌 t*⥗^"[ h*[hZr%mc AYWt4>-Ne;RXx1nܐHP2AdB9!AHc[|_v > QaD־y3c}gxIBdѓc;ܜ =<>I+k/PWJY8A1nÿ-{pH1J(mugX93ALLYvOED9::S.#Z3yP/Vo0Z3oB;#0^ʊ H)uRƟyV,s2xlDڦV.O-2(meEP2?o[3̱ o?shnLVXʎQ&PliDA:7hQr2@k UHS tsAMF]3QfOf36֬vX /Ρ4)!W1.[k[{!1PsF^F@s{<#5Zw^ik@gGlJ^1 autg;e6hi|͕,E%F{HM.%2tX+ 7;yPr䫨>>n\?|dftRTZ͓.o?agd?@$D`JS%o,FMK/->['cd^z;CёXW`Ze- '( r,WoxrEtvecEKt{b =?AϱϵFzlôLLn1tR\a8'8JH Lch8cڎ2Lul~X\l3GF;Ch 0ӑr]J!WL9p ->뤼sYA"nL||׽is6wmH5W!tjhq&/AB0 /?-FѮtdg~]s/ڲ{9.-}Cp=zqK* =Q؀vM}>@8u5xƌYWǏ_n{cuEY\c(^!y+6\ۏМs"'%Kkj.={e̘+VxRW'XU^~V!9 `<08g_ir.}]^g7`9-K˥3$XXZ{•55&˓gyik%裝=GT`n<x<.QC^GpԢ)s/~fSKkQ׎-WvdU;J2=Xv/Q^Cj*:~ J6lp\/]{ pض v>"VV6bDyI:' r1P |&z{o = \v'L^VS^z<_+yM 0|nz}OyPmת?'qϞ/"mm(ǁR歷>і-п /LmyGx+)SjjFq'.:ǽXgf ݿz,o~Ԏ[@j8OظÎҦr`rO=<&0xe3_t_dvZ#[֎Z(˛qlXk0d?df 6|q54 "7-˙<  5~Y-e6W5&1ܤe?voPMyxۆPꢱȽŝDz 3MQTT:k޼{;VjhX>:۪Έ㫀|u{EPs;^^X_g7iH% tꢛ7T/˷џ-ry^$E JeX tou)˞=ֹsg}K]6ayao+Vv7Z}P}رnqfRkiElYԑ0o.RWѨm~D 4TUU}5?{GyΚgE.*z4imK]0opTV^\J; k`#y@1yږ#vڇ.XVJ)"ԀoA4EEL;vǕ¯?*+ ^,$[((r1uy6K feW.3.2@l`\ =< k=^LA IENDB`ganglia-web-3.6.1/js/themes/default-rtl/dots.gif000066400000000000000000000002041231750357400214650ustar00rootroot00000000000000GIF89al!,l[ C4ΊY}U(Z%xbdlG,KC};KCQs$7KԭFaSjuvfmۦjzG1l궘v_:(;ganglia-web-3.6.1/js/themes/default-rtl/style.css000066400000000000000000000132631231750357400217100ustar00rootroot00000000000000/* * jsTree default-rtl theme 1.0 * Supported features: dots/no-dots, icons/no-icons, focused, loading * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search */ .jstree-default-rtl li, .jstree-default-rtl ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; } .jstree-default-rtl li { background-position:-90px 0; background-repeat:repeat-y; } .jstree-default-rtl li.jstree-last { background:transparent; } .jstree-default-rtl .jstree-open > ins { background-position:-72px 0; } .jstree-default-rtl .jstree-closed > ins { background-position:-54px 0; } .jstree-default-rtl .jstree-leaf > ins { background-position:-36px 0; } .jstree-default-rtl .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; } .jstree-default-rtl .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; } .jstree-default-rtl a .jstree-icon { background-position:-56px -19px; } .jstree-default-rtl a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; } .jstree-default-rtl.jstree-focused { background:#ffffee; } .jstree-default-rtl .jstree-no-dots li, .jstree-default-rtl .jstree-no-dots .jstree-leaf > ins { background:transparent; } .jstree-default-rtl .jstree-no-dots .jstree-open > ins { background-position:-18px 0; } .jstree-default-rtl .jstree-no-dots .jstree-closed > ins { background-position:0 0; } .jstree-default-rtl .jstree-no-icons a .jstree-icon { display:none; } .jstree-default-rtl .jstree-search { font-style:italic; } .jstree-default-rtl .jstree-no-icons .jstree-checkbox { display:inline-block; } .jstree-default-rtl .jstree-no-checkboxes .jstree-checkbox { display:none !important; } .jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; } .jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; } .jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; } .jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; } .jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; } .jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; } #vakata-dragged.jstree-default-rtl ins { background:transparent !important; } #vakata-dragged.jstree-default-rtl .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; } #vakata-dragged.jstree-default-rtl .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; } #jstree-marker.jstree-default-rtl { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; } .jstree-default-rtl a.jstree-search { color:aqua; } .jstree-default-rtl .jstree-locked a { color:silver; cursor:default; } #vakata-contextmenu.jstree-default-rtl-context, #vakata-contextmenu.jstree-default-rtl-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; } #vakata-contextmenu.jstree-default-rtl-context li { } #vakata-contextmenu.jstree-default-rtl-context a { color:black; } #vakata-contextmenu.jstree-default-rtl-context a:hover, #vakata-contextmenu.jstree-default-rtl-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } #vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a, #vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; } #vakata-contextmenu.jstree-default-rtl-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; } #vakata-contextmenu.jstree-default-rtl-context li ul { margin-left:-4px; } /* IE6 BEGIN */ .jstree-default-rtl li, .jstree-default-rtl ins, #vakata-dragged.jstree-default-rtl .jstree-invalid, #vakata-dragged.jstree-default-rtl .jstree-ok, #jstree-marker.jstree-default-rtl { _background-image:url("d.gif"); } .jstree-default-rtl .jstree-open ins { _background-position:-72px 0; } .jstree-default-rtl .jstree-closed ins { _background-position:-54px 0; } .jstree-default-rtl .jstree-leaf ins { _background-position:-36px 0; } .jstree-default-rtl a ins.jstree-icon { _background-position:-56px -19px; } #vakata-contextmenu.jstree-default-rtl-context ins { _display:none; } #vakata-contextmenu.jstree-default-rtl-context li { _zoom:1; } .jstree-default-rtl .jstree-undetermined a .jstree-checkbox { _background-position:-18px -19px; } .jstree-default-rtl .jstree-checked a .jstree-checkbox { _background-position:-36px -19px; } .jstree-default-rtl .jstree-unchecked a .jstree-checkbox { _background-position:0px -19px; } /* IE6 END */ /* RTL part */ .jstree-default-rtl .jstree-hovered, .jstree-default-rtl .jstree-clicked { padding:0 1px 0 2px; } .jstree-default-rtl li { background-image:url("dots.gif"); background-position: 100% 0px; } .jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-36px -19px; margin-left:2px; } .jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:0px -19px; margin-left:2px; } .jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-18px -19px; margin-left:2px; } .jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-36px -37px; } .jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:0px -37px; } .jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-18px -37px; }ganglia-web-3.6.1/js/themes/default-rtl/throbber.gif000066400000000000000000000034711231750357400223340ustar00rootroot00000000000000GIF89aFFFzzzXXX$$$666hhh! NETSCAPE2.0!Created with ajaxload.info! ,w  !DBAH¬aD@ ^AXP@"UQ# B\; 1 o:2$v@ $|,3 _# d53" s5 e!! ,v i@e9DAA/`ph$Ca%@ pHxFuSx# .݄YfL_" p 3BW ]|L \6{|z87[7!! ,x  e9DE"2r,qPj`8@8bH, *0- mFW9LPE3+ (B"  f{*BW_/ @_$~Kr7Ar7!! ,v 4e9!H"* Q/@-4ép4R+-pȧ`P(6᠝U/  *,)(+/]"lO/*Ak K]A~666!! ,l ie9"* -80H=N; TEqe UoK2_WZ݌V1jgWe@tuH//w`?f~#6#!! ,~ ,e9"* ; pR%#0` 'c(J@@/1i4`VBV u}"caNi/ ] ))-Lel  mi} me[+!! ,y Ie9"M6*¨"7E͖@G((L&pqj@Z %@wZ) pl( ԭqu*R&c `))( s_J>_\'Gm7$+!! ,w Ie9*, (*(B5[1 ZIah!GexzJ0e6@V|U4Dm%$͛p \Gx }@+| =+ 1- Ea5l)+!! ,y )䨞'AKڍ,E\(l&;5 5D03a0--ÃpH4V % i p[R"| #  6iZwcw*!! ,y )䨞,K*0 a;׋аY8b`4n ¨Bbbx,( Ƚ  % >  2*i* /:+$v*!! ,u )䨞l[$ Jq[q 3`Q[5:IX!0rAD8 CvHPfiiQAP@pC %D PQ46  iciNj0w )#!! ,y ). q ,G Jr(J8 C*B,&< h W~-`, ,>; 8RN<, <1T] c' qk$ @)#!;ganglia-web-3.6.1/js/themes/default/000077500000000000000000000000001231750357400172325ustar00rootroot00000000000000ganglia-web-3.6.1/js/themes/default/d.gif000066400000000000000000000056001231750357400201450ustar00rootroot00000000000000GIF89alH쥤uu~~JJMrujmx=BN`eq6GLWMR]UYb(/=_gw\`hZct[duZanX_k뜷ccWTFmC(6,Ӷ3YG""%:V=+)'K1lTv^11.jKs5:fnD Y%d=σڗBKPPb.h2alyt;{GR쑖\l{Zo2ǺסÜͧܡWįtږɊȐřSglf˶ΘѾߺӶݢYިcfީfhߪhjl߬kmټ٘WǦݙbݛeޝhޞjߠlrͱӻ &&++55MMnn޸ͩYYY&&&%%%!,lH H*\ȰÇ!8Q"ň3jȱƊA:ِǓ(SB_ȇ,M.r$L{S9N:y N_~jYhҗcG!ҧBR+Oihx+ÉhZ.ش˺pAUWJd^;B5FX]~6yrF1b8ϠCk,ӠI^͚װ%i҄g}Ɨ|B!.< @`^PC'@|"|ҩ_]>ҝ᣾5ჟΜLCW 0"F&, t`B 0Ե>꤃`9 A:FQƋ0C:W@|:A 0bvG&!B ;︓#"N`xqǚvI ;!A @rAɈ$ RÁ Xԙ;9A:I8xAuZ[@谅F<cFV, (! B Ft z!: V1ǹs 6,@PU2zƽZ^ABH!0[гюp鵚.- lm+иV G a3;PPbhWABpaF:I@{#ϡANюB! "x[sJ򕬌ehT#Z2E/~_,"Q0)HLeB)YjP3oY_ 8 ",Nt‚),Az",N"P3lu/bPF1; r X|(Bq=&A~ eX@ Y,t x&7 E'hኌ£|4WOd 'M2a u X*X"ĭNWyH dcH=2Rʌf4I+ʅta]XAfy)byQs!e.v c$Ux+T1AdK^2 1e.! S1ش"c*^a5Ak\fbNl\0a}V!e1V#*@89r%*n *`NsJ'< ~rZ#z;) )`.o}hE-ZQ&z׻Xcw_P`UP/'@>õ(ia ^.x "'T! YXZނE&8E?[\1 W7 dpǫ.c/A29bH1Q6ͦ@LUӜ|>4@GA4Qt7E;f#n " kp7J#d@`/ dhA1 h@5G m%iP+ t`PjHx6AmPAyiA ZX8;qmlg;ظ\p3 b8pjP77 \ۃnp8,A!vAp[9?xĥM]!(ӘFCnhlcF8"&4 6`k86~ oUcC`ЧQ jА5L Fu՝NYh/];ganglia-web-3.6.1/js/themes/default/d.png000066400000000000000000000167231231750357400201740ustar00rootroot00000000000000PNG  IHDRlH[ pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIDATxytUս?{s  DV>:uj >[}h\R۷: NZGV^-( J@!!!d{s{/I-$ku>$c #iHp#F6°0la+WH1cgu 63QʩWZeCe]5Pj*3r:\WN>lVXa O>!L XII ,3:ч*H^WNrrl}2rk x;Mc ͬ^[nE ,c 6l4:ObϾLꏜ\|3>'`MMM<,^x@jh۶mðiAk1 x+Ah1yos}hŵO3lq,dkr5˗p8%mY uN9RʬsB @@r:3ӎ n J,4F,XrYNdwM-55T#sψAgX8fΜ9X,ƚ5kzG"?|!XҊ<'/.H${DkAך տ:_CHxgZy(E>A@%aV?pe}ӦW2mQ,uʹE}FOTRRPP,)RJXBXaI,!m;cIIzUGyD@B$7I127).@GKArm[dmy]' Ƙ5:@)˲رu9$P$ۄek3 P41uB%XݵML2iv, UP'ú;RJ11dQX(:h?e+4CLzkuջZGH٣wA-a_Q`WrNX:@Q>FJ Xӡ` Jr I&ejRJ#c:]zeRJ R&3 cے|([[ܴ)Y9K 4nLhbEoJs,@atߙ~+sg骧Dq '0h@IaYm  }iP韥H:(`1?JSK-yV 7ٟh}1A(2n<.~*H6$oÙ3@38~X@ &H&Pn~ Ì6=8,#t(:UbqD!hM4+1dpbt4l!b'%WCI0ܣq"v$|=m3`  c%=XS 00f Gư uD:At@:iR_OԵ'I`)h͋X.'`֛[-xϺ3İOI^?sc/ecesyWQ06V9`drXq쏖a/m R>dbHxWЦ3Q|V6壦 JgF(ǖOc wb\3;1u1#!Y^(}T"N<ڊeI7Bxv07ᦳ Zl.θ :]]H1 >:/u]\M;'bqݽ'8Yc?tV9JΝB̧ٶm0fOEA$܀*ò$Mu'huL<ڎH!( h*{ҙ"owЫ#rک_#*sMu;\te05e1sֱ@CXryAA@4e)ʹ?ɲp!$%ih#P>T޽{)))I.U-em4Bb5yHkGk& c0̔I{ml]C7j~I ̞}Fc%_\q^z%BPΤmw`xƄ5km[݁Vm&9֭vgө$v]E⮎ͩ(2Iia1]qS?NW_u=#8èݬ󇔏NKkgk/6Io%O/ 06°!F8/ t"9 Ւ=PnHW?t|t2i Fh/ƴE+{Qiw{yB&p1^ֺ QT̻pQ}z06SݽpϽ B{+%#Ch$ xmX'J橂XS65o۰YZ؅c0*@ 倴V)MJIAX!DGjvSyɐR+̵ڭl !ߒ 4n 'n hGQK^?Nג2&pّxCQyc:((KPP 4 EuT`70A"} I iR[E&XZataVu.%/Yr; c:i[{d {3dd1PG XpZM㦜@MQq6xCav0 R,ѩ$U] Bd$:{O{VwTG JQq~y$>6a3'ug`FJ+iRqĮtzJe{s]WޣS~4 X}atȺfrbˡV +[rVf}\"  [ ՍmF}!Uq 1?ŲnlWIK9  )%HL) C8 0KSX\ tΙK83 mH \b(M`ik9.3vJ~e>G3_fF%1f9k7'+QBNfP&àҎ,2;I2gsn~n}d('i/ ,ǟ ,F-d:; &K&.:m._>c, h"T<1W2c~P4w2 S6] '<`j/?.Et*lf=?ƾޏHھΒcCa##Î\F~*Di{ 9i_p`G؋ӧ_[o¸qx﵉};7ZRr^ RWx(>wz8maw|u1wc|4-[=_Q+-\j{eja\&4i˸~ Ϳݼ智{Q}cX+.6cKM=x/TT\zgYژM:XYy^F566FgHWY2mWYti17L/{rtY{B,/-x>NqR--׮'?`fgM7 7;a~C"EEFC 8)x5466>TCE= Ӟܶ܆s|YYS]塟p{څ!W3tֵ_=eqi9E\~ҋ*6bQdw+ # owSIFq`K& nF|+}C={˖\\9K\bYz(ۍ̺lqmޟ+C-ЎcG6f0%J}ܤ~ɀ*a͟{7o`˖/5`7֖)ֹxM:*j5R#^-ʆv ʇwREp9%;YQ1) []kۼPǔ&ec.6+w?/^Z,ڤdROx['@7{,!ڀjpH5(z!qF?qs Ѩ[Z~_6fBLvg B<K&JSs1`L` a]^I&SL:L<\6IENDB`ganglia-web-3.6.1/js/themes/default/style.css000066400000000000000000000110251231750357400211030ustar00rootroot00000000000000/* * jsTree default theme 1.0 * Supported features: dots/no-dots, icons/no-icons, focused, loading * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search */ .jstree-default li, .jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; } .jstree-default li { background-position:-90px 0; background-repeat:repeat-y; } .jstree-default li.jstree-last { background:transparent; } .jstree-default .jstree-open > ins { background-position:-72px 0; } .jstree-default .jstree-closed > ins { background-position:-54px 0; } .jstree-default .jstree-leaf > ins { background-position:-36px 0; } .jstree-default .jstree-hovered { background:#e7f4f9; } .jstree-default .jstree-clicked { background:#beebff; } .jstree-default a .jstree-icon { background-position:-56px -19px; } .jstree-default a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; } .jstree-default.jstree-focused { background:#ffffee; } .jstree-default .jstree-no-dots li, .jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; } .jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; } .jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; } .jstree-default .jstree-no-icons a .jstree-icon { display:none; } .jstree-default .jstree-search { font-style:italic; } .jstree-default .jstree-no-icons .jstree-checkbox { display:inline-block; } .jstree-default .jstree-no-checkboxes .jstree-checkbox { display:none !important; } .jstree-default .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; } .jstree-default .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; } .jstree-default .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; } .jstree-default .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; } .jstree-default .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; } .jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; } #vakata-dragged.jstree-default ins { background:transparent !important; } #vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; } #vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; } #jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; } .jstree-default a.jstree-search { color:aqua; } .jstree-default .jstree-locked a { color:silver; cursor:default; } #vakata-contextmenu.jstree-default-context, #vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; } #vakata-contextmenu.jstree-default-context li { } #vakata-contextmenu.jstree-default-context a { color:black; } #vakata-contextmenu.jstree-default-context a:hover, #vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; } #vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a, #vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; } #vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; } #vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; } /* IE6 BEGIN */ .jstree-default li, .jstree-default ins, #vakata-dragged.jstree-default .jstree-invalid, #vakata-dragged.jstree-default .jstree-ok, #jstree-marker.jstree-default { _background-image:url("d.gif"); } .jstree-default .jstree-open ins { _background-position:-72px 0; } .jstree-default .jstree-closed ins { _background-position:-54px 0; } .jstree-default .jstree-leaf ins { _background-position:-36px 0; } .jstree-default a ins.jstree-icon { _background-position:-56px -19px; } #vakata-contextmenu.jstree-default-context ins { _display:none; } #vakata-contextmenu.jstree-default-context li { _zoom:1; } .jstree-default .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; } .jstree-default .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; } .jstree-default .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; } /* IE6 END */ganglia-web-3.6.1/js/themes/default/throbber.gif000066400000000000000000000034711231750357400215350ustar00rootroot00000000000000GIF89aFFFzzzXXX$$$666hhh! NETSCAPE2.0!Created with ajaxload.info! ,w  !DBAH¬aD@ ^AXP@"UQ# B\; 1 o:2$v@ $|,3 _# d53" s5 e!! ,v i@e9DAA/`ph$Ca%@ pHxFuSx# .݄YfL_" p 3BW ]|L \6{|z87[7!! ,x  e9DE"2r,qPj`8@8bH, *0- mFW9LPE3+ (B"  f{*BW_/ @_$~Kr7Ar7!! ,v 4e9!H"* Q/@-4ép4R+-pȧ`P(6᠝U/  *,)(+/]"lO/*Ak K]A~666!! ,l ie9"* -80H=N; TEqe UoK2_WZ݌V1jgWe@tuH//w`?f~#6#!! ,~ ,e9"* ; pR%#0` 'c(J@@/1i4`VBV u}"caNi/ ] ))-Lel  mi} me[+!! ,y Ie9"M6*¨"7E͖@G((L&pqj@Z %@wZ) pl( ԭqu*R&c `))( s_J>_\'Gm7$+!! ,w Ie9*, (*(B5[1 ZIah!GexzJ0e6@V|U4Dm%$͛p \Gx }@+| =+ 1- Ea5l)+!! ,y )䨞'AKڍ,E\(l&;5 5D03a0--ÃpH4V % i p[R"| #  6iZwcw*!! ,y )䨞,K*0 a;׋аY8b`4n ¨Bbbx,( Ƚ  % >  2*i* /:+$v*!! ,u )䨞l[$ Jq[q 3`Q[5:IX!0rAD8 CvHPfiiQAP@pC %D PQ46  iciNj0w )#!! ,y ). q ,G Jr(J8 C*B,&< h W~-`, ,>; 8RN<, <1T] c' qk$ @)#!;ganglia-web-3.6.1/lib/000077500000000000000000000000001231750357400144535ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Cache/000077500000000000000000000000001231750357400154565ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Cache/Driver_Json.php000066400000000000000000000027641231750357400204240ustar00rootroot00000000000000 ganglia-web-3.6.1/lib/Cache/Driver_Memcache.php000066400000000000000000000024311231750357400212040ustar00rootroot00000000000000get( 'ganglia_cache_' . gethostname() ) !== FALSE; } // end function g_cache_exists function g_cache_serialize($data) { global $conf; $mc = g_get_memcache(); $mc->set( 'ganglia_cache_' . gethostname() , $data ); $mc->set( 'ganglia_cache_timestamp_' . gethostname(), time() ); } // end function g_cache_serialize function g_cache_deserialize() { global $conf; $mc = g_get_memcache(); $index_array = $mc->get( 'ganglia_cache_' . gethostname() ); return $index_array; } // end function g_cache_deserialize function g_cache_expire () { global $conf; $mc = g_get_memcache(); return time() - $mc->get( 'ganglia_cache_timestamp_' . gethostname() ); } // end function g_cache_expire function g_get_memcache() { global $conf; if (!$GLOBALS['__memcached_pool']) { $GLOBALS['__memcached_pool'] = new Memcached(); $GLOBALS['__memcached_pool']->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT); foreach ($conf['memcached_servers'] AS $server) { list($host, $port) = explode(':', $server); $GLOBALS['__memcached_pool']->addServer( $host, (int)$port ); } } return $GLOBALS['__memcached_pool']; } // end function g_get_memcache ?> ganglia-web-3.6.1/lib/Events/000077500000000000000000000000001231750357400157175ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Events/Driver_Json.php000066400000000000000000000115231231750357400206560ustar00rootroot00000000000000 "ok", "event_id" => $event['event_id']); } return $message; } // end method ganglia_events_add ////////////////////////////////////////////////////////////////////////////// // Gets a list of all events that overlap with a specified time range ////////////////////////////////////////////////////////////////////////////// function ganglia_events_get( $start = NULL, $end = NULL ) { global $conf; $events_json = file_get_contents($conf['overlay_events_file']); $orig_events_array = json_decode($events_json, TRUE); // Save some time, pass back if no values given if ( $start == NULL && $end == NULL ) { return $orig_events_array; } $events_array = array(); foreach ($orig_events_array AS $k => $evt) { if ($evt['end_time'] != NULL) { // Duration event if ($start == NULL) { if ($evt['start_time'] <= $end && $evt['end_time'] >= $end) $events_array[] = $evt; } else if ($end == NULL) { if ($evt['start_time'] <= $start && $evt['end_time'] >= $start) $events_array[] = $evt; } else { if ($evt['end_time'] >= $start && $evt['start_time'] <= $end) $events_array[] = $evt; } } else { // Instantaneous event if ($start == NULL && $evt['start_time'] == $end) $events_array[] = $evt; else if ($end == NULL && $evt['start_time'] == $start) $events_array[] = $evt; else if ($evt['start_time'] >= $start && $evt['start_time'] <= $end) $events_array[] = $evt; } } return $events_array; } // end method ganglia_events_get function ganglia_event_delete( $event_id ) { global $conf; $orig_events_array = ganglia_events_get(); $events_array = array(); $event_found = 0; foreach ( $orig_events_array AS $k => $v ) { if ( $v['event_id'] != $event_id ) { $events_array[] = $v; } else { $event_found = 1; } } if ( $event_found == 1 ) { $json = json_encode($events_array); if ( file_put_contents($conf['overlay_events_file'], $json) === FALSE ) { api_return_error( "Can't write to " . $conf['overlay_events_file'] . ". Please check permissions." ); } else { $message = array( "status" => "ok", "message" => "Event ID " . $event_id . " deleted successfully" ); return $message; } } api_return_error( "Event ID ". $event_id . " not found" ); return NULL; // never reached } // end method ganglia_event_delete function ganglia_event_modify( $event ) { global $conf; $event_found = 0; $events_array = ganglia_events_get(); $new_events_array = array(); if (!isset($event['event_id'])) { api_return_error( "Event ID not found" ); } // isset event_id foreach ( $events_array AS $k => $e ) { if ( $e['event_id'] == $event['event_id'] ) { $event_found = 1; if (isset( $event['start_time'] )) { if ( $event['start_time'] == "now" ) { $e['start_time'] = time(); } else if ( is_numeric($event['start_time']) ) { $e['start_time'] = $event['start_time']; } else { $e['start_time'] = strtotime($event['start_time']); } } // end isset start_time foreach(array('cluster', 'description', 'summary', 'grid', 'host_regex') AS $k) { if (isset( $event[$k] )) { $e[$k] = $event[$k]; } } // end foreach if ( isset($event['end_time']) ) { $e['end_time'] = $event['end_time'] == "now" ? time() : strtotime($event['end_time']); } // end isset end_time } // if event_id // Add either original or modified event back in $new_events_array[] = $e; } // foreach events array if ( $event_found == 1 ) { $json = json_encode($new_events_array); if ( file_put_contents($conf['overlay_events_file'], $json) === FALSE ) { api_return_error( "Can't write to file " . $conf['overlay_events_file'] . ". Perhaps permissions are wrong." ); } else { $message = array( "status" => "ok", "message" => "Event ID " . $event_id . " modified successfully" ); } } // end if event_found return $message; } // end method ganglia_event_modify ?> ganglia-web-3.6.1/lib/Events/Driver_Mdb2.php000066400000000000000000000115561231750357400205370ustar00rootroot00000000000000getMessage()); } $sql = "INSERT INTO overlay_events ( description, summary, grid, cluster, host_regex, start_time, end_time ) VALUES ( " . ( isset($event['description']) ? $db->quote( $event['description'], 'text' ) : "NULL" ) . "," . ( isset($event['summary']) ? $db->quote( $event['summary'], 'text' ) : "NULL" ) . "," . ( isset($event['grid']) ? $db->quote( $event['grid'], 'text' ) : "NULL" ) . "," . ( isset($event['cluster']) ? $db->quote( $event['cluster'], 'text' ) : "NULL" ) . "," . ( isset($event['host_regex']) ? $db->quote( $event['host_regex'], 'text' ) : "NULL" ) . "," . ( isset($event['start_time']) ? $db->quote( $event['start_time'], 'integer' ) : "NULL" ) . "," . ( isset($event['end_time']) ? $db->quote( $event['end_time'], 'integer' ) : "NULL" ) . " ) ;"; $result =& $db->exec( $sql ); if (PEAR::isError($result)) { api_return_error( $result->getMessage()); } $event_id = $db->lastInsertID( 'overlay_events', 'event_id' ); if (PEAR::isError($event_id)) { api_return_error( $event_id->getMessage()); } $event_id = strval($event_id); $message = array( "status" => "ok", "event_id" => $event_id ); return $message; } // end method ganglia_events_add ////////////////////////////////////////////////////////////////////////////// // Gets a list of all events in an optional time range ////////////////////////////////////////////////////////////////////////////// function ganglia_events_get( $start = NULL, $end = NULL ) { global $conf; $db =& MDB2::factory( $conf['overlay_events_dsn'] ); if (DB::isError($db)) { api_return_error($db->getMessage()); } $sql = "SELECT * FROM overlay_events "; if ( $start != NULL || $end != NULL ) { $sql .= " WHERE "; $clauses = array(); if ( $start != NULL ) { $clauses[] = "start_time >= " . $db->quote( $start, 'integer' ); } if ( $end != NULL ) { $clauses[] = "start_time <= " . $db->quote( $end, 'integer' ); } $sql .= implode(' AND ', $clauses); } $sql .= " ORDER BY start_time, event_id"; $result =& $db->query( $sql ); if (PEAR::isError($result)) { api_return_error( $result->getMessage()); } $events_array = array(); while ( ( $row = $result->fetchRow( MDB2_FETCHMODE_ASSOC ) ) ) { $events_array[] = $row; } return $events_array; } // end method ganglia_events_get function ganglia_event_delete( $event_id ) { global $conf; $db =& MDB2::factory( $conf['overlay_events_dsn'] ); if (DB::isError($db)) { api_return_error($db->getMessage()); } $sql = "DELETE FROM overlay_events WHERE event_id = " . $db->quote( $event_id, 'integer' ); $result =& $db->query( $sql ); if (PEAR::isError($result)) { api_return_error( $result->getMessage()); } $message = array( "status" => "ok", "event_id" => $event_id ); return $message; } // end method ganglia_event_delete function ganglia_event_modify( $event ) { global $conf; if ( !isset( $event['event_id'] ) ) { api_return_error( "event_id not set" ); } $db =& MDB2::factory( $conf['overlay_events_dsn'] ); if (DB::isError($db)) { api_return_error($db->getMessage()); } $clauses = array(); if (isset( $event['start_time'] )) { if ( $event['start_time'] == "now" ) { $start_time = time(); } else if ( is_numeric($event['start_time']) ) { $start_time = $event['start_time']; } else { $start_time = strtotime($event['start_time']); } $clauses[] = "start_time = " . $db->quote( $start_time, 'integer' ); } // end isset start_time foreach(array('cluster', 'description', 'summary', 'grid', 'host_regex') AS $k) { if (isset( $event[$k] )) { $clauses[] = "${k} = " . $db->quote( $event[$k], 'text' ); } } // end foreach if ( isset($event['end_time']) ) { $end_time = $event['end_time'] == "now" ? time() : strtotime($event['end_time']); $clauses[] = "end_time = " . $db->quote( $end_time, 'integer' ); } // end isset end_time $sql = "UPDATE overlay_events SET " . implode( ",", $clauses ) . " WHERE event_id = " . $db->quote( $event['event_id'], 'integer' ); $result =& $db->exec( $sql ); if (PEAR::isError($result)) { api_return_error( $result->getMessage()); } $message = array( "status" => "ok", "event_id" => $event['event_id'] ); return $message; } // end method ganglia_event_modify ?> ganglia-web-3.6.1/lib/GangliaAcl.php000066400000000000000000000032311231750357400171450ustar00rootroot00000000000000addRole( new Zend_Acl_Role(GangliaAcl::GUEST)) ->addRole( new Zend_Acl_Role(GangliaAcl::ADMIN)); // define default resources // all clusters should be children of GangliaAcl::ALL_CLUSTERS $this->add( new Zend_Acl_Resource(GangliaAcl::ALL_RESOURCES) ); $this->add( new Zend_Acl_Resource(GangliaAcl::ALL_CLUSTERS), GangliaAcl::ALL_RESOURCES); $this->add( new Zend_Acl_Resource(GangliaAcl::ALL_VIEWS), GangliaAcl::ALL_RESOURCES); // guest can view everything and edit nothing. $this->allow(GangliaAcl::GUEST, GangliaAcl::ALL_RESOURCES, GangliaAcl::VIEW); $this->deny(GangliaAcl::GUEST, GangliaAcl::ALL_RESOURCES, GangliaAcl::EDIT); $this->allow(GangliaAcl::ADMIN, GangliaAcl::ALL_RESOURCES, GangliaAcl::EDIT); $this->allow(GangliaAcl::ADMIN, GangliaAcl::ALL_RESOURCES, GangliaAcl::VIEW); } public function addPrivateCluster($cluster) { $this->add( new Zend_Acl_Resource($cluster), self::ALL_CLUSTERS ); //$this->allow(self::ADMIN, $cluster, 'edit'); $this->deny(self::GUEST, $cluster); } } ?>ganglia-web-3.6.1/lib/GangliaAuth.php000066400000000000000000000046201231750357400173520ustar00rootroot00000000000000init(); } public function init() { if(!$this->environmentIsValid()) { return false; } $this->user = null; $this->group = null; $this->tokenIsValid = false; if(isSet($_COOKIE['ganglia_auth'])) { $cookie = $_COOKIE['ganglia_auth']; // magic quotes will break unserialization if($this->getMagicQuotesGpc()) { $cookie = stripslashes($cookie); } $data = unserialize($cookie); if(array_keys($data) != array('user','group','token')) { return false; } if($this->getAuthToken($data['user']) == $data['token']) { $this->tokenIsValid = true; $this->user = $data['user']; $this->group = $data['group']; } } } public function getUser() { return $this->user; } public function getGroup() { return $this->group; } public function isAuthenticated() { return $this->tokenIsValid; } public function getEnvironmentErrors() { $errors = array(); if(!isSet($_SERVER['ganglia_secret'])) { $errors[] = "No ganglia_secret set in the server environment. If you are using Apache, try adding 'SetEnv ganglia_secret ".sha1(mt_rand().microtime())."' to your configuration."; } return $errors; } public function environmentIsValid() { return count($this->getEnvironmentErrors())==0; } public function getAuthToken($user) { $secret = $_SERVER['ganglia_secret']; return sha1( $user.$secret ); } // this is how a user 'logs in'. public function setAuthCookie($user, $group=null) { setcookie('ganglia_auth', serialize( array('user'=>$user, 'group'=>$group, 'token'=>$this->getAuthToken($user)) ) ); $this->user = $user; $this->group = $group; $this->tokenIsValid = true; } public function destroyAuthCookie() { setcookie('ganglia_auth', '', time()); self::$auth = null; } protected function getMagicQuotesGpc() { return get_magic_quotes_gpc(); } } ?> ganglia-web-3.6.1/lib/Services/000077500000000000000000000000001231750357400162365ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Services/JSON.php000066400000000000000000001153771231750357400175360ustar00rootroot00000000000000 * @author Matt Knapp * @author Brett Stimmerman * @copyright 2005 Michal Migurski * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $ * @license http://www.opensource.org/licenses/bsd-license.php * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 */ /** * Marker constant for Services_JSON::decode(), used to flag stack state */ define('SERVICES_JSON_SLICE', 1); /** * Marker constant for Services_JSON::decode(), used to flag stack state */ define('SERVICES_JSON_IN_STR', 2); /** * Marker constant for Services_JSON::decode(), used to flag stack state */ define('SERVICES_JSON_IN_ARR', 3); /** * Marker constant for Services_JSON::decode(), used to flag stack state */ define('SERVICES_JSON_IN_OBJ', 4); /** * Marker constant for Services_JSON::decode(), used to flag stack state */ define('SERVICES_JSON_IN_CMT', 5); /** * Behavior switch for Services_JSON::decode() */ define('SERVICES_JSON_LOOSE_TYPE', 16); /** * Behavior switch for Services_JSON::decode() */ define('SERVICES_JSON_SUPPRESS_ERRORS', 32); /** * Behavior switch for Services_JSON::decode() */ define('SERVICES_JSON_USE_TO_JSON', 64); /** * Converts to and from JSON format. * * Brief example of use: * * * // create a new instance of Services_JSON * $json = new Services_JSON(); * * // convert a complexe value to JSON notation, and send it to the browser * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); * $output = $json->encode($value); * * print($output); * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] * * // accept incoming POST data, assumed to be in JSON notation * $input = file_get_contents('php://input', 1000000); * $value = $json->decode($input); * */ class Services_JSON { /** * constructs a new JSON instance * * @param int $use object behavior flags; combine with boolean-OR * * possible values: * - SERVICES_JSON_LOOSE_TYPE: loose typing. * "{...}" syntax creates associative arrays * instead of objects in decode(). * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. * Values which can't be encoded (e.g. resources) * appear as NULL instead of throwing errors. * By default, a deeply-nested resource will * bubble up with an error, so all return values * from encode() should be checked with isError() * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects * It serializes the return value from the toJSON call rather * than the object it'self, toJSON can return associative arrays, * strings or numbers, if you return an object, make sure it does * not have a toJSON method, otherwise an error will occur. */ function Services_JSON($use = 0) { $this->use = $use; $this->_mb_strlen = function_exists('mb_strlen'); $this->_mb_convert_encoding = function_exists('mb_convert_encoding'); $this->_mb_substr = function_exists('mb_substr'); } // private - cache the mbstring lookup results.. var $_mb_strlen = false; var $_mb_substr = false; var $_mb_convert_encoding = false; /** * convert a string from one UTF-16 char to one UTF-8 char * * Normally should be handled by mb_convert_encoding, but * provides a slower PHP-only method for installations * that lack the multibye string extension. * * @param string $utf16 UTF-16 character * @return string UTF-8 character * @access private */ function utf162utf8($utf16) { // oh please oh please oh please oh please oh please if($this->_mb_convert_encoding) { return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); } $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); switch(true) { case ((0x7F & $bytes) == $bytes): // this case should never be reached, because we are in ASCII range // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return chr(0x7F & $bytes); case (0x07FF & $bytes) == $bytes: // return a 2-byte UTF-8 character // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return chr(0xC0 | (($bytes >> 6) & 0x1F)) . chr(0x80 | ($bytes & 0x3F)); case (0xFFFF & $bytes) == $bytes: // return a 3-byte UTF-8 character // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return chr(0xE0 | (($bytes >> 12) & 0x0F)) . chr(0x80 | (($bytes >> 6) & 0x3F)) . chr(0x80 | ($bytes & 0x3F)); } // ignoring UTF-32 for now, sorry return ''; } /** * convert a string from one UTF-8 char to one UTF-16 char * * Normally should be handled by mb_convert_encoding, but * provides a slower PHP-only method for installations * that lack the multibye string extension. * * @param string $utf8 UTF-8 character * @return string UTF-16 character * @access private */ function utf82utf16($utf8) { // oh please oh please oh please oh please oh please if($this->_mb_convert_encoding) { return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); } switch($this->strlen8($utf8)) { case 1: // this case should never be reached, because we are in ASCII range // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return $utf8; case 2: // return a UTF-16 character from a 2-byte UTF-8 char // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return chr(0x07 & (ord($utf8{0}) >> 2)) . chr((0xC0 & (ord($utf8{0}) << 6)) | (0x3F & ord($utf8{1}))); case 3: // return a UTF-16 character from a 3-byte UTF-8 char // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 return chr((0xF0 & (ord($utf8{0}) << 4)) | (0x0F & (ord($utf8{1}) >> 2))) . chr((0xC0 & (ord($utf8{1}) << 6)) | (0x7F & ord($utf8{2}))); } // ignoring UTF-32 for now, sorry return ''; } /** * encodes an arbitrary variable into JSON format (and sends JSON Header) * * @param mixed $var any number, boolean, string, array, or object to be encoded. * see argument 1 to Services_JSON() above for array-parsing behavior. * if var is a strng, note that encode() always expects it * to be in ASCII or UTF-8 format! * * @return mixed JSON string representation of input var or an error if a problem occurs * @access public */ function encode($var) { header('Content-type: application/json'); return $this->encodeUnsafe($var); } /** * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) * * @param mixed $var any number, boolean, string, array, or object to be encoded. * see argument 1 to Services_JSON() above for array-parsing behavior. * if var is a strng, note that encode() always expects it * to be in ASCII or UTF-8 format! * * @return mixed JSON string representation of input var or an error if a problem occurs * @access public */ function encodeUnsafe($var) { // see bug #16908 - regarding numeric locale printing $lc = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, 'C'); $ret = $this->_encode($var); setlocale(LC_NUMERIC, $lc); return $ret; } /** * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format * * @param mixed $var any number, boolean, string, array, or object to be encoded. * see argument 1 to Services_JSON() above for array-parsing behavior. * if var is a strng, note that encode() always expects it * to be in ASCII or UTF-8 format! * * @return mixed JSON string representation of input var or an error if a problem occurs * @access public */ function _encode($var) { switch (gettype($var)) { case 'boolean': return $var ? 'true' : 'false'; case 'NULL': return 'null'; case 'integer': return (int) $var; case 'double': case 'float': return (float) $var; case 'string': // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT $ascii = ''; $strlen_var = $this->strlen8($var); /* * Iterate over every character in the string, * escaping with a slash or encoding to UTF-8 where necessary */ for ($c = 0; $c < $strlen_var; ++$c) { $ord_var_c = ord($var{$c}); switch (true) { case $ord_var_c == 0x08: $ascii .= '\b'; break; case $ord_var_c == 0x09: $ascii .= '\t'; break; case $ord_var_c == 0x0A: $ascii .= '\n'; break; case $ord_var_c == 0x0C: $ascii .= '\f'; break; case $ord_var_c == 0x0D: $ascii .= '\r'; break; case $ord_var_c == 0x22: case $ord_var_c == 0x2F: case $ord_var_c == 0x5C: // double quote, slash, slosh $ascii .= '\\'.$var{$c}; break; case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): // characters U-00000000 - U-0000007F (same as ASCII) $ascii .= $var{$c}; break; case (($ord_var_c & 0xE0) == 0xC0): // characters U-00000080 - U-000007FF, mask 110XXXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 if ($c+1 >= $strlen_var) { $c += 1; $ascii .= '?'; break; } $char = pack('C*', $ord_var_c, ord($var{$c + 1})); $c += 1; $utf16 = $this->utf82utf16($char); $ascii .= sprintf('\u%04s', bin2hex($utf16)); break; case (($ord_var_c & 0xF0) == 0xE0): if ($c+2 >= $strlen_var) { $c += 2; $ascii .= '?'; break; } // characters U-00000800 - U-0000FFFF, mask 1110XXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, @ord($var{$c + 1}), @ord($var{$c + 2})); $c += 2; $utf16 = $this->utf82utf16($char); $ascii .= sprintf('\u%04s', bin2hex($utf16)); break; case (($ord_var_c & 0xF8) == 0xF0): if ($c+3 >= $strlen_var) { $c += 3; $ascii .= '?'; break; } // characters U-00010000 - U-001FFFFF, mask 11110XXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c + 1}), ord($var{$c + 2}), ord($var{$c + 3})); $c += 3; $utf16 = $this->utf82utf16($char); $ascii .= sprintf('\u%04s', bin2hex($utf16)); break; case (($ord_var_c & 0xFC) == 0xF8): // characters U-00200000 - U-03FFFFFF, mask 111110XX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 if ($c+4 >= $strlen_var) { $c += 4; $ascii .= '?'; break; } $char = pack('C*', $ord_var_c, ord($var{$c + 1}), ord($var{$c + 2}), ord($var{$c + 3}), ord($var{$c + 4})); $c += 4; $utf16 = $this->utf82utf16($char); $ascii .= sprintf('\u%04s', bin2hex($utf16)); break; case (($ord_var_c & 0xFE) == 0xFC): if ($c+5 >= $strlen_var) { $c += 5; $ascii .= '?'; break; } // characters U-04000000 - U-7FFFFFFF, mask 1111110X // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $char = pack('C*', $ord_var_c, ord($var{$c + 1}), ord($var{$c + 2}), ord($var{$c + 3}), ord($var{$c + 4}), ord($var{$c + 5})); $c += 5; $utf16 = $this->utf82utf16($char); $ascii .= sprintf('\u%04s', bin2hex($utf16)); break; } } return '"'.$ascii.'"'; case 'array': /* * As per JSON spec if any array key is not an integer * we must treat the the whole array as an object. We * also try to catch a sparsely populated associative * array with numeric keys here because some JS engines * will create an array with empty indexes up to * max_index which can cause memory issues and because * the keys, which may be relevant, will be remapped * otherwise. * * As per the ECMA and JSON specification an object may * have any string as a property. Unfortunately due to * a hole in the ECMA specification if the key is a * ECMA reserved word or starts with a digit the * parameter is only accessible using ECMAScript's * bracket notation. */ // treat as a JSON object if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { $properties = array_map(array($this, 'name_value'), array_keys($var), array_values($var)); foreach($properties as $property) { if(Services_JSON::isError($property)) { return $property; } } return '{' . join(',', $properties) . '}'; } // treat it like a regular array $elements = array_map(array($this, '_encode'), $var); foreach($elements as $element) { if(Services_JSON::isError($element)) { return $element; } } return '[' . join(',', $elements) . ']'; case 'object': // support toJSON methods. if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { // this may end up allowing unlimited recursion // so we check the return value to make sure it's not got the same method. $recode = $var->toJSON(); if (method_exists($recode, 'toJSON')) { return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) ? 'null' : new Services_JSON_Error(class_name($var). " toJSON returned an object with a toJSON method."); } return $this->_encode( $recode ); } $vars = get_object_vars($var); $properties = array_map(array($this, 'name_value'), array_keys($vars), array_values($vars)); foreach($properties as $property) { if(Services_JSON::isError($property)) { return $property; } } return '{' . join(',', $properties) . '}'; default: return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) ? 'null' : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); } } /** * array-walking function for use in generating JSON-formatted name-value pairs * * @param string $name name of key to use * @param mixed $value reference to an array element to be encoded * * @return string JSON-formatted name-value pair, like '"name":value' * @access private */ function name_value($name, $value) { $encoded_value = $this->_encode($value); if(Services_JSON::isError($encoded_value)) { return $encoded_value; } return $this->_encode(strval($name)) . ':' . $encoded_value; } /** * reduce a string by removing leading and trailing comments and whitespace * * @param $str string string value to strip of comments and whitespace * * @return string string value stripped of comments and whitespace * @access private */ function reduce_string($str) { $str = preg_replace(array( // eliminate single line comments in '// ...' form '#^\s*//(.+)$#m', // eliminate multi-line comments in '/* ... */' form, at start of string '#^\s*/\*(.+)\*/#Us', // eliminate multi-line comments in '/* ... */' form, at end of string '#/\*(.+)\*/\s*$#Us' ), '', $str); // eliminate extraneous space return trim($str); } /** * decodes a JSON string into appropriate variable * * @param string $str JSON-formatted string * * @return mixed number, boolean, string, array, or object * corresponding to given JSON input string. * See argument 1 to Services_JSON() above for object-output behavior. * Note that decode() always returns strings * in ASCII or UTF-8 format! * @access public */ function decode($str) { $str = $this->reduce_string($str); switch (strtolower($str)) { case 'true': return true; case 'false': return false; case 'null': return null; default: $m = array(); if (is_numeric($str)) { // Lookie-loo, it's a number // This would work on its own, but I'm trying to be // good about returning integers where appropriate: // return (float)$str; // Return float or int, as appropriate return ((float)$str == (integer)$str) ? (integer)$str : (float)$str; } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { // STRINGS RETURNED IN UTF-8 FORMAT $delim = $this->substr8($str, 0, 1); $chrs = $this->substr8($str, 1, -1); $utf8 = ''; $strlen_chrs = $this->strlen8($chrs); for ($c = 0; $c < $strlen_chrs; ++$c) { $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); $ord_chrs_c = ord($chrs{$c}); switch (true) { case $substr_chrs_c_2 == '\b': $utf8 .= chr(0x08); ++$c; break; case $substr_chrs_c_2 == '\t': $utf8 .= chr(0x09); ++$c; break; case $substr_chrs_c_2 == '\n': $utf8 .= chr(0x0A); ++$c; break; case $substr_chrs_c_2 == '\f': $utf8 .= chr(0x0C); ++$c; break; case $substr_chrs_c_2 == '\r': $utf8 .= chr(0x0D); ++$c; break; case $substr_chrs_c_2 == '\\"': case $substr_chrs_c_2 == '\\\'': case $substr_chrs_c_2 == '\\\\': case $substr_chrs_c_2 == '\\/': if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || ($delim == "'" && $substr_chrs_c_2 != '\\"')) { $utf8 .= $chrs{++$c}; } break; case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): // single, escaped unicode character $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); $utf8 .= $this->utf162utf8($utf16); $c += 5; break; case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): $utf8 .= $chrs{$c}; break; case ($ord_chrs_c & 0xE0) == 0xC0: // characters U-00000080 - U-000007FF, mask 110XXXXX //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= $this->substr8($chrs, $c, 2); ++$c; break; case ($ord_chrs_c & 0xF0) == 0xE0: // characters U-00000800 - U-0000FFFF, mask 1110XXXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= $this->substr8($chrs, $c, 3); $c += 2; break; case ($ord_chrs_c & 0xF8) == 0xF0: // characters U-00010000 - U-001FFFFF, mask 11110XXX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= $this->substr8($chrs, $c, 4); $c += 3; break; case ($ord_chrs_c & 0xFC) == 0xF8: // characters U-00200000 - U-03FFFFFF, mask 111110XX // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= $this->substr8($chrs, $c, 5); $c += 4; break; case ($ord_chrs_c & 0xFE) == 0xFC: // characters U-04000000 - U-7FFFFFFF, mask 1111110X // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 $utf8 .= $this->substr8($chrs, $c, 6); $c += 5; break; } } return $utf8; } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { // array, or object notation if ($str{0} == '[') { $stk = array(SERVICES_JSON_IN_ARR); $arr = array(); } else { if ($this->use & SERVICES_JSON_LOOSE_TYPE) { $stk = array(SERVICES_JSON_IN_OBJ); $obj = array(); } else { $stk = array(SERVICES_JSON_IN_OBJ); $obj = new stdClass(); } } array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => 0, 'delim' => false)); $chrs = $this->substr8($str, 1, -1); $chrs = $this->reduce_string($chrs); if ($chrs == '') { if (reset($stk) == SERVICES_JSON_IN_ARR) { return $arr; } else { return $obj; } } //print("\nparsing {$chrs}\n"); $strlen_chrs = $this->strlen8($chrs); for ($c = 0; $c <= $strlen_chrs; ++$c) { $top = end($stk); $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { // found a comma that is not inside a string, array, etc., // OR we've reached the end of the character list $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); if (reset($stk) == SERVICES_JSON_IN_ARR) { // we are in an array, so just push an element onto the stack array_push($arr, $this->decode($slice)); } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { // we are in an object, so figure // out the property name and set an // element in an associative array, // for now $parts = array(); if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { // "name":value pair $key = $this->decode($parts[1]); $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); if ($this->use & SERVICES_JSON_LOOSE_TYPE) { $obj[$key] = $val; } else { $obj->$key = $val; } } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { // name:value pair, where name is unquoted $key = $parts[1]; $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); if ($this->use & SERVICES_JSON_LOOSE_TYPE) { $obj[$key] = $val; } else { $obj->$key = $val; } } } } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { // found a quote, and we are not inside a string array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); //print("Found start of string at {$c}\n"); } elseif (($chrs{$c} == $top['delim']) && ($top['what'] == SERVICES_JSON_IN_STR) && (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { // found a quote, we're in a string, and it's not escaped // we know that it's not escaped becase there is _not_ an // odd number of backslashes at the end of the string so far array_pop($stk); //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); } elseif (($chrs{$c} == '[') && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { // found a left-bracket, and we are in an array, object, or slice array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); //print("Found start of array at {$c}\n"); } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { // found a right-bracket, and we're in an array array_pop($stk); //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); } elseif (($chrs{$c} == '{') && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { // found a left-brace, and we are in an array, object, or slice array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); //print("Found start of object at {$c}\n"); } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { // found a right-brace, and we're in an object array_pop($stk); //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); } elseif (($substr_chrs_c_2 == '/*') && in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { // found a comment start, and we are in an array, object, or slice array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); $c++; //print("Found start of comment at {$c}\n"); } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { // found a comment end, and we're in one now array_pop($stk); $c++; for ($i = $top['where']; $i <= $c; ++$i) $chrs = substr_replace($chrs, ' ', $i, 1); //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); } } if (reset($stk) == SERVICES_JSON_IN_ARR) { return $arr; } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { return $obj; } } } } /** * @todo Ultimately, this should just call PEAR::isError() */ function isError($data, $code = null) { if (class_exists('pear')) { return PEAR::isError($data, $code); } elseif (is_object($data) && (get_class($data) == 'services_json_error' || is_subclass_of($data, 'services_json_error'))) { return true; } return false; } /** * Calculates length of string in bytes * @param string * @return integer length */ function strlen8( $str ) { if ( $this->_mb_strlen ) { return mb_strlen( $str, "8bit" ); } return strlen( $str ); } /** * Returns part of a string, interpreting $start and $length as number of bytes. * @param string * @param integer start * @param integer length * @return integer length */ function substr8( $string, $start, $length=false ) { if ( $length === false ) { $length = $this->strlen8( $string ) - $start; } if ( $this->_mb_substr ) { return mb_substr( $string, $start, $length, "8bit" ); } return substr( $string, $start, $length ); } } if (class_exists('PEAR_Error')) { class Services_JSON_Error extends PEAR_Error { function Services_JSON_Error($message = 'unknown error', $code = null, $mode = null, $options = null, $userinfo = null) { parent::PEAR_Error($message, $code, $mode, $options, $userinfo); } } } else { /** * @todo Ultimately, this class shall be descended from PEAR_Error */ class Services_JSON_Error { function Services_JSON_Error($message = 'unknown error', $code = null, $mode = null, $options = null, $userinfo = null) { } } } ganglia-web-3.6.1/lib/Zend/000077500000000000000000000000001231750357400153535ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl.php000066400000000000000000001241451231750357400165720ustar00rootroot00000000000000 array( 'allRoles' => array( 'allPrivileges' => array( 'type' => self::TYPE_DENY, 'assert' => null ), 'byPrivilegeId' => array() ), 'byRoleId' => array() ), 'byResourceId' => array() ); /** * Adds a Role having an identifier unique to the registry * * The $parents parameter may be a reference to, or the string identifier for, * a Role existing in the registry, or $parents may be passed as an array of * these - mixing string identifiers and objects is ok - to indicate the Roles * from which the newly added Role will directly inherit. * * In order to resolve potential ambiguities with conflicting rules inherited * from different parents, the most recently added parent takes precedence over * parents that were previously added. In other words, the first parent added * will have the least priority, and the last parent added will have the * highest priority. * * @param Zend_Acl_Role_Interface $role * @param Zend_Acl_Role_Interface|string|array $parents * @uses Zend_Acl_Role_Registry::add() * @return Zend_Acl Provides a fluent interface */ public function addRole($role, $parents = null) { if (is_string($role)) { $role = new Zend_Acl_Role($role); } if (!$role instanceof Zend_Acl_Role_Interface) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('addRole() expects $role to be of type Zend_Acl_Role_Interface'); } $this->_getRoleRegistry()->add($role, $parents); return $this; } /** * Returns the identified Role * * The $role parameter can either be a Role or Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @uses Zend_Acl_Role_Registry::get() * @return Zend_Acl_Role_Interface */ public function getRole($role) { return $this->_getRoleRegistry()->get($role); } /** * Returns true if and only if the Role exists in the registry * * The $role parameter can either be a Role or a Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @uses Zend_Acl_Role_Registry::has() * @return boolean */ public function hasRole($role) { return $this->_getRoleRegistry()->has($role); } /** * Returns true if and only if $role inherits from $inherit * * Both parameters may be either a Role or a Role identifier. If * $onlyParents is true, then $role must inherit directly from * $inherit in order to return true. By default, this method looks * through the entire inheritance DAG to determine whether $role * inherits from $inherit through its ancestor Roles. * * @param Zend_Acl_Role_Interface|string $role * @param Zend_Acl_Role_Interface|string $inherit * @param boolean $onlyParents * @uses Zend_Acl_Role_Registry::inherits() * @return boolean */ public function inheritsRole($role, $inherit, $onlyParents = false) { return $this->_getRoleRegistry()->inherits($role, $inherit, $onlyParents); } /** * Removes the Role from the registry * * The $role parameter can either be a Role or a Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @uses Zend_Acl_Role_Registry::remove() * @return Zend_Acl Provides a fluent interface */ public function removeRole($role) { $this->_getRoleRegistry()->remove($role); if ($role instanceof Zend_Acl_Role_Interface) { $roleId = $role->getRoleId(); } else { $roleId = $role; } foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) { if ($roleId === $roleIdCurrent) { unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]); } } foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) { if (array_key_exists('byRoleId', $visitor)) { foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) { if ($roleId === $roleIdCurrent) { unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]); } } } } return $this; } /** * Removes all Roles from the registry * * @uses Zend_Acl_Role_Registry::removeAll() * @return Zend_Acl Provides a fluent interface */ public function removeRoleAll() { $this->_getRoleRegistry()->removeAll(); foreach ($this->_rules['allResources']['byRoleId'] as $roleIdCurrent => $rules) { unset($this->_rules['allResources']['byRoleId'][$roleIdCurrent]); } foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $visitor) { foreach ($visitor['byRoleId'] as $roleIdCurrent => $rules) { unset($this->_rules['byResourceId'][$resourceIdCurrent]['byRoleId'][$roleIdCurrent]); } } return $this; } /** * Adds a Resource having an identifier unique to the ACL * * The $parent parameter may be a reference to, or the string identifier for, * the existing Resource from which the newly added Resource will inherit. * * @param Zend_Acl_Resource_Interface|string $resource * @param Zend_Acl_Resource_Interface|string $parent * @throws Zend_Acl_Exception * @return Zend_Acl Provides a fluent interface */ public function addResource($resource, $parent = null) { if (is_string($resource)) { $resource = new Zend_Acl_Resource($resource); } if (!$resource instanceof Zend_Acl_Resource_Interface) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('addResource() expects $resource to be of type Zend_Acl_Resource_Interface'); } $resourceId = $resource->getResourceId(); if ($this->has($resourceId)) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception("Resource id '$resourceId' already exists in the ACL"); } $resourceParent = null; if (null !== $parent) { try { if ($parent instanceof Zend_Acl_Resource_Interface) { $resourceParentId = $parent->getResourceId(); } else { $resourceParentId = $parent; } $resourceParent = $this->get($resourceParentId); } catch (Zend_Acl_Exception $e) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception("Parent Resource id '$resourceParentId' does not exist", 0, $e); } $this->_resources[$resourceParentId]['children'][$resourceId] = $resource; } $this->_resources[$resourceId] = array( 'instance' => $resource, 'parent' => $resourceParent, 'children' => array() ); return $this; } /** * Adds a Resource having an identifier unique to the ACL * * The $parent parameter may be a reference to, or the string identifier for, * the existing Resource from which the newly added Resource will inherit. * * @deprecated in version 1.9.1 and will be available till 2.0. New code * should use addResource() instead. * * @param Zend_Acl_Resource_Interface $resource * @param Zend_Acl_Resource_Interface|string $parent * @throws Zend_Acl_Exception * @return Zend_Acl Provides a fluent interface */ public function add(Zend_Acl_Resource_Interface $resource, $parent = null) { return $this->addResource($resource, $parent); } /** * Returns the identified Resource * * The $resource parameter can either be a Resource or a Resource identifier. * * @param Zend_Acl_Resource_Interface|string $resource * @throws Zend_Acl_Exception * @return Zend_Acl_Resource_Interface */ public function get($resource) { if ($resource instanceof Zend_Acl_Resource_Interface) { $resourceId = $resource->getResourceId(); } else { $resourceId = (string) $resource; } if (!$this->has($resource)) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception("Resource '$resourceId' not found"); } return $this->_resources[$resourceId]['instance']; } /** * Returns true if and only if the Resource exists in the ACL * * The $resource parameter can either be a Resource or a Resource identifier. * * @param Zend_Acl_Resource_Interface|string $resource * @return boolean */ public function has($resource) { if ($resource instanceof Zend_Acl_Resource_Interface) { $resourceId = $resource->getResourceId(); } else { $resourceId = (string) $resource; } return isset($this->_resources[$resourceId]); } /** * Returns true if and only if $resource inherits from $inherit * * Both parameters may be either a Resource or a Resource identifier. If * $onlyParent is true, then $resource must inherit directly from * $inherit in order to return true. By default, this method looks * through the entire inheritance tree to determine whether $resource * inherits from $inherit through its ancestor Resources. * * @param Zend_Acl_Resource_Interface|string $resource * @param Zend_Acl_Resource_Interface|string $inherit * @param boolean $onlyParent * @throws Zend_Acl_Resource_Registry_Exception * @return boolean */ public function inherits($resource, $inherit, $onlyParent = false) { try { $resourceId = $this->get($resource)->getResourceId(); $inheritId = $this->get($inherit)->getResourceId(); } catch (Zend_Acl_Exception $e) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e); } if (null !== $this->_resources[$resourceId]['parent']) { $parentId = $this->_resources[$resourceId]['parent']->getResourceId(); if ($inheritId === $parentId) { return true; } else if ($onlyParent) { return false; } } else { return false; } while (null !== $this->_resources[$parentId]['parent']) { $parentId = $this->_resources[$parentId]['parent']->getResourceId(); if ($inheritId === $parentId) { return true; } } return false; } /** * Removes a Resource and all of its children * * The $resource parameter can either be a Resource or a Resource identifier. * * @param Zend_Acl_Resource_Interface|string $resource * @throws Zend_Acl_Exception * @return Zend_Acl Provides a fluent interface */ public function remove($resource) { try { $resourceId = $this->get($resource)->getResourceId(); } catch (Zend_Acl_Exception $e) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception($e->getMessage(), $e->getCode(), $e); } $resourcesRemoved = array($resourceId); if (null !== ($resourceParent = $this->_resources[$resourceId]['parent'])) { unset($this->_resources[$resourceParent->getResourceId()]['children'][$resourceId]); } foreach ($this->_resources[$resourceId]['children'] as $childId => $child) { $this->remove($childId); $resourcesRemoved[] = $childId; } foreach ($resourcesRemoved as $resourceIdRemoved) { foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) { if ($resourceIdRemoved === $resourceIdCurrent) { unset($this->_rules['byResourceId'][$resourceIdCurrent]); } } } unset($this->_resources[$resourceId]); return $this; } /** * Removes all Resources * * @return Zend_Acl Provides a fluent interface */ public function removeAll() { foreach ($this->_resources as $resourceId => $resource) { foreach ($this->_rules['byResourceId'] as $resourceIdCurrent => $rules) { if ($resourceId === $resourceIdCurrent) { unset($this->_rules['byResourceId'][$resourceIdCurrent]); } } } $this->_resources = array(); return $this; } /** * Adds an "allow" rule to the ACL * * @param Zend_Acl_Role_Interface|string|array $roles * @param Zend_Acl_Resource_Interface|string|array $resources * @param string|array $privileges * @param Zend_Acl_Assert_Interface $assert * @uses Zend_Acl::setRule() * @return Zend_Acl Provides a fluent interface */ public function allow($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null) { return $this->setRule(self::OP_ADD, self::TYPE_ALLOW, $roles, $resources, $privileges, $assert); } /** * Adds a "deny" rule to the ACL * * @param Zend_Acl_Role_Interface|string|array $roles * @param Zend_Acl_Resource_Interface|string|array $resources * @param string|array $privileges * @param Zend_Acl_Assert_Interface $assert * @uses Zend_Acl::setRule() * @return Zend_Acl Provides a fluent interface */ public function deny($roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null) { return $this->setRule(self::OP_ADD, self::TYPE_DENY, $roles, $resources, $privileges, $assert); } /** * Removes "allow" permissions from the ACL * * @param Zend_Acl_Role_Interface|string|array $roles * @param Zend_Acl_Resource_Interface|string|array $resources * @param string|array $privileges * @uses Zend_Acl::setRule() * @return Zend_Acl Provides a fluent interface */ public function removeAllow($roles = null, $resources = null, $privileges = null) { return $this->setRule(self::OP_REMOVE, self::TYPE_ALLOW, $roles, $resources, $privileges); } /** * Removes "deny" restrictions from the ACL * * @param Zend_Acl_Role_Interface|string|array $roles * @param Zend_Acl_Resource_Interface|string|array $resources * @param string|array $privileges * @uses Zend_Acl::setRule() * @return Zend_Acl Provides a fluent interface */ public function removeDeny($roles = null, $resources = null, $privileges = null) { return $this->setRule(self::OP_REMOVE, self::TYPE_DENY, $roles, $resources, $privileges); } /** * Performs operations on ACL rules * * The $operation parameter may be either OP_ADD or OP_REMOVE, depending on whether the * user wants to add or remove a rule, respectively: * * OP_ADD specifics: * * A rule is added that would allow one or more Roles access to [certain $privileges * upon] the specified Resource(s). * * OP_REMOVE specifics: * * The rule is removed only in the context of the given Roles, Resources, and privileges. * Existing rules to which the remove operation does not apply would remain in the * ACL. * * The $type parameter may be either TYPE_ALLOW or TYPE_DENY, depending on whether the * rule is intended to allow or deny permission, respectively. * * The $roles and $resources parameters may be references to, or the string identifiers for, * existing Resources/Roles, or they may be passed as arrays of these - mixing string identifiers * and objects is ok - to indicate the Resources and Roles to which the rule applies. If either * $roles or $resources is null, then the rule applies to all Roles or all Resources, respectively. * Both may be null in order to work with the default rule of the ACL. * * The $privileges parameter may be used to further specify that the rule applies only * to certain privileges upon the Resource(s) in question. This may be specified to be a single * privilege with a string, and multiple privileges may be specified as an array of strings. * * If $assert is provided, then its assert() method must return true in order for * the rule to apply. If $assert is provided with $roles, $resources, and $privileges all * equal to null, then a rule having a type of: * * TYPE_ALLOW will imply a type of TYPE_DENY, and * * TYPE_DENY will imply a type of TYPE_ALLOW * * when the rule's assertion fails. This is because the ACL needs to provide expected * behavior when an assertion upon the default ACL rule fails. * * @param string $operation * @param string $type * @param Zend_Acl_Role_Interface|string|array $roles * @param Zend_Acl_Resource_Interface|string|array $resources * @param string|array $privileges * @param Zend_Acl_Assert_Interface $assert * @throws Zend_Acl_Exception * @uses Zend_Acl_Role_Registry::get() * @uses Zend_Acl::get() * @return Zend_Acl Provides a fluent interface */ public function setRule($operation, $type, $roles = null, $resources = null, $privileges = null, Zend_Acl_Assert_Interface $assert = null) { // ensure that the rule type is valid; normalize input to uppercase $type = strtoupper($type); if (self::TYPE_ALLOW !== $type && self::TYPE_DENY !== $type) { require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception("Unsupported rule type; must be either '" . self::TYPE_ALLOW . "' or '" . self::TYPE_DENY . "'"); } // ensure that all specified Roles exist; normalize input to array of Role objects or null if (!is_array($roles)) { $roles = array($roles); } else if (0 === count($roles)) { $roles = array(null); } $rolesTemp = $roles; $roles = array(); foreach ($rolesTemp as $role) { if (null !== $role) { $roles[] = $this->_getRoleRegistry()->get($role); } else { $roles[] = null; } } unset($rolesTemp); // ensure that all specified Resources exist; normalize input to array of Resource objects or null if (!is_array($resources)) { $resources = array($resources); } else if (0 === count($resources)) { $resources = array(null); } $resourcesTemp = $resources; $resources = array(); foreach ($resourcesTemp as $resource) { if (null !== $resource) { $resources[] = $this->get($resource); } else { $resources[] = null; } } unset($resourcesTemp); // normalize privileges to array if (null === $privileges) { $privileges = array(); } else if (!is_array($privileges)) { $privileges = array($privileges); } switch ($operation) { // add to the rules case self::OP_ADD: foreach ($resources as $resource) { foreach ($roles as $role) { $rules =& $this->_getRules($resource, $role, true); if (0 === count($privileges)) { $rules['allPrivileges']['type'] = $type; $rules['allPrivileges']['assert'] = $assert; if (!isset($rules['byPrivilegeId'])) { $rules['byPrivilegeId'] = array(); } } else { foreach ($privileges as $privilege) { $rules['byPrivilegeId'][$privilege]['type'] = $type; $rules['byPrivilegeId'][$privilege]['assert'] = $assert; } } } } break; // remove from the rules case self::OP_REMOVE: foreach ($resources as $resource) { foreach ($roles as $role) { $rules =& $this->_getRules($resource, $role); if (null === $rules) { continue; } if (0 === count($privileges)) { if (null === $resource && null === $role) { if ($type === $rules['allPrivileges']['type']) { $rules = array( 'allPrivileges' => array( 'type' => self::TYPE_DENY, 'assert' => null ), 'byPrivilegeId' => array() ); } continue; } if (isset($rules['allPrivileges']['type']) && $type === $rules['allPrivileges']['type']) { unset($rules['allPrivileges']); } } else { foreach ($privileges as $privilege) { if (isset($rules['byPrivilegeId'][$privilege]) && $type === $rules['byPrivilegeId'][$privilege]['type']) { unset($rules['byPrivilegeId'][$privilege]); } } } } } break; default: require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception("Unsupported operation; must be either '" . self::OP_ADD . "' or '" . self::OP_REMOVE . "'"); } return $this; } /** * Returns true if and only if the Role has access to the Resource * * The $role and $resource parameters may be references to, or the string identifiers for, * an existing Resource and Role combination. * * If either $role or $resource is null, then the query applies to all Roles or all Resources, * respectively. Both may be null to query whether the ACL has a "blacklist" rule * (allow everything to all). By default, Zend_Acl creates a "whitelist" rule (deny * everything to all), and this method would return false unless this default has * been overridden (i.e., by executing $acl->allow()). * * If a $privilege is not provided, then this method returns false if and only if the * Role is denied access to at least one privilege upon the Resource. In other words, this * method returns true if and only if the Role is allowed all privileges on the Resource. * * This method checks Role inheritance using a depth-first traversal of the Role registry. * The highest priority parent (i.e., the parent most recently added) is checked first, * and its respective parents are checked similarly before the lower-priority parents of * the Role are checked. * * @param Zend_Acl_Role_Interface|string $role * @param Zend_Acl_Resource_Interface|string $resource * @param string $privilege * @uses Zend_Acl::get() * @uses Zend_Acl_Role_Registry::get() * @return boolean */ public function isAllowed($role = null, $resource = null, $privilege = null) { // reset role & resource to null $this->_isAllowedRole = null; $this->_isAllowedResource = null; $this->_isAllowedPrivilege = null; if (null !== $role) { // keep track of originally called role $this->_isAllowedRole = $role; $role = $this->_getRoleRegistry()->get($role); if (!$this->_isAllowedRole instanceof Zend_Acl_Role_Interface) { $this->_isAllowedRole = $role; } } if (null !== $resource) { // keep track of originally called resource $this->_isAllowedResource = $resource; $resource = $this->get($resource); if (!$this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) { $this->_isAllowedResource = $resource; } } if (null === $privilege) { // query on all privileges do { // depth-first search on $role if it is not 'allRoles' pseudo-parent if (null !== $role && null !== ($result = $this->_roleDFSAllPrivileges($role, $resource, $privilege))) { return $result; } // look for rule on 'allRoles' psuedo-parent if (null !== ($rules = $this->_getRules($resource, null))) { foreach ($rules['byPrivilegeId'] as $privilege => $rule) { if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, null, $privilege))) { return false; } } if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) { return self::TYPE_ALLOW === $ruleTypeAllPrivileges; } } // try next Resource $resource = $this->_resources[$resource->getResourceId()]['parent']; } while (true); // loop terminates at 'allResources' pseudo-parent } else { $this->_isAllowedPrivilege = $privilege; // query on one privilege do { // depth-first search on $role if it is not 'allRoles' pseudo-parent if (null !== $role && null !== ($result = $this->_roleDFSOnePrivilege($role, $resource, $privilege))) { return $result; } // look for rule on 'allRoles' pseudo-parent if (null !== ($ruleType = $this->_getRuleType($resource, null, $privilege))) { return self::TYPE_ALLOW === $ruleType; } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, null, null))) { return self::TYPE_ALLOW === $ruleTypeAllPrivileges; } // try next Resource $resource = $this->_resources[$resource->getResourceId()]['parent']; } while (true); // loop terminates at 'allResources' pseudo-parent } } /** * Returns the Role registry for this ACL * * If no Role registry has been created yet, a new default Role registry * is created and returned. * * @return Zend_Acl_Role_Registry */ protected function _getRoleRegistry() { if (null === $this->_roleRegistry) { $this->_roleRegistry = new Zend_Acl_Role_Registry(); } return $this->_roleRegistry; } /** * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule * allowing/denying $role access to all privileges upon $resource * * This method returns true if a rule is found and allows access. If a rule exists and denies access, * then this method returns false. If no applicable rule is found, then this method returns null. * * @param Zend_Acl_Role_Interface $role * @param Zend_Acl_Resource_Interface $resource * @return boolean|null */ protected function _roleDFSAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null) { $dfs = array( 'visited' => array(), 'stack' => array() ); if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) { return $result; } while (null !== ($role = array_pop($dfs['stack']))) { if (!isset($dfs['visited'][$role->getRoleId()])) { if (null !== ($result = $this->_roleDFSVisitAllPrivileges($role, $resource, $dfs))) { return $result; } } } return null; } /** * Visits an $role in order to look for a rule allowing/denying $role access to all privileges upon $resource * * This method returns true if a rule is found and allows access. If a rule exists and denies access, * then this method returns false. If no applicable rule is found, then this method returns null. * * This method is used by the internal depth-first search algorithm and may modify the DFS data structure. * * @param Zend_Acl_Role_Interface $role * @param Zend_Acl_Resource_Interface $resource * @param array $dfs * @return boolean|null * @throws Zend_Acl_Exception */ protected function _roleDFSVisitAllPrivileges(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, &$dfs = null) { if (null === $dfs) { /** * @see Zend_Acl_Exception */ require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('$dfs parameter may not be null'); } if (null !== ($rules = $this->_getRules($resource, $role))) { foreach ($rules['byPrivilegeId'] as $privilege => $rule) { if (self::TYPE_DENY === ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) { return false; } } if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) { return self::TYPE_ALLOW === $ruleTypeAllPrivileges; } } $dfs['visited'][$role->getRoleId()] = true; foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) { $dfs['stack'][] = $roleParent; } return null; } /** * Performs a depth-first search of the Role DAG, starting at $role, in order to find a rule * allowing/denying $role access to a $privilege upon $resource * * This method returns true if a rule is found and allows access. If a rule exists and denies access, * then this method returns false. If no applicable rule is found, then this method returns null. * * @param Zend_Acl_Role_Interface $role * @param Zend_Acl_Resource_Interface $resource * @param string $privilege * @return boolean|null * @throws Zend_Acl_Exception */ protected function _roleDFSOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, $privilege = null) { if (null === $privilege) { /** * @see Zend_Acl_Exception */ require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('$privilege parameter may not be null'); } $dfs = array( 'visited' => array(), 'stack' => array() ); if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) { return $result; } while (null !== ($role = array_pop($dfs['stack']))) { if (!isset($dfs['visited'][$role->getRoleId()])) { if (null !== ($result = $this->_roleDFSVisitOnePrivilege($role, $resource, $privilege, $dfs))) { return $result; } } } return null; } /** * Visits an $role in order to look for a rule allowing/denying $role access to a $privilege upon $resource * * This method returns true if a rule is found and allows access. If a rule exists and denies access, * then this method returns false. If no applicable rule is found, then this method returns null. * * This method is used by the internal depth-first search algorithm and may modify the DFS data structure. * * @param Zend_Acl_Role_Interface $role * @param Zend_Acl_Resource_Interface $resource * @param string $privilege * @param array $dfs * @return boolean|null * @throws Zend_Acl_Exception */ protected function _roleDFSVisitOnePrivilege(Zend_Acl_Role_Interface $role, Zend_Acl_Resource_Interface $resource = null, $privilege = null, &$dfs = null) { if (null === $privilege) { /** * @see Zend_Acl_Exception */ require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('$privilege parameter may not be null'); } if (null === $dfs) { /** * @see Zend_Acl_Exception */ require_once 'Zend/Acl/Exception.php'; throw new Zend_Acl_Exception('$dfs parameter may not be null'); } if (null !== ($ruleTypeOnePrivilege = $this->_getRuleType($resource, $role, $privilege))) { return self::TYPE_ALLOW === $ruleTypeOnePrivilege; } else if (null !== ($ruleTypeAllPrivileges = $this->_getRuleType($resource, $role, null))) { return self::TYPE_ALLOW === $ruleTypeAllPrivileges; } $dfs['visited'][$role->getRoleId()] = true; foreach ($this->_getRoleRegistry()->getParents($role) as $roleParentId => $roleParent) { $dfs['stack'][] = $roleParent; } return null; } /** * Returns the rule type associated with the specified Resource, Role, and privilege * combination. * * If a rule does not exist or its attached assertion fails, which means that * the rule is not applicable, then this method returns null. Otherwise, the * rule type applies and is returned as either TYPE_ALLOW or TYPE_DENY. * * If $resource or $role is null, then this means that the rule must apply to * all Resources or Roles, respectively. * * If $privilege is null, then the rule must apply to all privileges. * * If all three parameters are null, then the default ACL rule type is returned, * based on whether its assertion method passes. * * @param Zend_Acl_Resource_Interface $resource * @param Zend_Acl_Role_Interface $role * @param string $privilege * @return string|null */ protected function _getRuleType(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null, $privilege = null) { // get the rules for the $resource and $role if (null === ($rules = $this->_getRules($resource, $role))) { return null; } // follow $privilege if (null === $privilege) { if (isset($rules['allPrivileges'])) { $rule = $rules['allPrivileges']; } else { return null; } } else if (!isset($rules['byPrivilegeId'][$privilege])) { return null; } else { $rule = $rules['byPrivilegeId'][$privilege]; } // check assertion first if ($rule['assert']) { $assertion = $rule['assert']; $assertionValue = $assertion->assert( $this, ($this->_isAllowedRole instanceof Zend_Acl_Role_Interface) ? $this->_isAllowedRole : $role, ($this->_isAllowedResource instanceof Zend_Acl_Resource_Interface) ? $this->_isAllowedResource : $resource, $this->_isAllowedPrivilege ); } if (null === $rule['assert'] || $assertionValue) { return $rule['type']; } else if (null !== $resource || null !== $role || null !== $privilege) { return null; } else if (self::TYPE_ALLOW === $rule['type']) { return self::TYPE_DENY; } else { return self::TYPE_ALLOW; } } /** * Returns the rules associated with a Resource and a Role, or null if no such rules exist * * If either $resource or $role is null, this means that the rules returned are for all Resources or all Roles, * respectively. Both can be null to return the default rule set for all Resources and all Roles. * * If the $create parameter is true, then a rule set is first created and then returned to the caller. * * @param Zend_Acl_Resource_Interface $resource * @param Zend_Acl_Role_Interface $role * @param boolean $create * @return array|null */ protected function &_getRules(Zend_Acl_Resource_Interface $resource = null, Zend_Acl_Role_Interface $role = null, $create = false) { // create a reference to null $null = null; $nullRef =& $null; // follow $resource do { if (null === $resource) { $visitor =& $this->_rules['allResources']; break; } $resourceId = $resource->getResourceId(); if (!isset($this->_rules['byResourceId'][$resourceId])) { if (!$create) { return $nullRef; } $this->_rules['byResourceId'][$resourceId] = array(); } $visitor =& $this->_rules['byResourceId'][$resourceId]; } while (false); // follow $role if (null === $role) { if (!isset($visitor['allRoles'])) { if (!$create) { return $nullRef; } $visitor['allRoles']['byPrivilegeId'] = array(); } return $visitor['allRoles']; } $roleId = $role->getRoleId(); if (!isset($visitor['byRoleId'][$roleId])) { if (!$create) { return $nullRef; } $visitor['byRoleId'][$roleId]['byPrivilegeId'] = array(); } return $visitor['byRoleId'][$roleId]; } /** * @return array of registered roles (Deprecated) * @deprecated Deprecated since version 1.10 (December 2009) */ public function getRegisteredRoles() { trigger_error('The method getRegisteredRoles() was deprecated as of ' . 'version 1.0, and may be removed. You\'re encouraged ' . 'to use getRoles() instead.'); return $this->_getRoleRegistry()->getRoles(); } /** * @return array of registered roles */ public function getRoles() { return array_keys($this->_getRoleRegistry()->getRoles()); } /** * @return array of registered resources */ public function getResources() { return array_keys($this->_resources); } } ganglia-web-3.6.1/lib/Zend/Acl/000077500000000000000000000000001231750357400160525ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl/Assert/000077500000000000000000000000001231750357400173135ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl/Assert/Interface.php000066400000000000000000000037321231750357400217310ustar00rootroot00000000000000_resourceId = (string) $resourceId; } /** * Defined by Zend_Acl_Resource_Interface; returns the Resource identifier * * @return string */ public function getResourceId() { return $this->_resourceId; } /** * Defined by Zend_Acl_Resource_Interface; returns the Resource identifier * Proxies to getResourceId() * * @return string */ public function __toString() { return $this->getResourceId(); } } ganglia-web-3.6.1/lib/Zend/Acl/Resource/000077500000000000000000000000001231750357400176415ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl/Resource/Interface.php000066400000000000000000000021671231750357400222600ustar00rootroot00000000000000_roleId = (string) $roleId; } /** * Defined by Zend_Acl_Role_Interface; returns the Role identifier * * @return string */ public function getRoleId() { return $this->_roleId; } /** * Defined by Zend_Acl_Role_Interface; returns the Role identifier * Proxies to getRoleId() * * @return string */ public function __toString() { return $this->getRoleId(); } } ganglia-web-3.6.1/lib/Zend/Acl/Role/000077500000000000000000000000001231750357400167535ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl/Role/Interface.php000066400000000000000000000021531231750357400213650ustar00rootroot00000000000000getRoleId(); if ($this->has($roleId)) { /** * @see Zend_Acl_Role_Registry_Exception */ require_once 'Zend/Acl/Role/Registry/Exception.php'; throw new Zend_Acl_Role_Registry_Exception("Role id '$roleId' already exists in the registry"); } $roleParents = array(); if (null !== $parents) { if (!is_array($parents)) { $parents = array($parents); } /** * @see Zend_Acl_Role_Registry_Exception */ require_once 'Zend/Acl/Role/Registry/Exception.php'; foreach ($parents as $parent) { try { if ($parent instanceof Zend_Acl_Role_Interface) { $roleParentId = $parent->getRoleId(); } else { $roleParentId = $parent; } $roleParent = $this->get($roleParentId); } catch (Zend_Acl_Role_Registry_Exception $e) { throw new Zend_Acl_Role_Registry_Exception("Parent Role id '$roleParentId' does not exist", 0, $e); } $roleParents[$roleParentId] = $roleParent; $this->_roles[$roleParentId]['children'][$roleId] = $role; } } $this->_roles[$roleId] = array( 'instance' => $role, 'parents' => $roleParents, 'children' => array() ); return $this; } /** * Returns the identified Role * * The $role parameter can either be a Role or a Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @throws Zend_Acl_Role_Registry_Exception * @return Zend_Acl_Role_Interface */ public function get($role) { if ($role instanceof Zend_Acl_Role_Interface) { $roleId = $role->getRoleId(); } else { $roleId = (string) $role; } if (!$this->has($role)) { /** * @see Zend_Acl_Role_Registry_Exception */ require_once 'Zend/Acl/Role/Registry/Exception.php'; throw new Zend_Acl_Role_Registry_Exception("Role '$roleId' not found"); } return $this->_roles[$roleId]['instance']; } /** * Returns true if and only if the Role exists in the registry * * The $role parameter can either be a Role or a Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @return boolean */ public function has($role) { if ($role instanceof Zend_Acl_Role_Interface) { $roleId = $role->getRoleId(); } else { $roleId = (string) $role; } return isset($this->_roles[$roleId]); } /** * Returns an array of an existing Role's parents * * The array keys are the identifiers of the parent Roles, and the values are * the parent Role instances. The parent Roles are ordered in this array by * ascending priority. The highest priority parent Role, last in the array, * corresponds with the parent Role most recently added. * * If the Role does not have any parents, then an empty array is returned. * * @param Zend_Acl_Role_Interface|string $role * @uses Zend_Acl_Role_Registry::get() * @return array */ public function getParents($role) { $roleId = $this->get($role)->getRoleId(); return $this->_roles[$roleId]['parents']; } /** * Returns true if and only if $role inherits from $inherit * * Both parameters may be either a Role or a Role identifier. If * $onlyParents is true, then $role must inherit directly from * $inherit in order to return true. By default, this method looks * through the entire inheritance DAG to determine whether $role * inherits from $inherit through its ancestor Roles. * * @param Zend_Acl_Role_Interface|string $role * @param Zend_Acl_Role_Interface|string $inherit * @param boolean $onlyParents * @throws Zend_Acl_Role_Registry_Exception * @return boolean */ public function inherits($role, $inherit, $onlyParents = false) { /** * @see Zend_Acl_Role_Registry_Exception */ require_once 'Zend/Acl/Role/Registry/Exception.php'; try { $roleId = $this->get($role)->getRoleId(); $inheritId = $this->get($inherit)->getRoleId(); } catch (Zend_Acl_Role_Registry_Exception $e) { throw new Zend_Acl_Role_Registry_Exception($e->getMessage(), $e->getCode(), $e); } $inherits = isset($this->_roles[$roleId]['parents'][$inheritId]); if ($inherits || $onlyParents) { return $inherits; } foreach ($this->_roles[$roleId]['parents'] as $parentId => $parent) { if ($this->inherits($parentId, $inheritId)) { return true; } } return false; } /** * Removes the Role from the registry * * The $role parameter can either be a Role or a Role identifier. * * @param Zend_Acl_Role_Interface|string $role * @throws Zend_Acl_Role_Registry_Exception * @return Zend_Acl_Role_Registry Provides a fluent interface */ public function remove($role) { /** * @see Zend_Acl_Role_Registry_Exception */ require_once 'Zend/Acl/Role/Registry/Exception.php'; try { $roleId = $this->get($role)->getRoleId(); } catch (Zend_Acl_Role_Registry_Exception $e) { throw new Zend_Acl_Role_Registry_Exception($e->getMessage(), $e->getCode(), $e); } foreach ($this->_roles[$roleId]['children'] as $childId => $child) { unset($this->_roles[$childId]['parents'][$roleId]); } foreach ($this->_roles[$roleId]['parents'] as $parentId => $parent) { unset($this->_roles[$parentId]['children'][$roleId]); } unset($this->_roles[$roleId]); return $this; } /** * Removes all Roles from the registry * * @return Zend_Acl_Role_Registry Provides a fluent interface */ public function removeAll() { $this->_roles = array(); return $this; } public function getRoles() { return $this->_roles; } } ganglia-web-3.6.1/lib/Zend/Acl/Role/Registry/000077500000000000000000000000001231750357400205635ustar00rootroot00000000000000ganglia-web-3.6.1/lib/Zend/Acl/Role/Registry/Exception.php000066400000000000000000000021271231750357400232340ustar00rootroot00000000000000_previous = $previous; } else { parent::__construct($msg, (int) $code, $previous); } } /** * Overloading * * For PHP < 5.3.0, provides access to the getPrevious() method. * * @param string $method * @param array $args * @return mixed */ public function __call($method, array $args) { if ('getprevious' == strtolower($method)) { return $this->_getPrevious(); } return null; } /** * String representation of the exception * * @return string */ public function __toString() { if (version_compare(PHP_VERSION, '5.3.0', '<')) { if (null !== ($e = $this->getPrevious())) { return $e->__toString() . "\n\nNext " . parent::__toString(); } } return parent::__toString(); } /** * Returns previous Exception * * @return Exception|null */ protected function _getPrevious() { return $this->_previous; } } ganglia-web-3.6.1/lib/cache.php000066400000000000000000000023741231750357400162350ustar00rootroot00000000000000 0) { $index_array['hosts'] = array_keys($index_array['cluster']); g_cache_serialize($index_array); } } ?> ganglia-web-3.6.1/lib/common_api.php000066400000000000000000000012451231750357400173070ustar00rootroot00000000000000 "error", "message" => $message ); die ( json_encode($digest) ); } function api_return_ok ( $message ) { $digest = array( "status" => "ok", "message" => $message ); die ( json_encode($digest) ); } // Handle PHP error set_error_handler("ganglia_api_error_handler"); function ganglia_api_error_handler ($no, $str, $file, $line, $context) { switch ($no) { case E_ERROR: case E_CORE_ERROR: case E_COMPILE_ERROR: case E_USER_ERROR: api_return_error( "$file [$line] : $str" ); break; } } ?> ganglia-web-3.6.1/lib/json.php000066400000000000000000000005561231750357400161430ustar00rootroot00000000000000encode( $s ); } function json_decode( $s ) { $j = new Services_JSON( SERVICES_JSON_LOOSE_TYPE ); return $j->decode( $s ); } } ?> ganglia-web-3.6.1/login.php000066400000000000000000000020331231750357400155240ustar00rootroot00000000000000setAuthCookie($_SERVER['REMOTE_USER']); $redirect_to = isSet( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : 'index.php'; header("Location: $redirect_to"); die(); } ?> Authentication Failed

    We were unable to log you in.

    $conf['auth_system'] is not defined.
    Please notify an administrator. Authentication is disabled by Ganglia configuration.
    $conf['auth_system'] = ''; Authentication is not configured correctly. The web server must provide an authenticated username.
    ganglia-web-3.6.1/logout.php000066400000000000000000000003441231750357400157300ustar00rootroot00000000000000destroyAuthCookie(); $redirect_to = isSet( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : 'index.php'; header("Location: $redirect_to"); ?>ganglia-web-3.6.1/meta_view.php000066400000000000000000000217651231750357400164110ustar00rootroot00000000000000assign("filters", $filter_defs); $data->assign("choose_filter", $choose_filter); } $source_names = array_keys($grid); # Build a list of cluster names and randomly pick a smaller subset to # display for control room mode. This allows a dedicated host to # eventually cycle through all the graphs w/o scrolling the mouse. A bunch # of these stations could monitor a large grid. # # For the standard meta view still display all the hosts. if ( $context == "control" ) { srand((double)microtime()*1000000); shuffle($source_names); $subset = array_slice($source_names, 0, abs($controlroom)); $source_names = $subset; } foreach( $source_names as $c) { $cpucount = $metrics[$c]["cpu_num"]['SUM']; if (!$cpucount) $cpucount=1; $load_one = $metrics[$c]["load_one"]['SUM']; $value = (double) $load_one / $cpucount; $sorted_sources[$c] = $value; $values[$c] = $value; isset($total_load) or $total_load = 0; $total_load += $value; } if ($sort == "descending") { $sorted_sources[$self] = 999999999; arsort($sorted_sources); } else if ($sort == "by name") { # SORT HACK to keep $self first; see below: $sorted_sources["AAAAA.$self"] = $sorted_sources[$self]; unset($sorted_sources[$self]); ksort($sorted_sources); } else if ($sort == "by hosts up") { foreach ($sorted_sources as $source => $val) { $sorted_sources[$source] = intval($grid[$source]['HOSTS_UP']); } $sorted_sources[$self] = 999999999; arsort($sorted_sources); } else if ($sort == "by hosts down") { foreach ($sorted_sources as $source => $val) { $sorted_sources[$source] = intval($grid[$source]['HOSTS_DOWN']); } $sorted_sources[$self] = 999999999; arsort($sorted_sources); } else { $sorted_sources[$self] = -1; asort($sorted_sources); } $sources = array(); # Display the sources. The first is ourself, the rest are our children. foreach ( $sorted_sources as $source => $val ) { # XXX: SORT HACK to keep $self first; see above if ($source == "AAAAA.$self") { $source = $self; } # Make sure the source is permitted by the filters, if any if(!filter_permit($source)) continue; $m = $metrics[$source]; $sourceurl = rawurlencode($source); if (isset($grid[$source]['GRID']) and $grid[$source]['GRID']) { $localtime = $grid[$source]['LOCALTIME']; # Is this the our own grid? if ($source==$self) { # Negative control room values means dont display grid summary. if ($controlroom < 0) continue; $num_sources = count($sorted_sources) - 1; $name = "$self ${conf['meta_designator']} ($num_sources sources)"; $graph_url = "me=$sourceurl&$get_metric_string"; $url = "./?$get_metric_string"; } else { # Set grid context. $name = "$source ${conf['meta_designator']}"; $graph_url = "G=$sourceurl&$get_metric_string&st=$localtime"; $authority = $grid[$source]['AUTHORITY']; $url = "$authority?gw=fwd&gs=$gridstack_url&$get_metric_string"; } $alt_url = "(tree view)"; $class = "grid"; } else { # Set cluster context. $name = $source; $localtime = $grid[$source]['LOCALTIME']; $graph_url = "c=$sourceurl&$get_metric_string&st=$localtime"; $url = "./?c=$sourceurl&$get_metric_string"; $alt_url = "(physical view)"; $class = "cluster"; } $cpu_num = $m["cpu_num"]['SUM'] ? $m["cpu_num"]['SUM'] : 1; $cluster_load15 = sprintf("%.0f", ((double) $m["load_fifteen"]['SUM'] / $cpu_num) * 100); $cluster_load5 = sprintf("%.0f", ((double) $m["load_five"]['SUM'] / $cpu_num) * 100); $cluster_load1 = sprintf("%.0f", ((double) $m["load_one"]['SUM'] / $cpu_num) * 100); $cluster_load = "$cluster_load15%, $cluster_load5%, $cluster_load1%"; $clusname = $source == $self ? '' : $source; $avg_cpu_num = find_avg($clusname, "", "cpu_num"); if ($avg_cpu_num == 0) $avg_cpu_num = 1; $cluster_util = sprintf("%.0f", ((double) find_avg($clusname, "", "load_one") / $avg_cpu_num ) * 100); $sources[$source]["name"] = $name; $sources[$source]["cpu_num"] = $m["cpu_num"]['SUM']; $sources[$source]["url"] = $url; $sources[$source]["class"] = $class; # Initialize some variables for the $sources array to be past to the template since private sources # may not have these defined $sources[$source]["alt_view"] = ""; $sources[$source]["cluster_load"] = ""; $sources[$source]["cluster_util"] = ""; $sources[$source]["num_dead_nodes"] = ""; $sources[$source]["range"] = ""; $sources[$source]["graph_url"] = ""; $sources[$source]["base64img"] = ""; if ($localtime) $sources[$source]["localtime"] = "Localtime:
      " . date("Y-m-d H:i", $localtime); # I dont like this either, but we need to have private clusters because some # users are skittish about publishing the load info. if(checkAccess($source, GangliaAcl::VIEW, $conf)) { $sources[$source]["alt_view"] = "$alt_url"; $sources[$source]["public"] = 1; if ($cluster_load) $sources[$source]["cluster_load"] = "Current Load Avg (15, 5, 1m):" ."
      $cluster_load"; if (isset($cluster_util)) $sources[$source]["cluster_util"] = "Avg Utilization (last $range):" ."
      $cluster_util%"; $sources[$source]["num_nodes"] = $grid[$source]["HOSTS_UP"]; $sources[$source]["num_dead_nodes"] = $grid[$source]["HOSTS_DOWN"]; $sources[$source]["range"] = $range; $sources[$source]["graph_url"] = $graph_url; if(isset($base64img)) { $sources[$source]["base64img"] = $base64img; } if ( $source == $self ) { $sources[$source]["self_summary_graphs"] = 1; } else { $sources[$source]["summary_graphs"] = 1; } } else { $sources[$source]["private"] = 1; $sources[$source]["num_nodes"] = $grid[$source]["HOSTS_UP"] + $grid[$source]["HOSTS_DOWN"]; } } $data->assign("sources", $sources); $snap_rows = array(); # Show load images. if ($conf['show_meta_snapshot']=="yes") { $data->assign("show_snapshot", 1); $data->assign("self", "$self ${conf['meta_designator']}"); foreach ($sorted_sources as $c=>$value) { if ($c==$self) continue; if ($c=="AAAAA.$self") continue; # SORT HACK; see above if (! checkAccess($c, GangliaAcl::VIEW, $conf)) { $Private[$c] = template("images/cluster_private.jpg"); continue; } $names[]=$c; if (isset($grid[$c]['GRID']) and $grid[$c]['GRID']) $image = load_image("grid", $values[$c]); else $image = load_image("cluster", $values[$c]); $Images[]=$image; } # Add private cluster pictures to the end. if (isset($Private) and is_array($Private)) { foreach ($Private as $c=>$image) { $names[]=$c; $Images[]=$image; } } # All this fancyness is to get the Cluster names # above the image. Not easy with template blocks. $cols=5; $i = 0; $count=count($names); while ($i < $count) { $snapnames = ""; $snapimgs = ""; foreach(range(0, $cols-1) as $j) { $k = $i + $j; if ($k >= $count) break; $n = $names[$k]; $snapnames .= "$n\n"; $snapimgs .= ""; if (isset($grid[$n]['GRID']) and $grid[$n]['GRID']) $snapimgs .= ""; else { $nameurl = rawurlencode($n); $snapimgs .= ""; } $snapimgs .= "\"$names[$k]\"\n"; } $snap_rows[$i]["names"] = $snapnames; $snap_rows[$i]["images"] = $snapimgs; $i += $cols; } $data->assign("snap_rows", $snap_rows); } $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/metric_group_view.php000066400000000000000000000101521231750357400201460ustar00rootroot00000000000000There was an error initializing the Dwoo PHP Templating Engine: " . $e->getMessage() . "

    The compile directory should be owned and writable by the apache user."; exit; } $tpl = new Dwoo_Template_File(template("metric_group_view.tpl")); $data = new Dwoo_Data(); $data->assign("may_edit_views", checkAccess( GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf) ); $data->assign("graph_engine", $conf['graph_engine']); function dump_var($var, $varId) { ob_start(); var_dump($var); error_log($varId . " = ". ob_get_clean()); } function getMetricGroup($metrics, $metric_group, $always_timestamp, $always_constant, $hostname, $baseGraphArgs, $data) { global $conf; list($metricMap, $metricGroupMap) = buildMetricMaps($metrics, $always_timestamp, $always_constant, $baseGraphArgs); //dump_var($metricMap, "metricMap"); //dump_var($metricGroupMap, "metricGroupMap"); # There is a special case where if you don't set group when you do gmetric # invocation it gets set to no_group which ends up being just [""] array if ( $metric_group == "no_group" ) $metric_group = ""; if (!isset($metricGroupMap[$metric_group])) { error_log("Missing metric group: " . $metric_group); exit(0); } $metric_array = $metricGroupMap[$metric_group]; $num_metrics = count($metric_array); if (function_exists("sort_metric_group_metrics")) { $metric_array = sort_metric_group_metrics($group, $metric_array); } else { // Sort by metric_name asort($metric_array); } $i = 0; foreach ($metric_array as $name) { $group["metrics"][$name]["graphargs"] = $metricMap[$name]['graph']; $group["metrics"][$name]["alt"] = "$hostname $name"; $group["metrics"][$name]["host_name"] = $hostname; $group["metrics"][$name]["metric_name"] = $name; $group["metrics"][$name]["title"] = $metricMap[$name]['title']; $group["metrics"][$name]["desc"] = $metricMap[$name]['description']; $group["metrics"][$name]["new_row"] = ""; if (!(++$i % $conf['metriccols']) && ($i != $num_metrics)) $group["metrics"][$name]["new_row"] = ""; } $data->assign("g_metrics", $group); } $size = isset($clustergraphsize) ? $clustergraphsize : 'default'; //set to 'default' to preserve old behavior $size = ($size == 'medium') ? 'default' : $size; // set host zoom class based on the size of the graph shown $additional_host_img_css_classes = ""; if ( isset($conf['zoom_support']) && $conf['zoom_support'] === true ) $additional_host_img_css_classes = "host_${size}_zoomable"; $data->assign("additional_host_img_css_classes", $additional_host_img_css_classes); $cluster_url = rawurlencode($clustername); $baseGraphArgs = "c=$cluster_url&h=$hostname" . "&r=$range&z=$size&jr=$jobrange" . "&js=$jobstart&st=$cluster[LOCALTIME]"; if ($cs) $baseGraphArgs .= "&cs=" . rawurlencode($cs); if ($ce) $baseGraphArgs .= "&ce=" . rawurlencode($ce); if (isset($_GET['event'])) $baseGraphArgs .= "&event=" . $_GET['event']; if (isset($_GET['ts'])) $baseGraphArgs .= "&ts=" . $_GET['ts']; getMetricGroup($metrics, $metric_group, $always_timestamp, $always_constant, $hostname, $baseGraphArgs, $data); if ( $conf['graph_engine'] == "flot" ) { $data->assign("graph_height", $conf['graph_sizes'][$size]["height"] + 50); $data->assign("graph_width", $conf['graph_sizes'][$size]["width"]); } $data->assign('GRAPH_BASE_ID', $GRAPH_BASE_ID); $data->assign('SHOW_EVENTS_BASE_ID', $SHOW_EVENTS_BASE_ID); $data->assign('TIME_SHIFT_BASE_ID', $TIME_SHIFT_BASE_ID); $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/mobile.php000066400000000000000000000127261231750357400156750ustar00rootroot00000000000000 Ganglia Mobile $clusters ) { foreach ($clusters AS $clustername) { $cluster_array[$clustername][] = $hostname; } } $cluster_names = array_keys($cluster_array); $available_views = get_available_views(); ?>

    Ganglia Mobile

    Please select a category:

    1 ) { ?>

    Ganglia Clusters

    $clustername ) { ?>
    ">

    Cluster

    Ganglia Views

      $view ) { $v = $view['view_name']; print '
    • ' . $v . '
    • '; } ?>
    ganglia-web-3.6.1/mobile_helper.php000066400000000000000000000310151231750357400172240ustar00rootroot00000000000000
    Back

    View

    Home
      $v"; } else { $range_menu .= "
    • $v
    • "; } } print $range_menu; ?>
    $view ) { if ( $view['view_name'] == $view_name ) { $view_elements = get_view_graph_elements($view); $range_args = ""; if ( isset($_GET['r']) && $_GET['r'] != "" ) $range_args .= "&r=" . rawurlencode($_GET['r']); if ( isset($_GET['cs']) && isset($_GET['ce']) ) $range_args .= "&cs=" . rawurlencode($_GET['cs']) . "&ce=" . rawurlencode($_GET['ce']); if ( count($view_elements) != 0 ) { foreach ( $view_elements as $id => $element ) { print " \"""; } } else { print "No graphs defined for this view. Please add some"; } } // end of if ( $view['view_name'] == $view_name } // end of foreach ( $views as $view_id print "
    "; } // end of if ( isset($_GET['view_name'])) /////////////////////////////////////////////////////////////////////////////// // Generate cluster summary view /////////////////////////////////////////////////////////////////////////////// if ( isset($_GET['show_cluster_metrics'])) { $clustername = $_GET['c']; ?>
    Back

    Cluster

    Home
      $v"; } else { $range_menu .= "
    • $v
    • "; } } print $range_menu; ?>
    array(), "excluded_reports" => array()); if ( is_file($conf['conf_dir'] . "/default.json") ) { $default_reports = array_merge($default_reports,json_decode(file_get_contents($conf['conf_dir'] . "/default.json"), TRUE)); } $cluster_file = $conf['conf_dir'] . "/cluster_" . preg_replace('/[^a-zA-Z0-9_-]/', '', $clustername) . ".json"; if ( pathinfo( $cluster_file, PATHINFO_DIRNAME ) != $conf['conf_dir'] ) { die('Invalid path detected'); } $override_reports = array("included_reports" => array(), "excluded_reports" => array()); if ( is_file($cluster_file) ) { $override_reports = array_merge($override_reports, json_decode(file_get_contents($cluster_file), TRUE)); } # Merge arrays $reports["included_reports"] = array_merge( $default_reports["included_reports"] , $override_reports["included_reports"]); $reports["excluded_reports"] = array_merge($default_reports["excluded_reports"] , $override_reports["excluded_reports"]); # Remove duplicates $reports["included_reports"] = array_unique($reports["included_reports"]); $reports["excluded_reports"] = array_unique($reports["excluded_reports"]); foreach ( $reports["included_reports"] as $index => $report_name ) { if ( ! in_array( $report_name, $reports["excluded_reports"] ) ) { print " \"".rawurlencode($clustername)."\" "; } } ?>
    ";
    Back

    Host

    Home
      $v"; } else { $range_menu .= "
    • $v
    • "; } } print $range_menu; ?>
    array(), "excluded_reports" => array()); if ( is_file($conf['conf_dir'] . "/default.json") ) { $default_reports = array_merge($default_reports,json_decode(file_get_contents($conf['conf_dir'] . "/default.json"), TRUE)); } $host_file = $conf['conf_dir'] . "/host_" . preg_replace('/[^a-zA-Z0-9_-]/', '', $hostname) . ".json"; if ( pathinfo( $host_file, PATHINFO_DIRNAME ) != $conf['conf_dir'] ) { die('Invalid path detected'); } $override_reports = array("included_reports" => array(), "excluded_reports" => array()); if ( is_file($host_file) ) { $override_reports = array_merge($override_reports, json_decode(file_get_contents($host_file), TRUE)); } # Merge arrays $reports["included_reports"] = array_merge( $default_reports["included_reports"] , $override_reports["included_reports"]); $reports["excluded_reports"] = array_merge($default_reports["excluded_reports"] , $override_reports["excluded_reports"]); # Remove duplicates $reports["included_reports"] = array_unique($reports["included_reports"]); $reports["excluded_reports"] = array_unique($reports["excluded_reports"]); foreach ( $reports["included_reports"] as $index => $report_name ) { if ( ! in_array( $report_name, $reports["excluded_reports"] ) ) { print " \"".rawurlencode($clustername)."\""; } } ?> $metric_attributes) { if ($metric_attributes['TYPE'] == "string" or $metric_attributes['TYPE']=="timestamp" or (isset($always_timestamp[$metric_name]) and $always_timestamp[$metric_name])) { $s_metrics[$metric_name] = $v; } elseif ($metric_attributes['SLOPE'] == "zero" or (isset($always_constant[$metric_name]) and $always_constant[$metric_name])) { $c_metrics[$metric_name] = $v; } else if (isset($reports[$metric_name]) and $reports[$metric]) continue; else { $metric_graphargs = "c=".rawurlencode($clustername)."&h=".rawurlencode($hostname)."&v=".rawurlencode($metric_attributes[VAL]) ."&m=$metric_name&r=".rawurlencode($range)."&z=$size&jr=$jobrange" ."&js=$jobstart&st=$cluster[LOCALTIME]"; if ($cs) $metric_graphargs .= "&cs=" . rawurlencode($cs); if ($ce) $metric_graphargs .= "&ce=" . rawurlencode($ce); # Adding units to graph 2003 by Jason Smith . if ($metric_attributes['UNITS']) { $encodeUnits = rawurlencode($metric_attributes['UNITS']); $metric_graphargs .= "&vl=$encodeUnits"; } if (isset($metric_attributes['TITLE'])) { $title = $metric_attributes['TITLE']; $metric_graphargs .= "&ti=$title"; } $g_metrics[$metric_name]['graph'] = $graph_args . "&" . $metric_graphargs; $g_metrics[$metric_name]['description'] = isset($metric_attributes['DESC']) ? $metric_attributes['DESC'] : ''; if ( !isset($metrics[$metric_name]['GROUP']) ) $group_name = "no group"; else $group_name = $metrics[$metric_name]['GROUP'][0]; // Make an array of groups if ( ! in_array($group_name, $groups) ) { $groups[] = $group_name; } $g_metrics_group[$group_name][] = $metric_name; } } ksort($g_metrics_group); foreach ( $g_metrics_group as $metric_group_name => $metric_group_members ) { ?>

    $metric_name ) { print " \"".htmlspecialchars($clustername)."\""; } ?>
    ganglia-web-3.6.1/nagios/000077500000000000000000000000001231750357400151655ustar00rootroot00000000000000ganglia-web-3.6.1/nagios/check_for_stuck_gmonds.php000066400000000000000000000040611231750357400224020ustar00rootroot00000000000000 $host_metrics ) { # if ( $ignore_host_reg != "" and preg_match("/" .$ignore_host_reg . "/", $mhost) ) { continue; } # Make sure that host is up and last time load_one was updated was over 3 minutes ago if ( $host_metrics['load_one']['TN'] > 180 && (( time() - $host_metrics['last_reported_timestamp']['VAL']) < 60 )) { # if ( $host_metrics['load_one']['TN'] > 180 ) { $stuck_gmond_hosts[] = $mhost; } } if ( count($stuck_gmond_hosts) == 0 ) { print "OK|All gmonds reporting normally. No stuck gmond hosts"; exit (0); } else { print "CRITICAL|Restart req'd on stuck gmonds => " . join(",", $stuck_gmond_hosts); exit (2); } ?> ganglia-web-3.6.1/nagios/check_for_stuck_gmonds.sh000077500000000000000000000022401231750357400222250ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_for_stuck_gmonds.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ $# -gt 0 ]; then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done fi RESULT=`curl -s ${GANGLIA_URL}?${CHECK_ARGS}` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done ganglia-web-3.6.1/nagios/check_ganglia_metric.sh000077500000000000000000000031551231750357400216320ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_metric.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 host=localhost.localdomain metric_name=load_one operator=more critical_value=1" exit 3 fi COMMAND="curl -s ${GANGLIA_URL}?${CHECK_ARGS}" RESULT=`$COMMAND` RETURN_VALUE=$? # if curl fails we should fail if [ $RETURN_VALUE -ne 0 ]; then echo "error fetching metric with command: ${COMMAND}" echo "result: $RESULT" echo "curl return_value: $RETURN_VALUE" exit 3 fi EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done # If we got no results that is an error in and of itself echo "UNKNOWN: no result from ganglia" exit 3 ganglia-web-3.6.1/nagios/check_heartbeat.php000066400000000000000000000065741231750357400210060ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $host_found = 0; # Find a FQDN of a supplied server name. for ( $i = 0 ; $i < sizeof($ganglia_hosts_array) ; $i++ ) { if ( strpos( $ganglia_hosts_array[$i], $host ) !== false ) { $fqdn = $ganglia_hosts_array[$i]; $host_found = 1; break; } } # Host has been found in the Ganglia tree if ( $host_found == 1 ) { # Check for the existence of a metric if ( isset($metrics[$fqdn]['last_reported_uptime_in_sec']['VAL']) ) { $last_reported = $metrics[$fqdn]['last_reported_uptime_in_sec']['VAL']; } else { echo("UNKNOWN|" . $metric_name . " - Invalid metric request for this host. Please check metric exists."); exit(3); } if ( $metrics[$fqdn]['last_reported_uptime_in_sec']['VAL'] < $threshold ) { print "OK|Last beacon received " . $metrics[$fqdn]['last_reported']['VAL']; exit (0); } else { print "CRITICAL|Last beacon received " . $metrics[$fqdn]['last_reported']['VAL']; exit (2); } } else { echo("UNKNOWN|" . $host. " - Hostname info not available. Likely invalid hostname"); exit(3); } ?> ganglia-web-3.6.1/nagios/check_heartbeat.sh000077500000000000000000000023651231750357400206260ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_heartbeat.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 host=localhost.localdomain threshold=20" exit 1 fi RESULT=`curl -s "${GANGLIA_URL}?${CHECK_ARGS}"` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done ganglia-web-3.6.1/nagios/check_host_regex.php000066400000000000000000000143351231750357400212100ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $results_ok = array(); $results_notok = array(); $metric_total_value = array(); # Loop through all hosts looking for matches foreach ( $ganglia_hosts_array as $index => $hostname ) { # Find matching hosts and make sure they are alive if ( preg_match("/" . $host_reg . "/", $hostname) && ( time() - $metrics[$hostname]['last_reported_timestamp']['VAL']) < 60) { # Loop through all the checks foreach ( $checks as $index => $check ) { # Separate check into it's pieces $pieces = explode(",", $check); $metric_name = $pieces[0]; $operator = $pieces[1]; $critical_value = $pieces[2]; unset($pieces); # Check for the existence of a metric if ( isset($metrics[$hostname][$metric_name]['VAL']) ) { $metric_value = $metrics[$hostname][$metric_name]['VAL']; } else { if ( !$ignore_unknowns ) { $results_notok[] = "UNKNOWN " . $hostname . " " . $metric_name . " not found"; } continue; } $ganglia_units = $metrics[$hostname][$metric_name]['UNITS']; if ( $operator == "totalmore" || $operator == "totalless") { if ( isset($metric_total_value[$metric_name]) ){ $metric_total_value[$metric_name]['VAL'] += $metric_value; }else{ $metric_total_value[$metric_name]['VAL'] = $metric_value; $metric_total_value[$metric_name]['UNITS'] = $ganglia_units; $metric_total_value[$metric_name]['CRIT'] = $critical_value; $metric_total_value[$metric_name]['OPER'] = $operator; } }else{ if ( ($operator == "less" && $metric_value > $critical_value) || ( $operator == "more" && $metric_value < $critical_value ) || ( $operator == "equal" && trim($metric_value) != trim($critical_value) ) || ( $operator == "notequal" && trim($metric_value) == trim($critical_value) ) ) { $results_ok[] = "OK " . $hostname . " " . $metric_name . " = " . $metric_value . " " . $ganglia_units; } else { $results_notok[] = "CRITICAL " . $hostname . " " . $metric_name . " = ". $metric_value . " " . $ganglia_units; } } } // end of foreach ( $checks as $index => $check } // end of if ( preg_match("/" . $host_reg } // end of foreach ( $ganglia_hosts_array as $index => $hostname ) { # Check for total metric value if( !empty($metric_total_value) ) { foreach ( $metric_total_value as $metric_name => $metric_total ) { if ( ( $metric_total['OPER'] == 'totalmore' && $metric_total['VAL'] < $metric_total['CRIT'] ) || ( $metric_total['OPER'] == 'totalless' && $metric_total['VAL'] > $metric_total['CRIT'] ) ){ $results_ok[] = 'OK "' . $host_reg . '" Total of ' . $metric_name . ' = ' . $metric_total['VAL'] . ' ' . $metric_total['UNITS']; } else { $results_notok[] = 'CRITICAL ' . $host_reg . ' Total of ' . $metric_name . ' = ' . $metric_total['VAL'] . ' ' . $metric_total['UNITS']; } } } unset($metric_total_value); if ( sizeof( $results_notok ) == 0 ) { print "OK!# Services OK = " . count($results_ok) . " ; " . join(",", $results_ok); exit(0); } else { print "CRITICAL!# Services OK = " . count($results_ok) . ", CRIT/UNK = " . count($results_notok) . " ; " . join(", ", $results_notok); exit(2); } ?> ganglia-web-3.6.1/nagios/check_host_regex.sh000077500000000000000000000027741231750357400210420ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia/nagios/check_host_regex.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 hreg=web|apache checks=load_one,more,1:load_five,more,2 ignore_unknowns=0" echo " Set ignore_unknowns=1 if you want to ignore hosts that don't posses a particular metric." echo " This is useful if you want to use a catchall regex e.g. .* however some hosts lack a metric" exit 1 fi RESULT=`curl -s -g "${GANGLIA_URL}?${CHECK_ARGS}"` EXIT_CODE=`echo $RESULT | cut -f1 -d'!'` REST=`echo $RESULT | cut -f2 -d'!'` for x in $EXIT_CODE; do case $x in OK) echo $REST exit 0;; WARNING) echo $REST exit 1;; CRITICAL) echo $REST exit 2;; *) echo $REST exit 3;; esac done ganglia-web-3.6.1/nagios/check_metric.php000066400000000000000000000073211231750357400203210ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $host_found = 0; # Find a FQDN of a supplied server name. for ( $i = 0 ; $i < sizeof($ganglia_hosts_array) ; $i++ ) { if ( !strcasecmp( $ganglia_hosts_array[$i], $host ) ) { $fqdn = $ganglia_hosts_array[$i]; $host_found = 1; break; } } # Host has been found in the Ganglia tree if ( $host_found == 1 ) { # Check for the existence of a metric if ( isset($metrics[$fqdn][$metric_name]['VAL']) ) { $metric_value = $metrics[$fqdn][$metric_name]['VAL']; } else { echo("UNKNOWN|" . $metric_name . " - Invalid metric request for this host. Please check metric exists."); exit(3); } $ganglia_units = $metrics[$fqdn][$metric_name]['UNITS']; if ( ($operator == "less" && $metric_value > $critical_value) || ( $operator == "more" && $metric_value < $critical_value ) || ( $operator == "equal" && trim($metric_value) != trim($critical_value) ) || ( $operator == "notequal" && trim($metric_value) == trim($critical_value) ) ) { print "OK|" . $metric_name . " = " . $metric_value . " " . $ganglia_units; exit (0); } else { print "CRITICAL|" . $metric_name . " = ". $metric_value . " " . $ganglia_units; exit (2); } } else { echo("UNKNOWN|" . $host. " - Hostname info not available. Likely invalid hostname"); exit(3); } ?> ganglia-web-3.6.1/nagios/check_metric_regex.php000066400000000000000000000113161231750357400215120ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $results_ok = array(); $results_notok = array(); # Loop through all hosts looking for matches foreach ( $ganglia_hosts_array as $index => $hostname ) { # Find matching hosts and make sure they are alive if ( preg_match("/" . $host_reg . "/", $hostname) && ( time() - $metrics[$hostname]['last_reported_timestamp']['VAL']) < 60) { # Loop through all the checks foreach ( $checks as $index => $check ) { # Separate check into it's pieces $pieces = explode(",", $check); $metric_name = $pieces[0]; $operator = $pieces[1]; $critical_value = $pieces[2]; unset($pieces); foreach ( $metrics[$hostname] AS $m_k => $m_v ) { if ( preg_match("/" . $metric_name . "/", $m_k) ) { $metric_value = $m_v['VAL']; $ganglia_units = $m_v['UNITS']; if ( ($operator == "less" && $metric_value > $critical_value) || ( $operator == "more" && $metric_value < $critical_value ) || ( $operator == "equal" && trim($metric_value) != trim($critical_value) ) || ( $operator == "notequal" && trim($metric_value) == trim($critical_value) ) ) { $results_ok[] = "OK " . $hostname . " " . $m_k . " = " . $metric_value . " " . $ganglia_units; } else { $results_notok[] = "CRITICAL " . $hostname . " " . $m_k . " = ". $metric_value . " " . $ganglia_units; } } } } // end of foreach ( $checks as $index => $check } // end of if ( preg_match("/" . $host_reg } // end of foreach ( $ganglia_hosts_array as $index => $hostname ) { if ( sizeof( $results_notok ) == 0 ) { print "OK|# Services OK = " . count($results_ok); exit(0); } else { print "CRITICAL|# Services OK = " . count($results_ok) . ", CRIT/UNK = " . count($results_notok) . " ; " . join(", ", $results_notok); exit(2); } ?> ganglia-web-3.6.1/nagios/check_metric_regex.sh000077500000000000000000000027531231750357400213450ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_metric_regex.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 hreg=web|apache checks=load_one,more,1:load_five,more,2" echo " Set ignore_unknowns=1 if you want to ignore hosts that don't posses a particular metric." echo " This is useful if you want to use a catchall regex e.g. .* however some hosts lack a metric" exit 1 fi RESULT=`curl -s "${GANGLIA_URL}?${CHECK_ARGS}"` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` REST=`echo $RESULT | cut -f2 -d'|'` for x in $EXIT_CODE; do case $x in OK) echo $REST exit 0;; WARNING) echo $REST exit 1;; CRITICAL) echo $REST exit 2;; *) echo $REST exit 3;; esac done ganglia-web-3.6.1/nagios/check_multiple_metrics.php000066400000000000000000000111351231750357400224150ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $host_found = 0; # Find a FQDN of a supplied server name. for ( $i = 0 ; $i < sizeof($ganglia_hosts_array) ; $i++ ) { if ( strpos( $ganglia_hosts_array[$i], $host ) !== false ) { $fqdn = $ganglia_hosts_array[$i]; $host_found = 1; break; } } # Host has been found in the Ganglia tree if ( $host_found == 1 ) { $results_ok = array(); $results_notok = array(); # Loop through all the checks foreach ( $checks as $index => $check ) { # Separate check into it's pieces $pieces = explode(",", $check); $metric_name = $pieces[0]; $operator = $pieces[1]; $critical_value = $pieces[2]; unset($pieces); # Check for the existence of a metric if ( isset($metrics[$fqdn][$metric_name]['VAL']) ) { $metric_value = $metrics[$fqdn][$metric_name]['VAL']; } else { $results_notok[] = "UNKNOWN " . $metric_name . " not found"; continue; } $ganglia_units = $metrics[$fqdn][$metric_name]['UNITS']; if ( ($operator == "less" && $metric_value > $critical_value) || ( $operator == "more" && $metric_value < $critical_value ) || ( $operator == "equal" && trim($metric_value) != trim($critical_value) ) || ( $operator == "notequal" && trim($metric_value) == trim($critical_value) ) ) { $results_ok[] = "OK " . $metric_name . " = " . $metric_value . " " . $ganglia_units; } else { $results_notok[] = "CRITICAL " . $metric_name . " = ". $metric_value . " " . $ganglia_units; } } // end of foreach ( $checks as $index => $check if ( sizeof( $results_notok ) == 0 ) { print "OK| Num OK: " . count($results_ok); exit(0); } else { print "CRITICAL| Num CRIT/UNK: " . count($results_notok) . " Num OK: " . count($results_ok) . " -- " . join(", ", $results_notok); exit(2); } } else { echo("UNKNOWN|" . $host . " - Hostname info not available. Likely invalid hostname"); exit(3); } ?> ganglia-web-3.6.1/nagios/check_multiple_metrics.sh000077500000000000000000000024251231750357400222450ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_multiple_metrics.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 host=localhost.localdomain checks=load_one,more,1:load_five,more,2" exit 1 fi RESULT=`curl -s ${GANGLIA_URL}?${CHECK_ARGS}` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done ganglia-web-3.6.1/nagios/check_multiple_metrics_warn.php000066400000000000000000000131061231750357400234440ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $host_found = 0; # Find a FQDN of a supplied server name. for ( $i = 0 ; $i < sizeof($ganglia_hosts_array) ; $i++ ) { if ( strpos( $ganglia_hosts_array[$i], $host ) !== false ) { $fqdn = $ganglia_hosts_array[$i]; $host_found = 1; break; } } # Host has been found in the Ganglia tree if ( $host_found == 1 ) { $results_ok = array(); $results_warn = array(); $results_crit = array(); # Loop through all the checks foreach ( $checks as $index => $check ) { # Separate check into it's pieces $pieces = explode(",", $check); $metric_name = $pieces[0]; $warn_operator = $pieces[1]; $warn_value = $pieces[2]; $critical_operator = $pieces[3]; $critical_value = $pieces[4]; unset($pieces); # Check for the existence of a metric if ( isset($metrics[$fqdn][$metric_name]['VAL']) ) { $metric_value = $metrics[$fqdn][$metric_name]['VAL']; } else { continue; } $ganglia_units = $metrics[$fqdn][$metric_name]['UNITS']; if ( ( $critical_operator == "less" && $metric_value < $critical_value) || ( $critical_operator == "more" && $metric_value > $critical_value ) || ( $critical_operator == "equal" && trim($metric_value) == trim($critical_value) ) || ( $critical_operator == "notequal" && trim($metric_value) != trim($critical_value) ) ) { $results_crit[] = "CRITICAL " . $metric_name . " = ". $metric_value . " " . $ganglia_units; } else if ( ( $warn_operator == "less" && $metric_value < $warn_value) || ( $warn_operator == "more" && $metric_value > $warn_value ) || ( $warn_operator == "equal" && trim($metric_value) == trim($warn_value) ) || ( $warn_operator == "notequal" && trim($metric_value) != trim($warn_value) ) ){ $results_warn[] = "WARNING " . $metric_name . " = ". $metric_value . " " . $ganglia_units; } else { $results_ok[] = "OK " . $metric_name . " = " . $metric_value . " " . $ganglia_units; } } // end of foreach ( $checks as $index => $check if ( sizeof( $results_crit ) != 0 ) { print "CRITICAL|System check - CRIT: (" . count($results_crit) . ") WARN: (" . count($results_warn) . ") OK: (" . count($results_ok) . ") --" . join(",", $results_crit) . " --" . join(",", $results_warn) . "--" . join(",", $results_ok); exit(2); } else if ( sizeof( $results_warn ) != 0 ) { print "WARNING|System check - WARN: (" . count($results_warn) . ") OK: (" . count($results_ok) . ") --" . join(",", $results_warn) . "--" . join(",", $results_ok); exit(1); } else if ( sizeof( $results_ok ) != 0 ) { print "OK|System check - OK: (" . count($results_ok) . ") --" . join(",", $results_ok); exit(0); } else { print("UNKNOWN|System check - No metrics returned values"); exit(3); } } else { echo("UNKNOWN|System check - " . $host. " - Hostname info not available. Likely invalid hostname"); exit(3); } ?> ganglia-web-3.6.1/nagios/check_multiple_metrics_warn.sh000077500000000000000000000027161231750357400232770ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The greplin-nagios-utils Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_multiple_metrics_warn.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Please provide arguments for host and checks, with check argument being a metric name, warning operator, warning value, critical operator, critical value" echo "Sample invocation $0 host=localhost.localdomain checks='load_one,more,1,more,2:load_five,more,2,more,4'" exit 1 fi RESULT=`curl -s ${GANGLIA_URL}?${CHECK_ARGS}` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done ganglia-web-3.6.1/nagios/check_value_same_everywhere.php000066400000000000000000000111041231750357400234160ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$mhost][$name]['VAL'] = $metrics[$mhost][$name]['VAL']; if ( isset($metrics[$mhost][$name]['UNITS']) ) $new_metrics[$mhost][$name]['UNITS'] = $metrics[$mhost][$name]['UNITS']; } } file_put_contents($conf['nagios_cache_file'], serialize($new_metrics)); unset($metrics); $metrics = $new_metrics; unset($new_metrics); } # Get a list of all hosts $ganglia_hosts_array = array_keys($metrics); $results_ok = array(); $results_notok = array(); # Initialize results array foreach ( $check_metrics as $index => $metric_name ) { $results[$metric_name]["values"] = array(); } // Loop through all hosts looking for matches foreach ( $ganglia_hosts_array as $index => $hostname ) { // Find matching hosts and make sure they are alive if ( preg_match("/" . $host_reg . "/", $hostname) && ( time() - $metrics[$hostname]['last_reported_timestamp']['VAL']) < 60) { // Now let's look at all the metrics foreach ( $check_metrics as $index => $metric_name ) { // Check for the existence of a metric if ( isset($metrics[$hostname][$metric_name]['VAL']) ) { $metric_value = $metrics[$hostname][$metric_name]['VAL']; // First we check if we have seen the value already. If not add the value to the // values array and add the host to members array if ( ! in_array($metric_value, $results[$metric_name]["values"] ) ) { $results[$metric_name]["values"][] = $metric_value; } // We have seen the value before // Find index of the value $value_index = array_search($metric_value, $results[$metric_name]["values"]); $results[$metric_name]["members"][$value_index][] = $hostname; } // end of if ( isset($metrics[$hostname][$metric_name]['VAL']) ) } // end of foreach ( $check_metrics as $index => $metric_name ) } // end of if ( preg_match("/" . $host_reg } // end of foreach ( $ganglia_hosts_array as $index => $hostname ) { $ok=true; $output = ""; foreach ( $results as $metric_name => $metrics_results ) { if ( sizeof($metrics_results["values"]) > 1 ) { $ok=false; $output .= " CRIT " . $metric_name . " differs values => "; foreach ( $metrics_results["values"] as $index => $value ) { $output .= $value . " ( " . join("," , $metrics_results["members"][$index]) . " ) "; } } else { $output .= ", " .$metric_name . " same => " . sizeof($metrics_results["members"][0]) . " nodes"; } } if ( $ok === true ) { print "OK|" . $output; exit(0); } else { print "CRITICAL|" . $output; exit(2); } ?> ganglia-web-3.6.1/nagios/check_value_same_everywhere.sh000077500000000000000000000024021231750357400232450ustar00rootroot00000000000000#!/bin/bash # Copyright 2011 The ganglia-web Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. GANGLIA_URL="http://localhost/ganglia2/nagios/check_value_same_everywhere.php" # Build the rest of the arguments into the arg string for the URL. CHECK_ARGS='' if [ "$#" -gt "0" ] then CHECK_ARGS=$1 shift for ARG in "$@" do CHECK_ARGS=${CHECK_ARGS}"&"${ARG} done else echo "Sample invocation $0 hreg=localhost.localdomain checks=svn_rev,deploy_id" exit 1 fi RESULT=`curl -s ${GANGLIA_URL}?${CHECK_ARGS}` EXIT_CODE=`echo $RESULT | cut -f1 -d'|'` IFS='|' for x in $EXIT_CODE; do case $x in OK) echo $RESULT exit 0;; WARNING) echo $RESULT exit 1;; CRITICAL) echo $RESULT exit 2;; *) echo $RESULT exit 3;; esac done ganglia-web-3.6.1/nagios/warmup_ganglia_cache.php000066400000000000000000000003701231750357400220160ustar00rootroot00000000000000 ganglia-web-3.6.1/nagios/warmup_metric_cache.php000066400000000000000000000026541231750357400217060ustar00rootroot00000000000000 $host_metrics ) { foreach ( $host_metrics as $name => $attributes ) { $new_metrics[$host][$name]['VAL'] = $metrics[$host][$name]['VAL']; if ( isset($metrics[$host][$name]['UNITS']) ) $new_metrics[$host][$name]['UNITS'] = $metrics[$host][$name]['UNITS']; } # Put host metrics in their own files as well file_put_contents($conf['nagios_cache_file'] . "_" . $host, serialize($new_metrics[$host])); } $temp_file = $conf['nagios_cache_file'] . ".temp"; file_put_contents($temp_file, serialize($new_metrics)); rename($temp_file, $conf['nagios_cache_file']); ?> ganglia-web-3.6.1/nagios/warmup_metric_cache.sh000077500000000000000000000007531231750357400215320ustar00rootroot00000000000000#!/bin/sh ################################################################# # This script will warmup the cache periodically (specified # by SLEEP_TIME). This is useful if you have lots of Nagios # checks ################################################################# SLEEP_TIME=11 WEB_USER="www-data" SCRIPT_HOME=${0%/*} # Go into an infinite loop while [ 1 ]; do echo "Metric cache - "`date` sudo -u $WEB_USER php $SCRIPT_HOME/warmup_metric_cache.php sleep $SLEEP_TIME done ganglia-web-3.6.1/node_legend.html000066400000000000000000000023561231750357400170440ustar00rootroot00000000000000 Ganglia Cluster Toolkit:: Node Image Legend
    Ganglia Node Image Legend
    Node ImageMeaning
    Red Over 100% Utilization. Utilization is: (1 min load) / (number of CPUs) * 100%.
    Orange75-100%
    Yellow50-74%
    Green25-49%
    Blue0-24%
    CrossbonesThe node is dead. We consider a node dead when the reporting node has not heard from it in 60 sec.

    Back

    ganglia-web-3.6.1/physical_view.php000066400000000000000000000164351231750357400172750ustar00rootroot00000000000000 # Part of the Ganglia Project, All Rights Reserved. # # Called from index.php, so cluster and xml tree vars # ($metrics, $clusters, $hosts) are set, and header.php # already called. $tpl = new Dwoo_Template_File( template("physical_view.tpl") ); $data = new Dwoo_Data(); $data->assign("cluster",$clustername); $cluster_url=rawurlencode($clustername); $data->assign("cluster_url",$cluster_url); $verbosity_levels = array('3' => "", '2' => "", '1' => ""); # Assign the verbosity level. Can take the value of the 'p' CGI variable. $verbose = $physical ? $physical : 2; $verbosity_levels[$verbose] = "checked"; $data->assign("verbosity_levels", $verbosity_levels); # # Give the capacities of this cluster: total #CPUs, Memory, Disk, etc. # $CPUs = cluster_sum("cpu_num", $metrics); # Divide by 1024^2 to get Memory in GB. $Memory = sprintf("%.1f GB", cluster_sum("mem_total", $metrics)/(float)1048576); $Disk = cluster_sum("disk_total", $metrics); $Disk = $Disk ? sprintf("%.1f GB", $Disk) : "Unknown"; list($most_full, $most_full_host) = cluster_min("part_max_used", $metrics); $data->assign("CPUs", $CPUs); $data->assign("Memory", $Memory); $data->assign("Disk", $Disk); # Show which node has the most full disk. $most_full_hosturl=rawurlencode($most_full_host); $most_full = $most_full ? "". "$most_full_host ($most_full% Used)" : "Unknown"; $data->assign("most_full", $most_full); $data->assign("cols_menu", $cols_menu); #------------------------------------------------------------------------------ # Organize hosts by rack locations. # Works with or without "location" host attributes. function physical_racks() { global $hosts_up, $hosts_down; # 2Key = "Rack ID / Rank (order in rack)" = [hostname, UP|DOWN] $rack = NULL; # If we don't know a node's location, it goes in a negative ID rack. $i=1; $unknownID= -1; if (is_array($hosts_up)) { foreach ($hosts_up as $host=>$v) { # Try to find the node's location in the cluster. list($rack, $rank, $plane) = findlocation($v); if ($rack>=0 and $rank>=0 and $plane>=0 and !array_key_exists($rank, $racks[$rack])) { $racks[$rack][$rank]=$v['NAME']; continue; } else { $i++; if (! ($i % 25)) { $unknownID--; } $racks[$unknownID][] = $v['NAME']; } } } if (is_array($hosts_down)) { foreach ($hosts_down as $host=>$v) { list($rack, $rank, $plane) = findlocation($v); if ($rack>=0 and $rank>=0 and $plane>=0 and !array_key_exists($rank, $racks[$rack])) { $racks[$rack][$rank]=$v['NAME']; continue; } else { $i++; if (! ($i % 25)) { $unknownID--; } $racks[$unknownID][] = $v['NAME']; } } } # Sort the racks array. if ($unknownID<-1) { krsort($racks); } else { ksort($racks); reset($racks); while (list($rack,) = each($racks)) { # In our convention, y=0 is close to the floor. (Easier to wire up) krsort($racks[$rack]); } } return $racks; } #------------------------------------------------------------------------------ # # Generates the colored Node cell HTML. Used in Physical # view and others. Intended to be used to build a table, output # begins with "" and ends the same. function nodebox($hostname, $verbose, $title="", $extrarow="") { global $cluster, $clustername, $metrics, $hosts_up, $GHOME; if (!$title) $title = $hostname; # Scalar helps choose a load color. The lower it is, the easier to get red. # The highest level occurs at a load of (loadscalar*10). $loadscalar=0.2; # An array of [NAME|VAL|TYPE|UNITS|SOURCE]. $m=$metrics[$hostname]; $up = $hosts_up[$hostname] ? 1 : 0; # The metrics we need for this node. # Give memory in Gigabytes. 1GB = 2^20 bytes. $mem_total_gb = $m['mem_total']['VAL']/1048576; $load_one=$m['load_one']['VAL']; $cpu_speed=round($m['cpu_speed']['VAL']/1000, 2); $cpu_num= $m['cpu_num']['VAL']; # # The nested tables are to get the formatting. Insane. # We have three levels of verbosity. At L3 we show # everything; at L1 we only show name and load. # $rowclass = $up ? rowStyle() : "down"; $host_url=rawurlencode($hostname); $cluster_url=rawurlencode($clustername); $row1 = "\n". "". "\n"; # Construct cell. $cell = $row1; if ($extrarow) $cell .= $extrarow; if ($verbose>1) $cell .= $row2; $cell .= "
    ". "$title 
    \n"; $cpus = $cpu_num > 1 ? "($cpu_num)" : ""; if ($up) $hardware = sprintf("cpu: %.2fG %s ", $cpu_speed, $cpus) . sprintf("mem: %.2fG",$mem_total_gb); else $hardware = " "; $row2 = "
    "; if ($verbose==2) $row2 .= $hardware; else if ($verbose > 2) { $hostattrs = $up ? $hosts_up : $hosts_down; $last_heartbeat = $hostattrs[$hostname]['TN']; $age = $last_heartbeat > 3600 ? uptime($last_heartbeat) : "${last_heartbeat}s"; $row2 .= "Last heartbeat $age"; $row3 = $hardware; } # # Load box. # if (!$cpu_num) $cpu_num=1; $loadindex = intval($load_one / ($loadscalar*$cpu_num)) + 1; # 10 is currently the highest allowed load index. $load_class = $loadindex > 10 ? "L10" : "L$loadindex"; $row1 .= "". "". "
    $load_one". "
    ". "
    \n"; # Tricky. if ($verbose>2) $cell .= $row3; $cell .= "\n"; return $cell; } #------------------------------------------------------------------------------- # Displays a rack and all its nodes. function showrack($ID) { global $verbose, $racks, $racks_data, $metrics, $cluster, $hosts_up, $hosts_down; global $cluster_url, $tpl, $clusters; $racks_data[$ID]["RackID"] = ""; if ($ID>=0) { $racks_data[$ID]["RackID"] = "Rack $ID"; } # A string of node HTML for the template. $nodes=""; foreach ($racks[$ID] as $name) { $nodes .= nodebox($name, $verbose); } return $nodes; } #------------------------------------------------------------------------------- # # My Main # # 2Key = "Rack ID / Rank (order in rack)" = [hostname, UP|DOWN] $racks = physical_racks(); $racks_data = array(); # Make a $cols-wide table of Racks. $i=1; foreach ($racks as $rack=>$v) { $racknodes = showrack($rack); $racks_data[$rack]["nodes"] = $racknodes; $racks_data[$rack]["tr"] = ""; if (! ($i++ % $conf['hostcols'])) { $racks_data["tr"] = ""; } } $data->assign("racks", $racks_data); $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/pie.php000066400000000000000000000300771231750357400152020ustar00rootroot00000000000000 and modified heavily by Federico Sacerdoti */ /* ** ** PHP Class for creating pie charts using the GD library functions ** ** There is a bug in the GD library somewhere that seems to kick in ** when you try to return images that are larger than 4K. We probably ** need a workaround for this... ** ** Pie charts look a bit shabby. There seems to be one or more ** roundoff errors lurking about making life hard for us. To fix this ** we should perhaps investigate how the Arc-drawing thingey works and ** try to find out how it gets the endpoints. Also the flood-filler ** doesn't quite cope with filling the pieces very well. ** ** Authors: Bjrn Borud, Guardian Networks AS, * * Modified to accept data as CGI variables, and made * more robust with different versions of GD by * Federico Sacerdoti */ /* {{{ piechart */ /* ** This is a class for creating pie charts. Generally you just have ** to instantiate it, and then make a call to the "init" method to ** set the size and transfer the data. ** ** The data is an array of arrays that consist the following data: **o numeric value **o value legend **o red \ **o green > the RGB values for the color of the slice/legend **o blue / ** */ if (!function_exists('imagegif') and !function_exists('imagepng')) { echo "No image support in this version of PHP
    "; } class piechart { /* {{{ attributes */ var $im; var $width, $height; var $data; var $colors; var $angles; var $left=10; var $right=140; var $top=30; var $bottom=10; var $head_top=5; var $head_space=2; var $legend_left=20; var $center_x; var $center_y; var $diameter; /* sum of values */ var $sum; /* font sizes */ var $fx, $fy; var $legend_num = ""; /* }}} */ /* {{{ constants */ var $PI = 3.1415926535897931; /* }}} */ /* {{{ roundoff */ /* ** PHP has no function for rounding off doubles to the nearest ** integer so we have to roll our own. */ function roundoff ($v) { if ( $v - floor($v) >= 0.5) { return(ceil($v)); } else { return(floor($v)); } } /* }}} */ /* {{{ deg2rad */ /* ** The built-in trig functions use radians and there's no ** function in PHP to convert between degrees and radians */ function deg2rad ($degrees) { return (($this->PI * $degrees) / doubleval(180)); } /* }}} */ /* {{{ get_xy_factors */ /* ** Calculate the directional vector for the sides of the ** piece of pie. */ function get_xy_factors ($degrees) { $x = cos($this->deg2rad($degrees)); $y = sin($this->deg2rad($degrees)); return (array($x, $y)); } /* }}} */ /* {{{ init */ /* ** Initialize the object and draw the pie. This would be the ** constructor in an ordinary OO scenario -- just that we haven't ** got constructors in PHP, now do we? ;-) ** ** RB 09.03.2006: ** - rearranged: please use indentation to seperate if/for blocks/statements! ** - modified to use draw_slices in stead of draw_slice */ function init ($w, $h, $d) { $this->im= ImageCreate($w, $h); imagesavealpha($this->im, true); $this->width = $w; $this->height = $h; $this->data = $d; $this->da_width = ($this->width - $this->left - $this->right); $this->da_height = ($this->height - $this->top - $this->bottom); $this->center_x = intval($this->left + ($this->da_width / 2)); $this->center_y = intval($this->top + ($this->da_height / 2)); /* font sizes */ $this->fx = array(0, 5,6,7,8,9); $this->fy = array(0, 7,8,10,14,11); /* decide the diameter of the pie */ if ($this->da_height > $this->da_width) { $this->diameter = $this->da_width; } else { $this->diameter = $this->da_height; } $this->white = ImageColorAllocateAlpha($this->im, 0, 0, 0, 127); imagefill($this->im, 0, 0, $this->white); $this->black = ImageColorAllocate($this->im,0,0,0); $n = count($this->data); for ($i = 0; $i < $n; $i++) { $this->colors[$i] = ImageColorAllocate($this->im, $this->data[$i][2], $this->data[$i][3], $this->data[$i][4]); $this->sum += $this->data[$i][0]; } $from = 0;$to = 0; for ($i = 0; $i < $n; $i++) { $this->angles[$i] = $this->roundoff( ($this->data[$i][0] * 360) / doubleval($this->sum)); } $this->draw_slices( $this->center_x, $this->center_y, $this->angles, $this->colors ); } /* }}} */ /* {{{ set_legend_percent */ /* utility function to set an attribute so we display percentages */ function set_legend_percent () { $this->legend_num = "p"; } /* }}} */ /* {{{ set_legend_value */ /* utility function to set an attribute so we display values */ function set_legend_value () { $this->legend_num = "v"; } /* }}} */ /* {{{ draw_point */ /* ** This function is just here for debugging purposes. It is ** sometimes very useful to be able to draw an X to check ** coordinates. */ function draw_point($x, $y) { ImageLine($this->im, $x-4, $y-4, $x+4, $y+4, $this->black); ImageLine($this->im, $x-4, $y+4, $x+4, $y-4, $this->black); } /* }}} */ /* {{{ draw_margins */ /* ** Also a debugging function to show where the margins are at */ function draw_margins () { ImageLine($this->im, 0, $this->top, $this->width, $this->top, $this->black); ImageLine($this->im, 0, $this->height - $this->bottom, $this->width, $this->height - $this->bottom, $this->black); ImageLine($this->im, $this->left, 0, $this->left, $this->height, $this->black); ImageLine($this->im, $this->width - $this->right, 0, $this->width - $this->right, $this->height, $this->black); } /* }}} */ /* {{{ draw_legends */ /* ** Draw legends at the right side of the pie chart. This function ** accepts a fontsize and gathers all the other information from ** the multilevel data array */ function draw_legends ($fontsize) { $n = count($this->data); $x1 = $this->width - $this->right + $this->legend_left; $x2 = $x1 + $this->fy[$fontsize]; for ($i = 0; $i < $n; $i++) { /* determine Y coordinates */ $y1 = ($i * $this->fy[$fontsize] * 1.5) + $this->top; $y2 = $y1 + $this->fy[$fontsize]; /* draw the legend color rectangle */ ImageFilledRectangle($this->im, $x1, $y1, $x2, $y2, $this->colors[$i]); ImageRectangle($this->im, $x1, $y1, $x2, $y2, $this->black); $legend = $this->data[$i][1]; /* decide what to show after legend */ switch ($this->legend_num) { case "v": $legend .= sprintf(" (%.2f)", $this->data[$i][0]); break; case "p": $legend .= sprintf(" (%.2f%%)", ($this->data[$i][0] * 100 / doubleval($this->sum))); break; } ImageString($this->im, $fontsize, $x2 + 5, $y1, $legend, $this->black); } } /* }}} */ /* {{{ draw_heading */ /* ** This function accepts an array of arrays containing (in order): ** o The text of the heading as a string ** o The fontsize as an integer ** o The justification ("c"=center) ** */ function draw_heading($head_data) { $n = count($head_data); $y = $this->head_top; for ($i = 0; $i < $n; $i++) { switch($head_data[$i][2]) { case "c": $x = ($this->width - $this->fx[$head_data[$i][1]] * strlen($head_data[$i][0])) / 2; break; case "r": /* uses left margin here... */ $x = $this->width - $this->left - ($this->fx[$head_data[$i][1]] * strlen($head_data[$i][0])); break; default: $x = $this->left; break; } ImageString($this->im, $head_data[$i][1], $x, $y, $head_data[$i][0], $this->black); $y += ($this->fy[$head_data[$i][1]] + $this->head_space); } } /* }}} */ /* {{{ draw_slices */ /* ** This function draws pieces of pie centered at x,y starting at ** "from" degrees and ending at "to" degrees using the specified color. ** ** RB 09.03.2006: ** - fixed: ImageFill for pie slices broke when a very small pie was drawn, ** and filled entire image with color (orange background color). ** - modified draw_slice to be draw_slices and added 3D effect ;) */ function draw_slices( $x, $y, $angles, $colors ) { $pie_count = count( $angles ); $PIE_THICKNESS = ($this->diameter * 0.075); for( $j = ($this->center_y+$PIE_THICKNESS); $j > $this->center_y; $j-- ) { for( $from = 0, $p = 0; $p < $pie_count; $p++, $from = $to ) { $to = $from + $angles[$p]; if( $to > 360 ) $to = 360; if ($to == $from) continue; $color = $colors[$p]; $orig_colors = imageColorsForIndex( $this->im, $color ); $dark_red = ( $orig_colors['red'] > 30 ) ? $orig_colors['red'] - 30 : $orig_colors['red']; $dark_green = ( $orig_colors['green'] > 30 ) ? $orig_colors['green'] - 30 : $orig_colors['green']; $dark_blue = ( $orig_colors['blue'] > 30 ) ? $orig_colors['blue'] - 30 : $orig_colors['blue']; $new_color = ImageColorAllocate( $this->im, $dark_red, $dark_green, $dark_blue ); ImageFilledArc( $this->im, $this->center_x, $j, $this->diameter, $this->diameter, $from, $to, $new_color, IMG_ARC_PIE ); } } for( $from = 0, $p = 0; $p < $pie_count; $p++, $from = $to ) { $to = $from + $angles[$p]; $color = $colors[$p]; if( $to > 360 ) $to = 360; if ($to == $from) continue; ImageFilledArc( $this->im, $this->center_x, $this->center_y, $this->diameter, $this->diameter, $from, $to, $color, IMG_ARC_PIE ); $from += $angles[$p]; } } /* }}} */ /* {{{ display */ /* ** Make sure the legends are drawn, then output the image to the ** client */ function display() { $this->draw_legends(2); #$this->draw_margins(); if (function_exists("imagepng")) { header ("Content-type: image/png"); imagepng ($this->im); } elseif (function_exists("imagegif")) { header ("Content-type: image/gif"); imagegif ($this->im); } elseif (function_exists("imagejpeg")) { header ("Content-type: image/jpeg"); imagejpeg ($this->im, "", 0.5); } elseif (function_exists("imagewbmp")) { header ("Content-type: image/vnd.wap.wbmp"); imagewbmp ($this->im); } else die("No image support in this PHP server"); } // Converts a hexcode color without a leading "#" into // its decimal red, green, blue components. function hex2rgb($hex) { $r = hexdec(substr($hex, 0, 2)); $g = hexdec(substr($hex, 2, 2)); $b = hexdec(substr($hex, 4, 2)); return array($r, $g, $b); } /* }}} */ }; /* }}} */ $debug=0; // Parse CGI GET vars and generate chart. // // Format is ?name=value,color&name2=value,color... // where name is set title, and color is in hex like (ffffff). // // Special names: title=[graph title], size=[WxH]. $vals = array(); $pie = new piechart; foreach($_GET as $key=>$val) { if ($key == "title") $heads[] = array($val, 4, "c"); elseif ($key == "size") list($w, $h) = explode("x", $val); else { list($value, $color) = explode(",", $val); list($r, $g, $b) = $pie->hex2rgb($color); $vals[] = array($value, $key, $r, $g, $b); } } if (!$w or !$h) { $w = 400; $h = 300; } $pie->init($w, $h, $vals); $pie->draw_heading($heads); $pie->set_legend_percent(); $pie->display(); ?> ganglia-web-3.6.1/search.php000066400000000000000000000045151231750357400156700ustar00rootroot00000000000000 $host_name ) { if ( preg_match("/$query/i", $host_name ) ) { $clusters = $index_array['cluster'][$host_name]; foreach ($clusters AS $cluster_name) { if ( $mobile ) $results .= 'Host: ' . $host_name ." (" . $cluster_name . ')'; else $results .= "Host: " . $host_name . " ( " . $cluster_name . " )
    "; } } } // Now let's look through metrics. foreach ( $index_array['metrics'] as $metric_name => $hosts ) { if ( preg_match("/$query/i", $metric_name ) ) { foreach ( $hosts as $key => $host_name ) { $clusters = $index_array['cluster'][$host_name]; foreach ($clusters AS $cluster_name) { if ( $mobile ) { $results .= 'Metric: ' . $host_name . " (" . $metric_name . ")
    "; } else { $results .= "Metric: " . $host_name . " @ " . $cluster_name . " (" . $metric_name . ")
    "; } } } } } } else { $results .= "Empty query string"; } if ( $results == "" ) { print "No results. Try a different search term. One term only."; } else { if ( $mobile ) { print $results; } else { print $results; } } ?> ganglia-web-3.6.1/show_node.php000066400000000000000000000116231231750357400164060ustar00rootroot00000000000000 # # Host is specified in get_context.php. if (empty($hostname)) { print "

    Missing a Node Name

    "; return; } $tpl = new Dwoo_Template_File( template("show_node.tpl") ); $data = new Dwoo_Data(); $data->assign("extra", template("node_extra.tpl")); $up = $hosts_up ? 1 : 0; $class = ($up) ? "even" : "down"; $data->assign("class",$class); $data->assign("name", $hostname); # $metrics is an array of [Metrics][Hostname][NAME|VAL|TYPE|UNITS|SOURCE]. # Find the host's physical location in the cluster. $hostattrs = ($up) ? $hosts_up : $hosts_down; list($rack,$rank,$plane) = findlocation($hostattrs); $location = ($rack<0) ? "Unknown" : "Rack $rack, Rank $rank, Plane $plane."; $data->assign("location",$location); if(isset($hostattrs['ip'])) { $data->assign("ip", $hostattrs['ip']); } else { $data->assign("ip", ""); } # The metrics we need for this node. $mem_total_gb = $metrics['mem_total']['VAL']/1048576; $load_one=$metrics['load_one']['VAL']; $load_five=$metrics['load_five']['VAL']; $load_fifteen=$metrics['load_fifteen']['VAL']; $cpu_user=$metrics['cpu_user']['VAL']; $cpu_system=$metrics['cpu_system']['VAL']; $cpu_idle=$metrics['cpu_idle']['VAL']; $cpu_num=$metrics['cpu_num']['VAL']; # Cannot be zero, since we use it as a divisor. if (!$cpu_num) { $cpu_num=1; } $cpu_speed=round($metrics['cpu_speed']['VAL']/1000, 2); $disk_total=$metrics['disk_total']['VAL']; $disk_free=$metrics['disk_free']['VAL']; $disk_use = $disk_total - $disk_free; $disk_units=$metrics['disk_total']['UNITS']; $part_max_used=$metrics['part_max_used']['VAL']; # Disk metrics are newer (as of 2.5.0), so we check more carefully. $disk = ($disk_total) ? "Using $disk_use of $disk_total $disk_units" : "Unknown"; $part_max = ($part_max_used) ? "$part_max_used% used." : "Unknown"; # Compute time of last heartbeat from node's dendrite. $clustertime=$cluster['LOCALTIME']; $data->assign("clustertime", strftime("%c", $clustertime)); $heartbeat=$hostattrs['REPORTED']; $age = $clustertime - $heartbeat; if ($age > 3600) { $data->assign("age", uptime($age)); } else { $s = ($age > 1) ? "s" : ""; $data->assign("age", "$age second$s"); } # The these hardware units should be more flexible. $s = ($cpu_num>1) ? "s" : ""; $data->assign("s",$s); $data->assign("cpu", sprintf("%s x %.2f GHz", $cpu_num, $cpu_speed)); $data->assign("mem", sprintf("%.2f GB", $mem_total_gb)); $data->assign("disk","$disk"); $data->assign("part_max_used", "$part_max"); $data->assign("load_one",$load_one); $data->assign("load_five",$load_five); $data->assign("load_fifteen",$load_fifteen); $data->assign("cpu_user",$cpu_user); $data->assign("cpu_system",$cpu_system); $data->assign("cpu_idle",$cpu_idle); # Choose a load color from a unix load value. function loadindex($load) { global $cpu_num; # Highest color comes at a load of loadscalar*10. $loadscalar=0.2; $level=intval($load/($loadscalar*$cpu_num))+1; # Trim level to a max of 10. $level = $level > 10 ? "L10" : "L$level"; return $level; } # Choose a load color from a 0-100 percentage. function percentindex($val) { $level = intval($val/10 + 1); $level = $level>10 ? "L10" : "L$level"; return $level; } $data->assign("load1",loadindex($load_one)); $data->assign("load5",loadindex($load_five)); $data->assign("load15",loadindex($load_fifteen)); $data->assign("user",percentindex($cpu_user)); $data->assign("sys",percentindex($cpu_system)); $data->assign("idle",percentindex(100 - $cpu_idle)); # Software metrics $os_name=$metrics['os_name']['VAL']; $os_release=$metrics['os_release']['VAL']; $machine_type=$metrics['machine_type']['VAL']; $boottime=$metrics['boottime']['VAL']; $booted=date("F j, Y, g:i a", $boottime); $uptime=uptime($cluster['LOCALTIME'] - $metrics['boottime']['VAL']); # Turning into MBs. A MB is 1024 bytes. $swap_free=$metrics['swap_free']['VAL']/1024.0; $swap_total=sprintf("%.1f", $metrics['swap_total']['VAL']/1024.0); $swap_used=sprintf("%.1f", $swap_total - $swap_free); $data->assign("OS","$os_name $os_release ($machine_type)"); $data->assign("booted","$booted"); $data->assign("uptime", $up ? $uptime : "[down]"); $data->assign("swap","Using $swap_used of $swap_total MB swap."); # For the back link. $cluster_url=rawurlencode($clustername); $data->assign("physical_view","./?p=$physical&c=$cluster_url"); # For the full host view link. $data->assign("full_host_view","./?c=$cluster_url&h=$hostname&$get_metric_string"); # For the reload link. $data->assign("self","./?c=$cluster_url&h=$hostname&p=$physical"); $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/stacked.php000066400000000000000000000124731231750357400160430ustar00rootroot00000000000000 $cluster_array ) { foreach ( $cluster_array as $index => $cluster ) { // Check cluster name if ( $cluster == $clustername ) { // If host regex is specified make sure it matches if ( isset($_REQUEST["host_regex"] ) ) { if ( preg_match("/" . $_REQUEST["host_regex"] . "/", $host ) ) { $hosts[] = $host; } } else { $hosts[] = $host; } # if ($conf['strip_domainname']) $host_len = strlen(strip_domainname($host)); else $host_len = strlen($host); $max_len = max($host_len, $max_len); } } } // Force all hosts to be in name order sort($hosts); foreach ( $hosts as $index => $host ) { $filename = $conf['rrds'] . "/$clustername/$host/$metricname.rrd"; if (file_exists($filename)) { $command .= " DEF:'a$index'='$filename':'sum':AVERAGE"; $total_cmd .= ",a$index,ADDNAN"; $c++; } else { // Remove host from the list if the metric doesn't exist to // avoid unsightly broken stacked graphs. unset($hosts[$index]); } } $mean_cmd = " CDEF:'mean'=total,$index,/"; $first_color = get_col(0); $min_index = min(array_keys($hosts)); foreach($hosts as $index => $host) { $cx = $i/(1+count($hosts)); $i++; $color = get_col($cx); if ($conf['strip_domainname']) $host = strip_domainname($host); if ( $index != $min_index ) $command .= " STACK:'a$index'#$color:'".str_pad($host, $max_len + 1, ' ', STR_PAD_RIGHT)."'"; else $command .= " AREA:'a$index'#$first_color:'".str_pad($host, $max_len + 1, ' ', STR_PAD_RIGHT)."'"; $c++; } #$command .= " LINE1:'a0'#333"; $c = 1; foreach($hosts as $index => $host) { #if ( $index != 0 ) # $command .= " STACK:'a$index'#000000"; $c++; } $command = sanitize($command); $command .= $total_cmd . $mean_cmd; $command .= " COMMENT:'\\j'"; $command .= " GPRINT:'total':AVERAGE:'Avg Total\: %5.2lf'"; $command .= " GPRINT:'total':LAST:'Current Total\: %5.2lf\\c'"; $command .= " GPRINT:'mean':AVERAGE:'Avg Average\: %5.2lf'"; $command .= " GPRINT:'mean':LAST:'Current Average\: %5.2lf\\c'"; header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header ("Cache-Control: no-cache, must-revalidate"); header ("Pragma: no-cache"); if (isset($_GET['debug'])) { header ("Content-type: text/plain"); echo ($command); } else { header ("Content-type: image/png"); my_passthru($command); } function HSV_TO_RGB ($H, $S, $V){ if($S == 0){ $R = $G = $B = $V * 255; }else{ $var_H = $H * 6; $var_i = floor( $var_H ); $var_1 = $V * ( 1 - $S ); $var_2 = $V * ( 1 - $S * ( $var_H - $var_i ) ); $var_3 = $V * ( 1 - $S * (1 - ( $var_H - $var_i ) ) ); if ($var_i == 0) { $var_R = $V ; $var_G = $var_3 ; $var_B = $var_1 ; } else if ($var_i == 1) { $var_R = $var_2 ; $var_G = $V ; $var_B = $var_1 ; } else if ($var_i == 2) { $var_R = $var_1 ; $var_G = $V ; $var_B = $var_3 ; } else if ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2 ; $var_B = $V ; } else if ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1 ; $var_B = $V ; } else if ($var_i == 5) { $var_R = $V ; $var_G = $var_1 ; $var_B = $var_2 ; } else { return array(255, 255, 255); } $R = $var_R * 255; $G = $var_G * 255; $B = $var_B * 255; } return array($R, $G, $B); } function get_col($value){ list($r,$g,$b) = HSV_TO_RGB($value, 1, 0.9); return sprintf('%02X%02X%02X',$r,$g,$b); } ?> ganglia-web-3.6.1/styles.css000066400000000000000000000264021231750357400157460ustar00rootroot00000000000000/* The Ganglia Web-frontend Cascading Style Sheet. */ body{ font: 75% "Trebuchet MS", sans-serif; margin: 5px;} A:link { color: rgb(32,41,204); text-decoration: none; } A:visited { color: rgb(51,51,204); text-decoration: none; } A:active { color: rgb(204,205,226); text-decoration: none; } h1 { font: bold 18pt/22pt helvetica, sans-serif; color: rgb(12,17,142); margin-bottom: 2mm; } h2 { font: bold 14pt/16pt helvetica; color: rgb(12,17,142); margin-left: 0.1em; margin-right: 0.1em; } h3 { font: bold 12pt/15pt helvetica, sans-serif; text-align: left; margin-left: 3mm; color: rgb(247,142,14); } h4 { font: bold 12pt/14pt helvetica; color: rgb(12,17,142); margin-left: 0.1em; margin-right: 0.1em; } small { font: 10pt/12pt arial, helvetica, sans-serif; } td.title { font-size: larger; font-weight: normal; background: rgb(238,238,238); text-align: center; } td.metric { background: rgb(217,218,224); padding: 2mm; } td.cluster { background: rgb(238,238,238); /* padding: 1mm; */ } td.grid { vertical-align: top; background: rgb(204,204,255); } td.self { vertical-align: top; text-align: center; background: rgb(255,153,153); } td.even { background: rgb(201,205,234); } td.odd { background: rgb(217,218,224); } td.small { font: 10pt/12pt times, serif; } td.L1 { background: rgb(184,249,119); } td.L2 { background: rgb(213,249,119); } td.L3 { background: rgb(228,249,119); } td.L4 { background: rgb(245,249,119); } td.L5 { background: rgb(249,239,119); } td.L6 { background: rgb(249,219,119); } td.L7 { background: rgb(249,202,119); } td.L8 { background: rgb(249,182,119); } td.L9 { background: rgb(249,152,119); } td.L10 { background: rgb(249,124,119); } td.down { background: rgb(255,153,153); } th { font: bold 12pt/14pt helvetica, sans-serif; color: rgb(238,238,238); background: rgb(80,80,80); border: thin solid rgb(47,47,47); } td em { font: 8pt/10pt helvetica, sans-serif; color: rgb(31,31,112); font-style: normal; } td strong { color: rgb(31,31,112); font-style: normal; } .footer { font: 10pt/12pt helvetica, sans-serif; color: rgb(100,100,100); } #trend_range_menu { font-size: 10px } table { font-size: 1em ! important } .nobr { white-space:nowrap; } .noborder { border:0; } .icon16 { height:16px;width:16px;border:0; } .flotlegend { margin-left: 40px; padding: 10px; width: 85%; clear: both; border: 1px solid #000000; background-opacity: 0.5; background-color: #e5e5ff; } .flotlegendtoplabel label { display: block; text-align: center; size: small; text-weight: bold; } /* Following buttons taken from http://ubuwaits.github.com/css3-buttons */ button.minimal-indent { background-color: #f3f3f3; background-image: -webkit-gradient(linear, left top, left bottom, from(#f3f3f3 0%), to(#dddddd 50%)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #f3f3f3 0%, #dddddd 50%, #d2d2d2 50%, #dfdfdf 100%); background-image: -moz-linear-gradient(top, #f3f3f3 0%, #dddddd 50%, #d2d2d2 50%, #dfdfdf 100%); background-image: -ms-linear-gradient(top, #f3f3f3 0%, #dddddd 50%, #d2d2d2 50%, #dfdfdf 100%); background-image: -o-linear-gradient(top, #f3f3f3 0%, #dddddd 50%, #d2d2d2 50%, #dfdfdf 100%); background-image: linear-gradient(top, #f3f3f3 0%, #dddddd 50%, #d2d2d2 50%, #dfdfdf 100%); border-right: 1px solid #dfdfdf; border-bottom: 1px solid #b4b4b4; border-right: 1px solid #dfdfdf; -webkit-border-radius: 5px; -moz-border-radius: 5px; -ms-border-radius: 5px; -o-border-radius: 5px; border-radius: 5px; -webkit-box-shadow: inset 0 1px 0 0 white, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef; -moz-box-shadow: inset 0 1px 0 0 white, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef; -ms-box-shadow: inset 0 1px 0 0 white, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef; -o-box-shadow: inset 0 1px 0 0 white, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef; box-shadow: inset 0 1px 0 0 white, 0 1px 0 0 #d5d5d5, 0 -1px 2px 1px #efefef; color: #666; font: bold 16px "helvetica neue", helvetica, arial, sans-serif; margin: 0; padding: 7px 0; text-shadow: 0 1px 1px #fff; width: 150px; } button.minimal-indent:hover { background-color: #e5e5e5; background-image: -webkit-gradient(linear, left top, left bottom, from(#e5e5e5 0%), to(#d1d1d1 50%)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%); background-image: -moz-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%); background-image: -ms-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%); background-image: -o-linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%); background-image: linear-gradient(top, #e5e5e5 0%, #d1d1d1 50%, #c4c4c4 50%, #b8b8b8 100%); -webkit-box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3; -moz-box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3; -ms-box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3; -o-box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3; box-shadow: inset 0 1px 0 0 #f2f2f2, 0 1px 0 0 #c9c9c9, 0 -1px 2px 1px #e3e3e3; } button.minimal-indent:active { -webkit-box-shadow: inset 0 0 30px 0 #999999, 0 1px 0 0 white; -moz-box-shadow: inset 0 0 30px 0 #999999, 0 1px 0 0 white; -ms-box-shadow: inset 0 0 30px 0 #999999, 0 1px 0 0 white; -o-box-shadow: inset 0 0 30px 0 #999999, 0 1px 0 0 white; box-shadow: inset 0 0 30px 0 #999999, 0 1px 0 0 white; } button.cupid-green { background-color: #7fbf4d; background-image: -webkit-gradient(linear, left top, left bottom, from(#7fbf4d), to(#63a62f)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #7fbf4d, #63a62f); background-image: -moz-linear-gradient(top, #7fbf4d, #63a62f); background-image: -ms-linear-gradient(top, #7fbf4d, #63a62f); background-image: -o-linear-gradient(top, #7fbf4d, #63a62f); background-image: linear-gradient(top, #7fbf4d, #63a62f); border: 1px solid #63a62f; border-bottom: 1px solid #5b992b; -webkit-border-radius: 3px; -moz-border-radius: 3px; -ms-border-radius: 3px; -o-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: inset 0 1px 0 0 #96ca6d; -moz-box-shadow: inset 0 1px 0 0 #96ca6d; -ms-box-shadow: inset 0 1px 0 0 #96ca6d; -o-box-shadow: inset 0 1px 0 0 #96ca6d; box-shadow: inset 0 1px 0 0 #96ca6d; color: #fff; line-height: 1; text-align: center; text-shadow: 0 -1px 0 #4c9021; font: 8px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; padding: 0px 2px 0px 2px; } button.cupid-green:hover { background-color: #76b347; background-image: -webkit-gradient(linear, left top, left bottom, from(#76b347), to(#5e9e2e)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #76b347, #5e9e2e); background-image: -moz-linear-gradient(top, #76b347, #5e9e2e); background-image: -ms-linear-gradient(top, #76b347, #5e9e2e); background-image: -o-linear-gradient(top, #76b347, #5e9e2e); background-image: linear-gradient(top, #76b347, #5e9e2e); -webkit-box-shadow: inset 0 1px 0 0 #8dbf67; -moz-box-shadow: inset 0 1px 0 0 #8dbf67; -ms-box-shadow: inset 0 1px 0 0 #8dbf67; -o-box-shadow: inset 0 1px 0 0 #8dbf67; box-shadow: inset 0 1px 0 0 #8dbf67; cursor: pointer; } button.cupid-green:active { border: 1px solid #5b992b; border-bottom: 1px solid #538c27; -webkit-box-shadow: inset 0 0 8px 4px #548c29, 0 1px 0 0 #eeeeee; -moz-box-shadow: inset 0 0 8px 4px #548c29, 0 1px 0 0 #eeeeee; -ms-box-shadow: inset 0 0 8px 4px #548c29, 0 1px 0 0 #eeeeee; -o-box-shadow: inset 0 0 8px 4px #548c29, 0 1px 0 0 #eeeeee; box-shadow: inset 0 0 8px 4px #548c29, 0 1px 0 0 #eeeeee; } button.shiny-blue { background-color: #759ae9; background-image: -webkit-gradient(linear, left top, left bottom, from(#759ae9 0%), to(#376fe0 50%)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #759ae9 0%, #376fe0 50%, #1a5ad9 50%, #2463de 100%); background-image: -moz-linear-gradient(top, #759ae9 0%, #376fe0 50%, #1a5ad9 50%, #2463de 100%); background-image: -ms-linear-gradient(top, #759ae9 0%, #376fe0 50%, #1a5ad9 50%, #2463de 100%); background-image: -o-linear-gradient(top, #759ae9 0%, #376fe0 50%, #1a5ad9 50%, #2463de 100%); background-image: linear-gradient(top, #759ae9 0%, #376fe0 50%, #1a5ad9 50%, #2463de 100%); border-top: 1px solid #1f58cc; border-right: 1px solid #1b4db3; border-bottom: 1px solid #174299; border-left: 1px solid #1b4db3; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 0 2px 0 rgba(57, 140, 255, 0.8); -moz-box-shadow: inset 0 0 2px 0 rgba(57, 140, 255, 0.8); -ms-box-shadow: inset 0 0 2px 0 rgba(57, 140, 255, 0.8); -o-box-shadow: inset 0 0 2px 0 rgba(57, 140, 255, 0.8); box-shadow: inset 0 0 2px 0 rgba(57, 140, 255, 0.8); color: #fff; font: 8px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; padding: 0px 2px 0px 2px; text-shadow: 0 -1px 1px #1a5ad9; } button.shiny-blue:hover { background-color: #5d89e8; background-image: -webkit-gradient(linear, left top, left bottom, from(#5d89e8 0%), to(#2261e0 50%)); /* Saf4+, Chrome */ background-image: -webkit-linear-gradient(top, #5d89e8 0%, #2261e0 50%, #044bd9 50%, #0d53de 100%); background-image: -moz-linear-gradient(top, #5d89e8 0%, #2261e0 50%, #044bd9 50%, #0d53de 100%); background-image: -ms-linear-gradient(top, #5d89e8 0%, #2261e0 50%, #044bd9 50%, #0d53de 100%); background-image: -o-linear-gradient(top, #5d89e8 0%, #2261e0 50%, #044bd9 50%, #0d53de 100%); background-image: linear-gradient(top, #5d89e8 0%, #2261e0 50%, #044bd9 50%, #0d53de 100%); cursor: pointer; } button.shiny-blue:active { border-top: 1px solid #1b4db3; border-right: 1px solid #174299; border-bottom: 1px solid #133780; border-left: 1px solid #174299; -webkit-box-shadow: inset 0 0 5px 2px #1a47a0, 0 1px 0 #eeeeee; -moz-box-shadow: inset 0 0 5px 2px #1a47a0, 0 1px 0 #eeeeee; -ms-box-shadow: inset 0 0 5px 2px #1a47a0, 0 1px 0 #eeeeee; -o-box-shadow: inset 0 0 5px 2px #1a47a0, 0 1px 0 #eeeeee; box-shadow: inset 0 0 5px 2px #1a47a0, 0 1px 0 #eeeeee; } /* css for timepicker */ .ui-timepicker-div .ui-widget-header { margin-bottom: 8px; } .ui-timepicker-div dl { text-align: left; } .ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; } .ui-timepicker-div dl dd { margin: 0 10px 10px 65px; } .ui-timepicker-div td { font-size: 90%; } .ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; } .ui-timepicker-rtl{ direction: rtl; } .ui-timepicker-rtl dl { text-align: right; } .ui-timepicker-rtl dl dd { margin: 0 65px 10px 10px; } label[class~="show_event_text"] span.ui-button-text { font-size: 60% !important } label[class~="show_timeshift_text"] span.ui-button-text { font-size: 60% !important } .ganglia-qtip { width: 250px; } .ganglia-qtip .qtip-content { font-size: 14px; } .img_view { float: left; margin: 0 0 10px 10px; } ganglia-web-3.6.1/tasseo.php000066400000000000000000000114521231750357400157170ustar00rootroot00000000000000 {else} {/if} {/if}
    Show Hosts Scaled: {foreach $showhosts_levels id showhosts implode=""} {/foreach}
    {if isset($columns_size_dropdown) && ($showhosts != 0)}
    Size  {$size_menu}
    Columns  {$cols_menu} (0 = metric + reports)
    {/if}
    {$additional_filter_options}
    {include('cluster_host_metric_graphs.tpl')}
    ganglia-web-3.6.1/templates/default/compare_hosts.tpl000066400000000000000000000037651231750357400227310ustar00rootroot00000000000000
    Enter host regular expression:
    {if $hreg_arg != ''} {if $number_of_metrics == 0}

    No matching metrics

    {else} {$i = 0} {foreach $host_metrics metric} {$graphId = cat($GRAPH_BASE_ID "dg_" $i)} {$showEventsId = cat($SHOW_EVENTS_BASE_ID "dg_" $i)}
    {$metric}
    {math "$i + 1" assign=i} {/foreach} {/if} {/if}
    ganglia-web-3.6.1/templates/default/decompose_graph.tpl000066400000000000000000000032101231750357400232030ustar00rootroot00000000000000
    {if $number_of_items == 0 }

    No graphs decomposed

    {else} {foreach $items item}

    {/foreach} {/if}
    ganglia-web-3.6.1/templates/default/footer.tpl000066400000000000000000000101061231750357400213440ustar00rootroot00000000000000
    Available Metric actions.
    Invoke automatic rotation system. Automatic rotation rotates all of the graphs/metrics specified in a view waiting 30 seconds in between each. This will run as long as you have this page open.

    Please select the view you want to rotate.

    Loading view, please wait...
    Live dashboard provides you with an overview of all view metrics in a compact format. Data updates every 15 seconds. Only those elements that contain a metric or graph report are supported. Aggregate graphs will not be included.

    You can get more graphs per page by using your browser zoom functionality.

    Please select the view you want to view

    Loading view, please wait...
    ganglia-web-3.6.1/templates/default/grid_tree.tpl000066400000000000000000000014141231750357400220140ustar00rootroot00000000000000
    {$self} Grid Tree
    {if isset($parentgrid)} {/if}
    {$parents}
    {$children}
    {$self}


    Legend:
     This Grid.
     A Remote Grid.
    ganglia-web-3.6.1/templates/default/header-nobanner.tpl000066400000000000000000000012031231750357400230740ustar00rootroot00000000000000 Ganglia Cluster Toolkit:: {page_title}
    {node_menu}

    ganglia-web-3.6.1/templates/default/header.tpl000066400000000000000000000247451231750357400213140ustar00rootroot00000000000000 Ganglia:: {$page_title} {$custom_time_head} {if isset($user_header)} {include(file="user_header.tpl")} {/if} {if $auth_system_enabled}
    {if $username} Currently logged in as: {$username} | Logout {else} You are not currently logged in. | Login {/if}

    {/if}
    {$page_title} at {$date}
    {$range_menu}
    {$custom_time}
    {$additional_buttons}  {$alt_view}
    {if $context != "cluster" && $context != "cluster-summary"} {/if} {if $context == "meta"}
    {$sort_menu}
    {/if} {if $node_menu != ""}
    {$node_menu}  {$additional_filter_options}
    {/if} {if $hide_header} {else} {/if} {if $overlay_events} {else} {/if}
    ganglia-web-3.6.1/templates/default/host_extra.tpl000066400000000000000000000001171231750357400222270ustar00rootroot00000000000000 ganglia-web-3.6.1/templates/default/host_overview.tpl000066400000000000000000000013311231750357400227510ustar00rootroot00000000000000
    {$node_msg} {foreach $s_metrics_data s_metric} {/foreach} {foreach $c_metrics_data c_metric} {/foreach}
    Time and String Metrics
    {$s_metric.value}
     
    Constant Metrics
    {$c_metric.value}

    {if isset($extra)} {include(file="$extra")} {/if}
    ganglia-web-3.6.1/templates/default/host_view.tpl000066400000000000000000000232521231750357400220630ustar00rootroot00000000000000 {if $graph_engine == "flot"} {/if}
    Empty
    {$optional_reports}
    {if $may_edit_cluster}
    {/if}
    {$host} graphs ({$host_metrics_count}) last {$range} sorted {$sort} {if isset($columns_dropdown)} Columns  {$metric_cols_menu} Size  {$size_menu} {/if}
    {foreach $g_metrics_group_data group g_metrics} {$mgId = "mg_"; $mgId .= regex_replace($group, '/[^a-zA-Z0-9_]/', '_')}
    {if $g_metrics.visible}
    {else}
    {/if} {if $g_metrics.visible} {include('metric_group_view.tpl')} {/if}
    {/foreach}
    ganglia-web-3.6.1/templates/default/images/000077500000000000000000000000001231750357400205745ustar00rootroot00000000000000ganglia-web-3.6.1/templates/default/images/cluster_0-24.jpg000066400000000000000000000063261231750357400234300ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222Yv"?  !1AQq"a#26Rrt3BCbcs/!1A"Qaq2Br ?袊! 5i&1Vk ͻ庣pߎ%Gq8ZSy#=+tah*RԥTʔԓZznnܖG?[ Vwq͵:Zdy61f}49]RORGO让tU^L}69UN{q۽jA>'HC'+BԝYl6s2' h%rJFxX$F{TĐH%XIc*BV #*_/;w'T RϼAliԺ]8R4V=:֚xz[>Zei19x WA+"3ڲ΋÷;ec0慎VKu|n)QNGH?[#$ᑜV>!@c1*o:FN;JԆK֓Gsq(*rcJ6_qy}_OT8gȚv~X3÷GRځ= ϙi졖[Km a(Ht-rpôoVwEi (RPRմRÊgOZ~|B8*֏C:7!BRJQtU*[,NLV SEZ_Jmv޹4`m4܄< @\nlވҶ'$$CHnBv-xR$I I)# W;wig1 /rTC$t;q/kMIqO Uk݊2l"üh.bm&t"ϔj9EAhH֬.ͣq`O6I|+ҽgy]I 3gĬJkڎn;ջ٩-0<1gMm~_%.kڟKm6y'3PGK>g2)PYLkzO&ϻi˜Wv8*g62\5%)8HOg;j=<'Z P}8Va[GhMO`J"}2)|8J;Ce[Тۭ/X(t ^h>ŏx{+\h܊Iii8l,MNӜnI9Af Q sET JJ@$i͗OHwY#h.!]/Zm3^#h]E}rdt#LJ[!1 V VtuJbom\%nʦXaێim$`% `+Q1f9̶!ET'(+=^,rx#K2q pBs>ꚼ'),Udž@hTf4WiNK>m\U_d ӝ^u+v֋>~Q}RGTIx#5^p e[N^3zu}, eF'KHRVԜ[uiX>;~4۷Rqsr}՚YAܽAO\oc VEkgS[]rՌ<˃0慧e֒tuK %Q7uvz/]$ Xj,)a]?SfqJC7+wڕ 8um'!I# v+ETIr#K:﫣)| E^qF8Ka묆 ZVvE+H1bGYCL0 ` y~(0e`iUj_;P7&4[RRմ*8MKQ&3א`PEZ r}℥x:JֶPujTVWy)s_X iTpω=Z-=KU?H{\T4˜7>ް΍O)PYLkzO^6f).jm%^‚9[R撚GPZ?jA''Kp}3Z1˳*"jsNJ]CSĢBrpT Ja )Ed$um  op?⡧{KvswȊiN1^cuL9tn Bʲ $Ԏu*ť`!]+%nQ$g6iBFR Mʡ@:%5@D((BZdzݧ*oIf`pDtXz_hC  Ir8ZXn0lC k~PZl3>Cf RkPҭg6l[##3hZua-?ɗ$uIm>Q}iZbFmooeLqdJAO:[iJKJޝ$؃jToi+B 8T":sv1JWx|נho۳g0l͑1 vڥiDhXH!`ՁZΝӖ/jMC{qgsZJ>4ڊF:+BڕLbQEuETganglia-web-3.6.1/templates/default/images/cluster_50-74.jpg000066400000000000000000000064231231750357400235200ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222Yv"<!1AQ"2aq3Bbr$R#4Cs.!1AQaq2" ?袊Ȥ-.x(uq9"Hݜ t;o9]l1XysyMmrJAB.νN&K0zK#uI4v;z?4R>#ĩ1de/s $ ,1?f\WImbdL3Il]mi˻7%/7F{A_JUu2Ucc,GXZw3a[lm@W_1}'\bBWhz ȋdb_ݧ9ka;;HЫ s"y׵hWc\*w>^_YO.BT'"n%̥y M#(;m7wM*++ Iwr\{Hn=̺Bùb=)نcU*m?#u&H$1lge5i3(Rjsr7$3G&&Cލ#U"㫓 akBlWen#=Qة:ޯckfnEܝN'q|`EsnT!t?j %Ye3rerw3:DĊXǿr}馕>x?['}S]`\]{ʸߔ)!XY9XX/r}n~?Vf3YZ(οjz+tjGbMC n^,Gj~]5x$YmXވ_#_.c㷐,hUKoDoZZI`VZhhvgL9n}86g4hZ+"; &=+bt}*H1fN*\.ޔUĒH@f.N@İcE :߭24ʿJ w^ꆞnd!Lr5*ܴHu"9m;PGwmpk=RL5Sod@L=? xwu|&; V[~u|(]sxחs:Y1F%GF(2èlQI!dƧSּ?ׯOϯEnbQ1^>btIDPkجzwښis8dh3 QwpJlSگ,-rYEqo KVb;jخ vnKe݋%1>QV`uI8]sO쪤DmzsэC 3cbydv2)Tgo>.~8x m:E5+uxrȽ- o&Ɲ1 Nu{!p-( <+s2H-ۀUml`( RJRv F \_+%vO+Cg[R@ jΒi̋?zC0k7.<,.=",:ޱV>6K:Eo-"I$HV> FʧNiN$9Y$ו$N5K>woov 'v[+yZƼI=ԝO'F Ҝ](!\TbErzlwcc%}VZ8+,`ܨV.AQwo4*qȸM-̟T1۹D"f4s-̷<0^l6B﷠(ˆ^,o/^=qsyq7nghT6?<}汲x}D&?uJ3ռ,1hd Wǣlk'aZ? R)2*(p%~ڐ$]3Iv=Th5p.au(u݂1ԩKxܫ&;)q>A5$hGvD6j{'6y fbqZ_InlTbGw~n:ɏ s7yviOHı㷂5""P4U[dM `U '("QErR~]Ub MS!6Jpp6;NT^ƽ igy1rL&O25ͻHe>83`?JX$}^<YnX ϥHQ;1Iw_cc0)NCj{hË,6lr\4 u {{YGKB{eםL@~nX7QK1<1+t/9g'q. Q]AȆ}?pY̒+u4;OJ)>$X(EUQE˗ganglia-web-3.6.1/templates/default/images/cluster_75-100.jpg000066400000000000000000000066371231750357400236040ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222Yv"@ !1AQq"2a5BRs#b3CSr/!1AaQq"2 ?袊"V~V-;TtQ B{;K= RɲZN!7e@ O8ZP[ V庵k='nb7w_m!<"j뻊9RyL> e'θ !bihFaP皼9ڌ䝊X@ƐޔHْ}80H< lS]^ڭVB&.{}NKh3ʕ)WW[p-RȈCyQ!#$HT)̈́\9w@lix,8MD7Y mY I p9EA3NaEւ ]mkCޒǚKtڌyk)!hhP\5FPrqr 9*ʱ ioR[M3)C -Ãk8=-=T:RQ%M ]cs"8mx )APsGP kLt趚+wڕ 8um'!I# +jRtQEE&#Lv,S:*D\g>v !n}YKaҳ{*hBhCm H Or4jwKDƢpO_:U\50iL(-JeM- m?ij#:Յw_bJN< =UqX8pb?rwKO;)HI!X@GBTUlWpžrǧTO(-8'Ujk\ ]+w($vKTK{n|h[Klndvhl9?S;C!MR goV8?x:lmE1K-+2:P;pu({Vm+PR8f Cy.q~š.!HZR(dz<(Ӻ]UB̓b@^ܥ'*GuHY! %)#f٤yՌ$BhC~1Ra= }( e9)~ھɩéL! o4Y })Vվwz*I-''!_iRs8qsɛ\ـ]-ZQ#\EWV^I|آ B̬{[Vn2v!e'>-|j֨4Vߙݵ]ޭ8'l4 x)v?1H^5mOIuG=y'Sh|Vu:^/ĕ%M6qVM<қ$dj_fv9ҖRY%GZeaVmێKh)IVpIդG/T39zʵ74MC۶ kuK{L֌vʦ58ڜUi58\J$'. ,s vR,8(ֿpZK-ВA{&X%˟2;<Ѷ VN|l:*r7e ';]s5 M:.3JMl F߾Jgt4 Zcqn=s.?I1E ƹt?U?~cI`CR=F+S/㞥u%rCrpi`>d\wgJ#'w{c㺊s#q -Tٜݬ.q̌Z˨V< V?qZZnհYq!,]i٧;rJ2A잜T#<̬e6|qP[3SKC~b5ܱ%$2F ?Wq^ilݚペy/wu\~JRC[8:+-yvH$2kͱ.R/*qmȑ0a!nNgISOH~ 6ݛNy av帲eb; {p=O|AG;#lrpϖF}TRu7Yo.Hk$UR`u9I9%``֛*˦e\On` vf\< 5gY ;s\ A9S 4͛0hBGƲ6| 3݃qZoX!\1o0Q6.Ix Q&8Ǣ=č->ۥ?Gq[\VOU:\~cR,QƝQ%J;GQsBo%ױeX!5ya:O$%:(QP[F"CO*Y<(yd #dIKvW[.XB--'9) uO.$cE66Δӱ퍼dH_9ZϙQ^BYY^^ I=2ݓV~Mt|JRvSЀv*c#Jz;3`):4BRnV}Ł ծ塩6n_oq\ iG9RU䔨K[.'BrpAm/=sPfy.(f(mv*<7$oAv s^@'r+)(IR2Tj{1 e1 HmX,0z6rc98U#7.3X^U)o9hLx,Ř1RU`=tMvBJ{ΟZQ*݆6iBFRtG6 {rJ(("Vvuw;%7u' bhPX EVE%ѹچGlG8جݦ/ףȲX3O %`ANw 7_l,&[o`1h8IMtSX)b?gp?rLuIzaRVy('32,c 9Z<{qm$dw&h(C#;}lNэ[5^ZQ$9fs+izN۴ͰA4㮸<TVSjHi& ,ϔ((QEEganglia-web-3.6.1/templates/default/images/cluster_overloaded.jpg000066400000000000000000000065501231750357400251710ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222Yv"A !1AQq"a26BRt#3r$CSs0!1AqQa"3BR ?袊ňZlUe2˰NuGTSS;Ioi#Qm%71 =+wlj (AZVqdJQyCO݌)j\8/eJ]f\TN>2H<8dg(zNK;4? 8*3Rq.2ZNBFAW袊+"n]ik|"+kOx&RI4G8!yg9"qC,@P>43N-֗UTU̼RZrZSjK`)X?YdTZizCm@|Mi Z}℥#x:8$y-NU6A=oE^J*# y7H)Div:Ȇ6 Yۻj4 8[ĀӰ'/C5++u֋5D&Im g$D JR5D\.kZFws":vҥXq QY9}i,[BH9Gk8yᏲX!Ќ:>׼@*Ӳ)&=PJӱ؜i;Y;~4TN{ws+Ϯ, ݮ֐;< V>_Tږz4{+qsin=s8<˅"o4Zq6)Z>DsQW"VjN#ϴݾk[_Uf$Z>*+nqeCs H->$4q>4W 4ҝ9{ M<Ki%(R29vCZOPL\Rs). 6۽yc/X@^Xa(2qw,[5 cb<]9>8-]mV:oIQ kQ, iSRV 5ON[ (%-$ &MG2V|OTp&kK3!a{O]ޠH qc> uKMǵ4q$|Qʖ|Sfg*AdLv[KrKqqow%-^%);s)ΊF !s½/&Z%BrCqEV?%c銅ZhzYIbKvMSd(pǐ-$)*Bn6HPA"Uť:,]ˏQ^QE*,[i *SgIc$nII>vH\:F5; UQZ]@$ԓ(kUF1 ''IfGijSDdw)Cڦowy&S-ҨmG@0y% 4鄸mpB\uZ'8vj4v[e!  H|+ҜyW<(+j4QEH׳{ʼn`Ԝ<K2;8qÈ+D0WL{tk7yτ*}q|*g6if:CmCujQ&^NSߪ<~#>UՇFRTդ9zc5jNm:wP\A?.d:35mENjjQEvEQXganglia-web-3.6.1/templates/default/images/cluster_private.jpg000066400000000000000000000056541231750357400245230ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222Yv"@ !1AQq"2a#4BRs3b$Scr(1!A"Qar ?袊}S$źO]Q Bl\#D ԏ(ZUhZE 핺Gr9-oc-JQq9[ԵTniGnSޮw4^SǸ4QPRg|8RW74(L7j'ˣj?VYy)s_X iTprz |?֣dY#r\IRTgeuhٙt&Afj2ڗٯG)>v5Hi1֗ZDD!jI VﯙێKh)IVpIդG/T39y];WOI6_vݷdRQfcחfU1EO-AǏ sNJN^!Q!9w8Ic|T :a )Ed$u Ie@8Zsu7|SY;sH:TwycVO<+[aSFBUF$G:OiIq"3W,U]'mDԹ_\[]QBu5ܱ%$2F ?ڜsy߃y/wu\UѧԔg] ×3%h/6IJ =koŒi`2۱%'¨>ii umeeIil=o~ոkqcK3eW[UNX$Tbږ I)+9Zj*"kwmV7OB#-{ Ƿ(TVE[UO n wZ|2X꓃h<>N6Ų0-JH8IMR9viXIj =%mde=tq9ד\ɫQ!*'KJV5o;"/Ixn>p=eIu~+3YA-;\#o3]un-ٵjoȬS_Ҝ;1e*JpGDcƠB`YZ]w\cdN-:t=㹼~T~o 2'1ʛX#;#׻n׃]/jIfg)bO~f%1R1U598EI!4+5෫`]5 Z4oDE 8I%;_GH#h[[.PLJFvQK]"n?9}STҲKVVMXaKf"O@TErjʉWQXscݫ#\6zW@J^Xܖi=eFRD 8u6Ev'b%DEך`Aj I]Cf{˛ݫuGv}Z1](YSծ5$lV㳀@٣^>r+ex6*mcDR%,,o|Z(.4$f=qE`~K5O (,2!h^񃸓5nP5tOtԲI߹`=OMFjƽuھ*vjGy^[SŚU<#(|.5=kиݤYR5Lz ;5iS .tS[V?*TOxo-+*(K _)y@~K5O q (v!k|)k+gd _#΀ 9OQ ,5S8bis䐆tzkv(u C>9}L1%,$.k _Zl\τE;pL.x- .6(:/kV.AD19}HT"ĦPraaOg3SdF4Y;_;Ha9ƪCp_|<1u}{KYO]ME,̖)ױڇ ycׂdYGq!`5{Ј ;JcD]/ J<CеhsH-;Azв'G#Ak\ݽk*vhCM_u<+.t4͓tz$3$kݥ:[o{U5;⨍ 4p/ ZBه&]]+YXlRLgǨ:]~FoR"˼õwN]~FoR"˼õwN]~FoR"˼õwN]~FoR"˼õwN]~FoR"˼õwN]~FoR"˼õwN]~FoR"˼õwN]~FoR"d;it x{c|Ѝ@q#] D@ganglia-web-3.6.1/templates/default/images/grid_25-49.jpg000066400000000000000000000063571231750357400227760ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222b"D !1A7QVu2aq"3B#$CRbcrt'!1AQ"2qb ?Bٙn]ylq&ԥI%9$17̫S_kX}+&'.eZ^X.(,gmi*(Rl5mpxLTӅLBw9 MwdjjݳKWK.-eZuE20qͪ.i'=WzM@ʬlk>g4Ml&i$%ySQΜ#Z[ &5eТM[ʵ;1EUي/5:ۻ橕i7g$$kɨPwwdوZ\@RNA[` Mк xd'2NQ~,9jvb ybv>u 2]XChIRc!yjvf yceguE?5\RCR8Jčpt dcz rR@mr4?DDtΛ%v6䴍i@זʵf ybv'|'xk$1.L@Y)mPYJF6<SZML&AC<;H*3ﳃ.MeR@~6ih8C)B0GDT.uUf!XB#>nѸ)6jmr :{5\O4i  RHrI? ""HjW0liˡ# jbb/Bָau¸)X)߄!?[ffXV?GuI聱?WLO@@"=uY?!##5iNN%[8~a)W&fKl J@LrS)LlLL07!a1.)ƙˀʴW@;#W´[tROYf&*y+h %(oHG8i"$LBA]6|O&72"is)i%KƤ?<'K+{{ɯ=y\csS-%\JС#݅gT6TTU))JXda9 q5 njVr^zYWN$)+BbV)rt$h# '#{B??[=c<~,+o|$@}+&'!Ϥ&+T~%IϴR8 _B,h="YJȗ 0h͔)]@_V蒇7!(B#dTާ].RByB*$(?8Z% J8uӱWT􂩦xvЄں^Cwjq{@}}49xvЄں^Cwjq{@}}49xvЄں^Cwjq{@}}49xvЄں^Cwjq{@u+YLRRa+ KsSKu!X# (30 ganglia-web-3.6.1/templates/default/images/grid_50-74.jpg000066400000000000000000000065611231750357400227670ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222b";!17AVu"Q#2aq3Bt'!1AQ"q ?7Hi:vcڔI$$S} ҝزMcC./ ;e^=3e5\$엜K1Zg2ppU3閮\e(mI;֎_V.21lkX^MC,.݊`Bp{2Bq@9l+)aZZ 2oz~0w#Yzx4eNiNYצ ҝزMC5wMt(EyܩM(nZZvI'$dVt VC0k Vŗ ziЭ)݋/xu0ua %JRO^iNצ4^<͔צRIDے,@pw8ԡ$Κ )n%;!?G 벶؊񧅼o?E[k$zLs6Q^t+JZf5权</lvĹm#g)@߳|c$JkR4jږ鬂pPg%ƳxG4nxoφm,iNYצ ҝزMI[n ܡ!!c ۫R3,m E 깶92QOtq?AJNҀZ7q|֠*iS̆%:\$$8vZ7q|׍[v!x.MqZT?I d% HvVؐRdΜC ()o<a99+-],I/F + gh,-?JȥI ȑRn[Kr붩h>NˉPTw) 2IWf*T_g/߽09p%'?\Oh#Zo^c*NTUbxjBm ƥ l!%wXAGc) 'R@m(Rt)RID6W pAvW;UJxEZM%JW"l s4XoF?"G;;J{GuyoViVgB_ &gbr[2 ȃQZ7q|*nܟjEڦ{ ל(g $$IT͊wK%C TY]$ӏ-.1ItX 0yjqy^$XY̴R;ΦÐqLD!+qI8$o *e[ʘ0+IA~d;P lMk^k4(AC/\9t((d gMߵNnzPRQ”#$|O<5.} yoK $r 9;I<ɮ3qXKMC eE93,2a IU}RaQuQ$ YEP);N'7۲[Wٹ؛vxV8{RrP9vElr~`S|To8 d gfm9lfm^]dŎԕ8Ohy6Kyqbejs}dtY.(%#$R5v4" ipX{g~u^h72$Ga r JI|c'*Ky6WՎƘV!Z)͔J$ϖq8v7ikCQ{@e7 sSWT+L yqh-*ߔ)[fxt-Rjzt?WoIXP'ҀwY?J% ZŶBvdvV=?!Q\)I*[j X$F958yrkYrΡq!`) .pc W*Nۿ u$e:/J%JBXm)ⴌ8F6n%%l8C ʽR (;Mado\fm~%$rNy mִetCFJUڒ\eĭ  I"ϣɓu"$,RRgc+BrRC`>"ɫYFnDWN$)+Bh5E p} M<3A#eX Pa^S RNҞ^[U)P C./RjzRWqOb#}OQKgJڠyEm/o2Sp`Ð|-)$ec=c;dMrMWQIR(U-r\}ȵbĶ>쬐iRV]mV!IRֵnI'icQ=:2R 'sEd]Q7c֯H؝UNjH߲Hn3Oj'iؚpҮEcl+ƃrfHj;,!)75XڣG[RKCPȭieRa[qeAH<×ʵ>r?N6ңlQA"wt[ i 󯾛ppP MX*T!7JR<(?[=~S?>5TV>Im纔#` mӽa޻^RzýwϨNzQ{JP>;}EU)@:w;|ުtXwTް]/zӽa޻^RzýwϨNzQ{JP>;}EU)@:w;|ުtXwTrԷtǺ^SJܩKu!X# (3Rganglia-web-3.6.1/templates/default/images/grid_75-100.jpg000066400000000000000000000065761231750357400230520ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222b"A!17AVuQa"#2Rq3B$brt.1!AQa2qb ?Bٙn]ylq&ԥI%9$Uي/5,~E̫S_kec{f?k5(3>k'5$LV󲈫(XfB]I:u} 5'R>twʵǜ˴6kꔴ(+(Ɣ8c|zFs0p.Nu0nLis=t9Ӫ!?iZ֖I໶)$yjvb ya̫S_k虣ԙԟ[$g$ jW^)'R6*bvTF%*7;p9jvb ya̫S_kaBJ;.jVfh ז<]vhԪnܤ4DIDjO5´ dtdIp;F"Ԧ]!-ZY/sО{mҺ=E`.4Q^Xs*;[4_k*VRE BN!\i'XZ9kkuj6 Z#rp?h@gt7if N޽tGۥdzUي/52NQ~A,ISj ԤۘaAHX"6@TbR/䬲QO$) |"C;z;"~Zl!S RYa+8Iqd%9d?GuI+  O 𴩠e8qdnswLCqلMLOJ34 49G&IWd[#lcKFBKל0oAc ))P؂0A"BrLЪم:a8en,M,nj2I]X^9m-MR%G*(6Fb.l324'=;m_ yGuyPYJF8=B*Vfnf`Hi嶀ۋ"@6eɻ]~e!$HY4H##(BIS,7d7u&I!3F!_60iɾș-egWVꖕ7G<%#'MFF-:UE&Z9JbnZܨRXZJ) GkJUѸM4pw?2#?[=c<~^+o|$Ĭ[2 "~EQTKfi8@<eyFJ՜sIf,ٹFge\}ۉ)RT2=QYU($Ӝɟgm^C}uԘ],$l%~SHG+T}fSЗ)JDx5^r^)$JqΧ}hU <sV_r.rmم8 HHJ c=C |[vf[jU $%9$$v|f,:R*5*` @?z]2ʰH#Fm1 ǒt>  궪MN9%]T*H^pN:铛"< 39A棲G0iU4s7[ A'rZr#!SNP1¬iX1V$nTR~*s-Cqi}gxz~~|=|nq{tѩS1J=14NH`\94% !8JTx\Nqk:عz -˴R198ɏxzn~$#@?>qyhIܷi/7) a)QdI3b"HjW[6hБ511衎`y*G:rU??[=c<~ʰ~cm\$xN"=|VO R}F2IĻC+qgA J34[e-JRY1˿M"Cm`:.OVb]M<%<. p20JcN3ʄaZR!xPMp-$,nqq:9H2uuI d4ʴJw QQDhxq{C=u|j[yHBEOtyOV'aX)߄ ±?WLOD rbz f[.)  hA[USjLrI(M1Nu8eI д:\miZe ) T0AVh:ԫbYq8d :jR4;+*O~XR׺#!sTk4io]gT4$fU=0lķTDiu0] †Dj&jqƃ.8:464 Q4@]uvZ検!FAc$iּ?9׳šH.{=i[,#E L16 s\BB*;z;ztyOV'a_Z.V//rYa6s(Hb>xvЄ9xvں^BDں^CwjQ{AwjQ{}}E4!N}}E49xvЄ9xvں^BDں^CwjQ{AwjQ{}}E4!Zu+YLRRa+ KsSKu!X# (30 ganglia-web-3.6.1/templates/default/images/grid_overloaded.jpg000066400000000000000000000062351231750357400244350ustar00rootroot00000000000000JFIF``Created with The GIMPC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222b"A !1A7QVau"2q#3BRbrt,!1A"Qa2ѱ ?Bٙn]ylq&ԥI%9$17̫S_kX~+&'.eZ^.(Le[W'QClr;弓uZjb򞫸'VNKD$葯wƛo^K n{e,Ph צ8]9E^n.ZdjYcgX<11zp:Rlgi퟽+I4LKԽyjvb za̫S_k}MJO5J>t.*"5~;mt-. )' ĈIN_Oqf[\ k2O~bqVU~.$Ճk=)dWSAI I f:9s}P+Iol!u$>"HjW0liˡ# jbb,#Rgl;;{tyOVa5rRbz l~ 8P'rau P:J<'g'_C Ŝv~a)W&fKl J@Lis>(MrmԦ0h Tӯ ƭ{\s SpYk+mMM@e]iR!ݪ.1t;~ȯsk>]oH?S) ߵ1E}IZa(-D2^S (`?uGx-k5;/}c>eZKC!I9G; lvYT0Y(mK 8̎sJKK71*ią%hVB NQj4f T7ЏFB<~SwGovcm\o|!">Yh^e9JQRR~+EJs)YH1eS}ҥit(c E/46Zl 9VGV*M'w%gGxӄ)R[N4w^'nAs3v._LTVl?,B!NSU)@>z]R"^^嬲ImJP`4wں^BwjQ{}}ET!9xvPں^BwjQ{}}ET!9xvPں^BwjQ{}}ET!9xvPں^BwjQ{}}ET!xW-z.zn▬fMXG'R0@T~Y  yVev=(nPm)M>[YmY䡴WSFvdR 'IJ?yDbFvC%ZIZ֣m4#p9H)q܎"N_6Uה ?!1wM⹴$~Y9ׅvieցľ+Ub KR%V 5'ÿWQIw95VQlEs|MMH˝iE%CCR()霙.қUsJVwϾd@ݞpv+4缀uYuîKuե %JZA m$TJκJ[58ʎCj1"a,-  _ۨMswhww4\SP7+Y4<NO67! aWZmyȲ, QNc4/+br!9+W;vߕ\4T wưBK,iePGW¥zJA[y|*W߫([uR|;?n( 'aes&5򥵹x%MI)r]9"ChެJ(R4 tRH8SiM8-HgG`u?8 5ùEs.qiZB3a;xwki?I? ٭F`-rul$HliOL}-.{qe=F C^v*_p+EGxqcU0Sn\S٘<{{!ųlӁnC*㦥$ Hd((㾯1/JX;TIU('?kĶÒ)ZE)IEidHzGcͱ{Q@tch;6=E;vm{ݣv0EHaٶ?!vól~CP#f:Gcͱ{Q@tch;6=E;vm{ݣv0EHaٶ?!vól~CP#r6iֹ2X)] ZJIFy(ganglia-web-3.6.1/templates/default/images/logo.jpg000066400000000000000000000324551231750357400222470ustar00rootroot00000000000000JFIFCreated with The GIMPC v"  2! "1#2A $%R&3Ba?l1qJPث2=AO!uTCoQQZş(|nxk(w8&2 :Ɍ2,K<k j_uH=p3ou"O}=Om5ջ'1qaLW0`wY.̂HS!UTOqDUB@pG(B) r++rێ6kBNPx9T>DuV(w%UR.?8$슪\/h2"TR@Q.B" آ!Da*v3yT0Ncc4l PNq1P[ЊHnA_Aj,+3_Qb#uf!mU[$QXqdND@"N"K2mfcR>lP(* 4yHQ{&+ذ )rz.r"`8F %EAĶI{"?C8DE'eǬj6G%\sٍ!}\SyB.e)T#S7{H.JDQ qEA` { 6uUdOKNK "Sݻ ]ŷN涒%2k|f5'2Xm7@ò޵f [~^t[ۣ~\jW5#%af+*qWm LF_g(z=ܳƙ}i>Nz(T$%B!TsP5~C6}F#r]8D: /rt^¡ea w߆N{J=8DCD{J־}Q9l:VytCTUDQ!E>EF{٭LJdkҭj+ /=*ȢU\uUFZ"u> GV,= 8Xd|D#d Fu }՟|~D[Deⷳj3ھ3hm1v6I.ڦ >hc;ߏyW?oDbHPdmpsr49A7>K1&5sr ꜋(įrk`bSK~C~U|O8tU0vb'?*/enC[fVEkMwO?$^&WIy%~>Gco*t8uٕL%Èv֘e9*iu ]4,#;)y9$.5k)y=f4čcy\ʜrL̊ *%e8f̙Mtݑy)7%G5;Ft~+7Ӎ20yD#P7xb"-trLefӕé!?o3>B@=%Bѽ붖5:46\TasD,>ȬcJe6[KKޞKkgg "sk姻1Ү*:|z |\\Ճ0뜘ɗ\3F= {A9^}+ϥܱt?+k\3|Zin(IdQ|>3%wD\;3\l`7VaѨ=5Ucr q*WnPϐ6n2Y{ }]qy7or9xvY.35ힹp܏-Le a'.4juu{Ҭ\1 Yuߍ 1*?i 9H-c^?] =Œm hjrH=XzrH@ԕ%Af&]qTcY%fțvV+@3E*“d.,G;È)"sŎ-)VFG"B<-f['EuhDbl k9s:ؓ꼤* q *tܒ'ʺfFiӄqב }쨪h(IFD>H;}pTNx㏵P_KƔ}'eջ._mYO'vB{$lt"mxUNnmMFwvo";dBIٻmmg5%u@%@JF q\ȭMOk2T ,8ӑ%Ez;?8ͪvm^ lSL{y 2}fؙ=6KJԹCɛg<қ/8&}F/>s;!Ɖ]Ʒ^MXЮ ʦ-u9S/fqV\CLB4f|4mjmc2,.~52}mIt锯ͪvvuete_ƶs_ c(ظ7P;+;}]}.WM7(ET+e+הyl2{* [<|~>fY忐ݷ`8]wgV*nGMϵWRK5M>ՃT흼ZG䄻-iE6ǒ>knc ;'3wDi)WeP,ȯ ?#fɉU"GZ:{1Ι_'}YZ>ˮu-ovB[lc fUVǒ#v2SWZ‡Loߩ Qk`]hbX8TIΫ@ʏ @U=0\[l}SqK8[fdSnZWĵn}YR i%_mgYGYGG1"G".6c6\j) 2Նyɗ2Hˋ%!ŢetcY~[t|s|t6]ԫ{$Ǘ1$Gm]nn o۔rJ!{QiprB s5EQDZQodGf-s8("%E1H^U\rDd FI;"*)**")"uT4E><(h*J=_U8EUɎ.8HhJ/! *"s"*s'ϦF׷'RG'q(]\DQŌ)8o 6wWj)ss FWUbf-(ب+Nd9TjIܢ(*U3FU={Ɛ#F>{*`R.dWR/mQSv{N:gumLӺ"{hhH<RVayo<œ NDP%!u@N8JO`wŪ^lhσ9账C7[h.$"+[_-2T-BBTPDϥ Ǭi6C`M8*Y:E?T{z;WRm A| EGd$%h4@B$&U4UXy l[( X[&;s}=tpP{',v iM9e*|TbH2?di&@b(Sn3uG/#`,F#29D>uuS QP˔i} :fI?U.ʄiߪ/=m;'($/7PlfkO`€ADqS^^˱ǘlJQliiD_ %З E Lxy_EDD/@ /"h`/CMU ȢHd_`J(S" D<Ӊ.:=@WUDE~$xIW>-=d)3l$Xdy/LدgTH@Pd =^Uϱ!EEm8 5nè];^d`-T/(vlonBOb;fnr덣rAꪨFM** e'd }D03%To'DySqDю1W.<u(9*d{mK4+-"fNO^m]՚C>V1Iy樞r$ ICcal_2јrJ$@Ta/m$qɈF4$֚-e%^ʺ^;&,b sQ`pzɨv1f26j&e6?nS2%*LsDBCW\Q %D.S6?)WMer%-]=UEANޚ TfYt(LOn52J.y|X)KYDìQQ&P  bH"T iN;!iUx_V*iOXAy[FaXm̺KTC_ 6(ȕͿ餏^gnf!yue} " p~͡"j[EB<[L'|NʼXJ(rUSIjdJfX 2 Pm?@52<4.ƛ)ʒM9}[>SkŸ/ bqJ! b0H\#_m8dQhυR̋'g V+5`NMڠ,xČ-(ۯ+\+VEq\#SmURFOd$NcCDÒE9a@y]qbs=T %%Ѫ2*6veWGŸAT^#N "!E2xqhi4E˲0>"Dn+bQrRYkceOH#laT:H8"%>fE0Dt8[EE!87Q>P\UWB_i;jNM]ɶ⪵QWnj_dfcXѱ'6{}ýی2FVPm%PE_ c}{YeˈÝ6:RV%6b>s8ҧUQA1o ]My#%U4^]DQQNhUǨѧ׳Ӣ"U8sG{Ji ʼ+7ٶ̱z%]e m}b$ kLAIX9"Llp5&Kq֫!ֽXRhb?Ee7&F8,Uc$a=Kw9GK2JТq NR{#Qܝ~s2G\s5$/UE!-l;1m&A_8! Ksw4co>;044$f-dI띈AC$"D7IL{Lv8菺v':ݜ@]!0|zNPf1;"+0hIq 'פO*%!,<@?jz&rYh$DZ~*k)8Յˬ" ns츎9 gwB5_|\&Ϟ}{l,f]]ƥϚrHJȌMonrbX4K1ekˏ2^ Br[Ҙpc ~HgJ!e7ƍ.eiD}կnWGx@Uy;E@ռkz+*߱[V Y ێmmUi|OV'Y72ݘVrB B  "*'[*M}A4yXVɷ T{"БW\ve?6C#O?$O7\&@W=Y^6מK7)dn GԂTG)'V&qWS$ӞS"9-#Ǐ-ّS&4 Z"Nt#"!*#aFbx[:m-&%kM ('ZTѰ(*]דmU,t"KMZ,DacF#hyElhLUS >?$iW n+M^u@ӰH&J/dTNz%?3: (LtDE$H }eUXd>h#>ٽ*G {Ơ.vR}ĨcƈHe [4܋k_@ U lc0(에QVےBL pS'FL9}*r6DV5X%MERu$wJxTyH>r_ ->ڟ`2nH@e'"Ǥ@q^}qS/9OT /ޱJbIv8@{SMQf4h`nftT"t+8ᗯ\_Xq&Dv`C}FZ"A $'Q%B"Tm=9l6S5hC&İg 119(d92 ",3c(XRu!gQ7 'f4p#E#eVUح4yDzk[4 ~UK ~UG$aEń&Ul:4,V꼗mDb\7SNJ틵T^T{m: @Ec.P."Ff̉U+.s'pQE1We$̞Ų[eXW hkۍa%na"t,!8 Enݥz間އeOgY!]B1GPCA^ȅ?Q緬%;ۈ.;ObQQMDS}308-K4V2q&)H[ 8"pTl#>qUQUQTDG|@P0Q_DnK،̘{+ƒ9ZfSբۓr{Wu5P9/PRA3 ì2 X6c„0ш6%)s$6'-:f\t`[WǏYi5! C*h̋WٛQ6C>!8fb4%V]JiRXNj5q9YJц4')nQ۟.+\ 47cF!0͖M_1gȲƢ{ 2M5$'S't4EUXH)nټ}#+Ȣ>}~{"? kKf>%ͅ&9$ܜ-?*ƄN48k^Lk#!f}Vrpi&u i.iy޾SZcK E\$Ň>!ˮrC :8Qp③!'nfZjz;-6RR ƅ\q=hGIpim9iQ }uMNl\$ƚ I`[6!qN_gtDZ#cLv]~G>$F[ MMh,Hn ZgN.k^jχ$ 0PG}mU w}ܮ#ӵHeCePmDq "*2Iر^V1[vVbTT((OHgM7Q3Չl,%L2'=%FN:rNj]RRUx^-b;Y<8}eycRy]Lm|fe8U0XOqL܎ee~E A糱wO{ X<6w1sXS! ǭQ^6Yn3Cg/el^+Q[L6VnOq˪jÛwI+h\~AcݏSaR\IocC7j̉ w$_*2y~䍦?ȴ1w\K3`OAv64zThMɲܚ{oM}&Mm :vmC#o{ؾ9z061 cGHxm5mK0ۥb-\bon ikOv5}Yd0! ?[#\=aSi@̡%=61a7ϟ6<ַ̻CM%>l*uޫ_pbkRUjvQ19;IۃYqO0 2듡k[[ݮh۽WidTY:e+D4U2u[MeM鬃ک1=m1g)w!Tk6ICpZ*)G{kY㙶Sn/!m ^bx{{3E/!hN yjP-jż#O%n5E}vǑ~M98Uz%^c)ɲ8NK|l7jdyбm}MM7~/36S"l]f٥mIUol3"F"aUuT1gS<,;2aX֒ɢc&0騋XR[bx391f+,#Fz έXNs/|$nvFUsg콥[dl.kcɋenmL~cmA z?]介\Ls}ɧmv}VrYDklViҰ=~v:G!J0j_îv*ݏulv_ .msug5`Y.蚢za/ōz bMrR.٨3!tXk@q&Gr{B"9E7Q@2hTQD7~Aʗ)91vKu_7%2t{*o$1"rK9Ul ESjbD"wN‡S؉U^}~ImI4?FI '(cڄDr%"ZKz8h\ ڃTxE8}$H+Lp=U A%J*"**/-+lg4*zlv=Q嘵+|E ,nkganglia-web-3.6.1/templates/default/images/node_25-49.jpg000066400000000000000000000017451231750357400227720ustar00rootroot00000000000000JFIFBBC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222<"9 !1#"AQUaq&236BCRr'Q!a12qA ?Pݛ+}T6zZֺ~2!OmJ6ؓ7UXgL{0w]JWy9|qj*ka2E!8BsV,rHNGo_RmG;u:ؖ{m{m41Io'=hQ%mL %+1 3t=.6qrNiQ;!:rj,˰@%@\G+V˻ olXqj?wQ1+"ȼ5B9ګNvh{ y\~w3}]/ŏCjh-2cIÞacN;ۥq{T`1T& . nMS|k6 K *@ǩ\7UG$N<|vɇ 2F%nߎ4J<ѴtAi|̥Y1J[LLf)ǩ}oI,qe XNJI>_m\#|Ak'66v+܎<ݦ-g'7TQ%Eizu:ۢHlӦH\HiYWrOZ1r[ K+dStwHgWRj4L ٧X(㆞0te?usC_ganglia-web-3.6.1/templates/default/images/node_50-74.jpg000066400000000000000000000017571231750357400227710ustar00rootroot00000000000000JFIFBBC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222<"= !1"#2AqQU$%&6BRabcst&!aQ"1AB ?_n& ͏2i#Hd2j;M@rβ%,+q! U!@G85$.q֛+)ʕ \:L33+.dbMM✜$+m[Vz|m#hS%Fm+K` Iފffzmp*[X_¤4H|5f׷7 @+P ?-mkI  U&ΗǢ -,tLasaڋX9OFO|nCxLyx܅?PR]DZi47MEt\KD*yh=򥫍RfRQM43Oe*8\)@x;Sǩm,Q0d,q_io!>_ڧpps =$y?]#g'Aoډ(xDjd8UmyV,+_*:j[;T, jD<`0z5IPԑR9v.]N?:rUixݧZhh飂5$U؊9Aganglia-web-3.6.1/templates/default/images/node_dead.jpg000066400000000000000000000021531231750357400232010ustar00rootroot00000000000000JFIFBBC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222<"8 !"1A#BQRq&236Tat%!qAQ1a ?wU 4"M{LM)U^g,91=jv(]DFIqhU\r&eCD%ZO@m+Eݻ)(Ϥ5]Z$/Q c, j+dD}r{ƲEMW LG4`rW# #P܉ eG.TQ0U[u+!=r n5o{K[VP#p'$E,cq4T$v-vtoEU$U9^j5oKeVS\![mSSeiX,vI=I!–&ֿtd^~[mi׭po LzyX1N{cnj(PېF <q$ N{'N+ \|rFA G[>N۹0&[[ W*kl|_g9/8uȗE5M$G%(Fz {={rN[(-7NVIUP$E$xT1b"gSx~TDؽM:jQkD]!܏>mHwt`g&- OZ {^zI] B+H3CkU]KF< qz {;LFIU3^(1-=Ru7 ۂm~3su}T^(&O5+++psΒ~'Տ@D'JE$gԉ3o92{'5UOܹ; /iyjoNo䑆S F%Tw\\dq~` {ml$֥ixpՋ|I>z[㦢0!D3s<}XstrPΠ^jZI*To ?Ƹ1^OcPoK"IXH${T{^2ܮ$>_= ݝaO2GCiGpU.7c6>B4PtYPĒq>'M:h?%:O7bd]9[K$|z#O)id q %ɽ ss q( u?a[="2ͺ*#BԱ4&P8.qGkhgu }kV}- =+aA3GQod3?ganglia-web-3.6.1/templates/default/meta_view.tpl000066400000000000000000000077361231750357400220450ustar00rootroot00000000000000{if isset($filters)} {foreach $filters filter} {/foreach}
    {$filter.filter_name}
    {/if} {foreach $sources source} {if isset($source.public)} {if isset($source.self_summary_graphs)} {/if} {if isset($source.summary_graphs)} {/if} {/foreach}
     
    {$source.name} {$source.alt_view}
    CPUs Total:{$source.cpu_num}
    Hosts up:{$source.num_nodes}
    Hosts down:{$source.num_dead_nodes}
     
    {$source.name} LOAD {$source.name} MEM
    {$source.name} CPU {$source.name} NETWORK
    {$source.name} LOAD {$source.name} MEM
    {/if} {/if} {if isset($source.private)}
    CPUs Total:{$source.cpu_num}
    Nodes:{$source.num_nodes}
     
    This is a private cluster.
    {if isset($show_snapshot)}
    Snapshot of the {$self} | Legend
    {foreach $snap_rows snap_row} {$snap_row.names}{$snap_row.images} {/foreach}
    {/if} ganglia-web-3.6.1/templates/default/metric_group_view.tpl000066400000000000000000000042371231750357400236070ustar00rootroot00000000000000 {$i = 0} {foreach $g_metrics["metrics"] g_metric} {$g_metric.new_row} {math "$i + 1" assign=i} {/foreach}
    {$g_metric.metric_name} {if $g_metric.title != '' && $g_metric.title != $g_metric.metric_name}- {$g_metric.title}{/if}
    {if $may_edit_views} {$graph_args = "&";$graph_args .= $g_metric.graphargs;} {/if} {if $graph_engine == "flot"}
    {else} {$graphId = cat($GRAPH_BASE_ID $mgId $i)} {$showEventsId = cat($SHOW_EVENTS_BASE_ID $mgId $i)} {$timeShiftId = cat($TIME_SHIFT_BASE_ID $mgId $i)}
    {$g_metric.alt} {/if}
    ganglia-web-3.6.1/templates/default/node_extra.tpl000066400000000000000000000000701231750357400221750ustar00rootroot00000000000000 ganglia-web-3.6.1/templates/default/physical_view.tpl000066400000000000000000000031351231750357400227200ustar00rootroot00000000000000
    {$cluster} cluster - Physical View | Columns  {$cols_menu}
    Verbosity level (Lower is more compact):
    {foreach $verbosity_levels verbosity checked} {$verbosity}   {/foreach}
    Total CPUs: {$CPUs}
    Total Memory: {$Memory}
    Total Disk: {$Disk}
    Most Full Disk: {$most_full}
    {foreach $racks rack} {$rack.tr} {/foreach}
    {$rack.RackID} {$rack.nodes}

    Legend
    Node Name 
    1-min load
    cpu: CPU clock (GHz) (num CPUs)
    mem: Total Memory (GB)
    ganglia-web-3.6.1/templates/default/show_node.tpl000066400000000000000000000040251231750357400220360ustar00rootroot00000000000000
    {$name} Info
     
    {$name}
    {$ip}
    Location: {$location}

    Cluster local time {$clustertime}
    Last heartbeat received {$age} ago.
    Uptime {$uptime}

    Load: {$load_one} {$load_five} {$load_fifteen}
    1m5m15m

    CPU Utilization: {$cpu_user} {$cpu_system} {$cpu_idle}
    usersysidle
    Hardware
    CPU{$s}: {$cpu}
    Memory (RAM): {$mem}
    Local Disk: {$disk}
    Most Full Disk Partition: {$part_max_used}
    Software
    OS: {$OS}
    Booted: {$booted}
    Uptime: {$uptime}
    Swap: {$swap}
    Physical View | Reload
    {if isset($extra)} {include(file="$extra")} {/if}
    ganglia-web-3.6.1/templates/default/view_content.tpl000066400000000000000000000037431231750357400225630ustar00rootroot00000000000000
    {if isset($number_of_view_items)} {if $number_of_view_items == 0 }

    No graphs defined for this view. Please add some

    {else} {$i = 0} {foreach $view_items view_item} {$graphId = cat($GRAPH_BASE_ID "view_" $i)} {$showEventsId = cat($SHOW_EVENTS_BASE_ID "view_" $i)}
    {if $view_item['canBeDecomposed'] == 1} {/if}
    {if $graph_engine == "flot"}
    {else} {/if}
    {math "$i + 1" assign=i} {/foreach} {/if} {/if}
    ganglia-web-3.6.1/templates/default/views_view.tpl000066400000000000000000000104671231750357400222470ustar00rootroot00000000000000
    {if !$display_views_using_tree} {$existing_views} {/if}
    {if !$display_views_using_tree} {/if}
    {include('view_content.tpl')}
    {if $ad_hoc_view} {/if} ganglia-web-3.6.1/test/000077500000000000000000000000001231750357400146645ustar00rootroot00000000000000ganglia-web-3.6.1/test/GangliaAclTest.php000066400000000000000000000107371231750357400202270ustar00rootroot00000000000000object = new GangliaAcl; } // This is the normal way to access the ACL. public function testGetInstance() { $obj1 = GangliaAcl::getInstance(); $obj2 = GangliaAcl::getInstance(); $this->assertEquals( $obj1, $obj2 ); } public function testGuestCanViewNormalClusters() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::GUEST, GangliaAcl::ALL_CLUSTERS, GangliaAcl::VIEW ) ); } public function testAdminCanViewNormalClusters() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, GangliaAcl::ALL_CLUSTERS, GangliaAcl::VIEW ) ); } public function testGuestCannotEdit() { $this->assertFalse( $this->object->isAllowed( GangliaAcl::GUEST, GangliaAcl::ALL_CLUSTERS, GangliaAcl::EDIT ) ); } public function testAdminCanEdit() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, GangliaAcl::ALL_CLUSTERS, GangliaAcl::EDIT ) ); } public function testAdminCanAccessPrivateCluster() { $this->object->addPrivateCluster( 'clustername' ); $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, 'clustername', GangliaAcl::VIEW ) ); $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, 'clustername', GangliaAcl::EDIT ) ); } public function testGuestCannotAccessPrivateCluster() { $this->object->addPrivateCluster( 'clustername' ); $this->assertFalse( $this->object->isAllowed( GangliaAcl::GUEST, 'clustername', GangliaAcl::VIEW ) ); $this->assertFalse( $this->object->isAllowed( GangliaAcl::GUEST, 'clustername', GangliaAcl::EDIT ) ); } public function testGuestCanViewNormalCluster() { $this->object->add( new Zend_Acl_Resource('clustername'), GangliaAcl::ALL_CLUSTERS ); $this->object->addRole( 'username', GangliaAcl::GUEST ); $this->object->allow( 'username', 'clustername', array(GangliaAcl::EDIT, GangliaAcl::VIEW) ); $this->assertTrue( $this->object->isAllowed( GangliaAcl::GUEST, 'clustername', GangliaAcl::VIEW ) ); } public function testUserMayBeGrantedViewAccessToPrivateCluster() { $this->object->addPrivateCluster( 'clustername' ); $this->object->addRole( 'newuser', GangliaAcl::GUEST ); $this->object->allow( 'newuser', 'clustername', GangliaAcl::VIEW ); $this->assertTrue( $this->object->isAllowed( 'newuser', 'clustername', GangliaAcl::VIEW ) ); $this->assertFalse( $this->object->isAllowed( 'newuser', 'clustername', GangliaAcl::EDIT ) ); } public function testUserMayBeGrantedEditAccessToPrivateCluster() { $this->object->addPrivateCluster( 'clustername' ); $this->object->addRole( 'newuser', GangliaAcl::GUEST ); $this->object->allow( 'newuser', 'clustername', array( GangliaAcl::VIEW, GangliaAcl::EDIT ) ); $this->assertTrue( $this->object->isAllowed( 'newuser', 'clustername', GangliaAcl::VIEW ) ); $this->assertTrue( $this->object->isAllowed( 'newuser', 'clustername', GangliaAcl::EDIT ) ); } public function testGuestCanViewViews() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::GUEST, GangliaAcl::ALL_VIEWS, GangliaAcl::VIEW ) ); } public function testAdminCanViewViews() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, GangliaAcl::ALL_VIEWS, GangliaAcl::VIEW ) ); } public function testGuestCannotEditViews() { $this->assertFalse( $this->object->isAllowed( GangliaAcl::GUEST, GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT ) ); } public function testAdminCanEditViews() { $this->assertTrue( $this->object->isAllowed( GangliaAcl::ADMIN, GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT ) ); } public function testUserCanViewViews() { $this->object->addRole( 'newuser', GangliaAcl::GUEST ); $this->assertTrue( $this->object->isAllowed( 'newuser', GangliaAcl::ALL_VIEWS, GangliaAcl::VIEW ) ); } public function testUserCannotEditViews() { $this->object->addRole( 'newuser', GangliaAcl::GUEST ); $this->assertFalse( $this->object->isAllowed( 'newuser', GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT ) ); } } ?> ganglia-web-3.6.1/test/GangliaAuthTest.php000066400000000000000000000050421231750357400204220ustar00rootroot00000000000000cookie_data = array('user'=>$user, 'group'=>null, 'token'=>sha1($user.$_SERVER['ganglia_secret'])); } public function tearDown() { GangliaAuth::destroyInstance(); } // This is the normal way to access the Auth instance. public function testGetInstance() { $obj1 = GangliaAuth::getInstance(); $obj2 = GangliaAuth::getInstance(); $this->assertEquals( $obj1, $obj2 ); } public function testEnvironmentIsInvalidWithoutGangliaSecret() { unset($_SERVER['ganglia_secret']); $auth = GangliaAuth::getInstance(); $errors = $auth->getEnvironmentErrors(); $this->assertEquals("No ganglia_secret set in the server environment.", substr($errors[0], 0, 48)); } public function testGetAuthTokenHashesUsernameAndSecret() { $auth = GangliaAuth::getInstance(); $expected = sha1($this->cookie_data['user'] . $_SERVER['ganglia_secret']); $this->assertEquals($expected, $auth->getAuthToken($this->cookie_data['user'])); } public function testAuthUnserializesCookieInformation() { $_COOKIE['ganglia_auth'] = serialize( $this->cookie_data ); $auth = GangliaAuth::getInstance(); $this->assertTrue($auth->isAuthenticated()); $this->assertEquals('foo', $auth->getUser()); } public function testIncorrectHashCausesAuthFailure() { $this->cookie_data['token'] = 'xxxxxxx'; $_COOKIE['ganglia_auth'] = serialize( $this->cookie_data ); $auth = GangliaAuth::getInstance(); $this->assertFalse($auth->isAuthenticated()); $this->assertNull($auth->getUser()); } public function testSlashesAddedByMagicQuotesDontCauseUnserializationFailure() { $_COOKIE['ganglia_auth'] = addslashes(serialize( $this->cookie_data )); // we can't enable real magic_quotes_gpc at runtime, so we mock it. $auth = $this->getMockBuilder('GangliaAuth') ->disableOriginalConstructor() ->setMethods(array('getMagicQuotesGpc')) ->getMock(); $auth->expects($this->once()) ->method('getMagicQuotesGpc') ->will($this->returnValue(true)); // this is normally part of the constructor, but we have to get our mock getMagicQuotesGpc in place first $auth->init(); $this->assertTrue($auth->isAuthenticated()); $this->assertEquals('foo', $auth->getUser()); } } ?> ganglia-web-3.6.1/trend_navigation.php000066400000000000000000000040721231750357400177540ustar00rootroot00000000000000 $value ) { if ( ! in_array($key, $drop_args) ) $graph_args[] = rawurlencode($key) . "=" . rawurlencode( str_replace("_/graph_php?", "", $value) ); } $query_string = preg_replace("/(&trendrange=)(\d+)/", "", $_SERVER['QUERY_STRING'] ); $query_string = preg_replace("/(&trendhistory=)(\d+)/", "", htmlspecialchars($query_string, ENT_QUOTES) ); ?>
    Use data from last $month ) { if ( $_REQUEST['trendhistory'] == $month ) $checked = 'checked="checked"'; else $checked = ""; ?> /> months | Extend trend line $month ) { if ( $_REQUEST['trendrange'] == $month ) $checked = 'checked="checked"'; else $checked = ""; ?> /> months ahead
    ganglia-web-3.6.1/version.php.in000066400000000000000000000001301231750357400165020ustar00rootroot00000000000000 ganglia-web-3.6.1/view_content.php000066400000000000000000000037141231750357400171270ustar00rootroot00000000000000

    View names valid characters are 0-9, a-z, A-Z, -, _ and space. View has not been created.

    assign("additional_host_img_css_classes", $additional_host_img_css_classes); $view_items = NULL; $view = $viewList->getView($view_name); if ($view != NULL) { $range = isset($_GET["r"]) ? escapeshellcmd(rawurldecode($_GET["r"])) : NULL; $cs = isset($_GET["cs"]) ? escapeshellcmd($_GET["cs"]) : NULL; $ce = isset($_GET["ce"]) ? escapeshellcmd($_GET["ce"]) : NULL; if ($cs or $ce) $range = "custom"; $view_items = getViewItems($view, $range, $cs, $ce); } if (isset($view_items)) { $data->assign("view_items", $view_items); $data->assign("number_of_view_items", sizeof($view_items)); } $data->assign('GRAPH_BASE_ID', $GRAPH_BASE_ID); $data->assign('SHOW_EVENTS_BASE_ID', $SHOW_EVENTS_BASE_ID); $dwoo->output($tpl, $data); ?> ganglia-web-3.6.1/views_view.php000066400000000000000000000317771231750357400166240ustar00rootroot00000000000000

    View names valid characters are 0-9, a-z, A-Z, -, _ and space. View has not been created.

    viewExists($view_name)) { $output = "Alert: View with the name " . $view_name . " already exists."; } else { $empty_view = array ("view_name" => $view_name, "items" => array()); $view_filename = viewFileName($view_name); if (pathinfo($view_filename, PATHINFO_DIRNAME) != $conf['views_dir']) { die('Invalid path detected'); } $json = json_encode($empty_view); if (file_put_contents($view_filename, json_prettyprint($json)) === FALSE) { $output = "Alert:" . " Can't write to file " . htmlspecialchars($view_filename) . " Perhaps permissions are wrong."; } else { $output = "View has been created successfully."; } } } $json = '{"output": "

    ' . $output . '

    "'; if ($conf['display_views_using_tree']) { $json .= ',"tree_node": {"data":"' . $view_name . '","attr":{"id":"' . viewId($view_name) . '"'; $json .= ',"view_name":"' . $view_name . '"'; $json .= '}}'; } $json .= '}'; echo $json; exit(0); } /////////////////////////////////////////////////////////////////////////////// // Delete view /////////////////////////////////////////////////////////////////////////////// if (isset($_GET['delete_view'])) { if (! checkAccess(GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf)) { $output = "You do not have access to edit views."; } else { if (!$viewList->viewExists($view_name)) { $output = "Alert: View with the name " . $view_name . " does not exist."; } else { $view_filename = viewFileName($view_name); if (pathinfo($view_filename, PATHINFO_DIRNAME) != $conf['views_dir']) { die('Invalid path detected'); } if (unlink($view_filename) === FALSE) { $output = "Alert:" . " Can't remove file $view_filename." . " Perhaps permissions are wrong."; } else { $output = "View has been successfully removed."; $viewList->removeView($view_name); } } } } // delete_view /////////////////////////////////////////////////////////////////////////////// // Add to view /////////////////////////////////////////////////////////////////////////////// if (isset($_GET['add_to_view'])) { if (! checkAccess(GangliaAcl::ALL_VIEWS, GangliaAcl::EDIT, $conf)) { $output = "You do not have access to edit views."; } else { if (!$viewList->viewExists($view_name)) { $output = "Alert: View " . $view_name . " does not exist. This should not happen."; } else { $view = $viewList->getView($view_name); # Check if we are adding an aggregate graph if (isset($_GET['aggregate'])) { foreach ($_GET['mreg'] as $key => $value) $metric_regex_array[] = array("regex" => $value); foreach ($_GET['hreg'] as $key => $value) $host_regex_array[] = array("regex" => $value); $item_array = array("aggregate_graph" => "true", "metric_regex" => $metric_regex_array, "host_regex" => $host_regex_array, "graph_type" => stripslashes($_GET['gtype']), "vertical_label" => stripslashes($_GET['vl']), "title" => $_GET['title'], "glegend" => $_GET['glegend']); if (isset($_GET['x']) && is_numeric($_GET['x'])) { $item_array["upper_limit"] = $_GET['x']; } if (isset($_GET['n']) && is_numeric($_GET['n'])) { $item_array["lower_limit"] = $_GET['n']; } if (isset($_GET['c'])) { $item_array["cluster"] = $_GET['c']; } if (isset($_GET['h'])) { $item_array['host'] = $_GET['h']; unset($item_array['host_regex']); } if (isset($_GET['m'])) { $item_array['metric'] = $_GET['m']; unset($item_array['metric_regex']); } if (isset($_GET['g'])) { $item_array['graph'] = $_GET['g']; } if ($item_array['host_regex'] == null) $item_array['host_regex'] = '.*'; $view['items'][] = $item_array; unset($item_array); } else { if ($_GET['type'] == "metric") { $items = array("hostname" => $_GET['host_name'], "metric" => $_GET['metric_name']); if (isset($_GET['vertical_label'])) $items["vertical_label"] = stripslashes($_GET['vertical_label']); if (isset($_GET['title'])) $items["title"] = stripslashes($_GET['title']); if (isset($_GET['c'])) $items["cluster"] = $_GET['c']; if (isset($_GET['warning']) && is_numeric($_GET['warning'])) $items["warning"] = $_GET['warning']; if (isset($_GET['critical']) && is_numeric($_GET['critical'])) $items["critical"] = $_GET['critical']; $view['items'][] = $items; } else $view['items'][] = array("hostname" => $_GET['host_name'], "graph" => $_GET['metric_name']); } $view_filename = $view['file_name']; // Remove the file_name attribute, it is not stored in the view defn. unset($view['file_name']); $json = json_encode($view); if (file_put_contents($view_filename, json_prettyprint($json)) === FALSE ) { $output = "Alert:" . " Can't write to file: \"$view_filename\"." . " Perhaps permissions are wrong."; } else { $output = "View has been updated successfully."; } } } ?>

    name = $name; } public function hasChild($name) { if ($this->children == NULL) return false; return array_key_exists($name, $this->children); } public function getChild($name) { return $this->children[$name]; } public function getChildren() { return $this->children; } public function addChild($node) { if ($this->children == NULL) $this->children = array(); $this->children[$node->getName()] = $node; $node->setParent($this); return $node; } public function getNode($path) { $parent = $this; foreach (explode("/", $path) as $node_name) { if ($parent->hasChild($node_name)) { $parent = $parent->getChild($node_name); } else { $parent = $parent->addChild(new ViewTreeNode($node_name)); } } return $parent; } public function getData() { return $this->data; } public function setData($data) { $this->data = $data; } public function getName() { return $this->name; } public function getParent() { return $this->parent; } public function setParent($parent) { $this->parent = $parent; } public function toJson($initially_open) { $pathName = $this->getPathName(); $id = viewId($pathName); $json = '{"data":"' . $this->name . '","attr":{"id":"' . $id . '"'; if ($this->data != NULL) $json .= ',"view_name":"' . $pathName . '"'; $json .= '}'; if ($initially_open && in_array($pathName, $initially_open)) $json .= ',"state":"open"'; if ($this->children != NULL) { $json .= ',"children":['; $i = 0; foreach ($this->children as $child_node) { if ($i++ > 0) $json .= ','; $json .= $child_node->toJson($initially_open); } $json .= ']'; } $json .= '}'; return $json; } function getPathName() { global $VIEW_NAME_SEP; if ($this->parent == NULL) return $this->name; $pathName = $this->name; $parent = $this->parent; while ($parent->getParent() != NULL) { $pathName = $parent->getName() . $VIEW_NAME_SEP . $pathName; $parent = $parent->getParent(); } return $pathName; } } function build_view_tree($views) { $view_tree = new ViewTreeNode('root'); foreach ($views as $view) { $node = new ViewTreeNode($view['view_name']); $node->setData($view); if ($view['parent'] != NULL) { $parent = $view_tree->getNode($view['parent']); $parent->addChild($node); } else { $view_tree->addChild($node); } } return $view_tree; } $existing_views = ''; if ($conf['display_views_using_tree']) { $initially_open = NULL; if (!isset($_SESSION['view_tree_built']) && isset($conf['view_tree_nodes_initially_open'])) $initially_open = $conf['view_tree_nodes_initially_open']; $view_tree = build_view_tree($viewList->getViews()); $existing_views = '['; $i = 0; foreach ($view_tree->getChildren() as $view_node) { if ($i++ > 0) $existing_views .= ','; $existing_views .= $view_node->toJson($initially_open); $i++; } $existing_views .= ']'; $_SESSION['view_tree_built'] = TRUE; } else { foreach ($viewList->getViews() as $view) { if ($view['parent'] == NULL) { $v = $view['view_name']; $vid = viewId($v); $checked = ($_GET['vn'] == $v); $existing_views .= ''; } } } if (isset($_GET['views_menu'])) { if (!$conf['display_views_using_tree']) { ?>
    assign("range",$range); if (isset($conf['ad-hoc-views']) && $conf['ad-hoc-views'] === true && isset($_GET['ad-hoc-view'])) { $is_ad_hoc = true; } // Pop up a warning message if there are no available views // (Disable temporarily, otherwise we can't create views) if (sizeof($viewList->getViews()) == -1 && !$is_ad_hoc) { $error_msg = '

    Alert: There are no views defined.

    '; } $size = isset($clustergraphsize) ? $clustergraphsize : 'default'; // set to 'default' to preserve old behavior if ($size == 'medium') $size = 'default'; $additional_host_img_css_classes = ""; if (isset($conf['zoom_support']) && $conf['zoom_support'] === true) $additional_host_img_css_classes = "host_${size}_zoomable"; $data->assign("additional_host_img_css_classes", $additional_host_img_css_classes); $data->assign("existing_views", $existing_views); $data->assign("view_name", $view_name); $data->assign("display_views_using_tree", $conf["display_views_using_tree"]); $view_items = NULL; if ($is_ad_hoc) { $data->assign("ad_hoc_view", true); $data->assign("ad_hoc_view_json", rawurlencode($_GET['ad-hoc-view'])); $ad_hoc_view_json = json_decode(heuristic_urldecode($_GET['ad-hoc-view']), true); $view_items = getViewItems($ad_hoc_view_json, $range, $cs, $ce); } else { $view = $viewList->getView($view_name); if ($view != NULL) $view_items = getViewItems($view, $range, $cs, $ce); } if (isset($view_items)) { $data->assign("view_items", $view_items); $data->assign("number_of_view_items", sizeof($view_items)); } $data->assign('GRAPH_BASE_ID', $GRAPH_BASE_ID); $data->assign('SHOW_EVENTS_BASE_ID', $SHOW_EVENTS_BASE_ID); $dwoo->output($tpl, $data); ?>