From 68b0ffff95a267964f36efc8ac0a5d31ff33cf6e Mon Sep 17 00:00:00 2001 From: olahaye74 Date: Fri, 28 Feb 2014 11:43:39 +0100 Subject: [PATCH 01/35] Source1 not needed. apache.conf already in current directory. No need to %__cp %SOURCE1 as well. More over, using more than one source prevent a build from tarball using rpmbuild -tb ganglia-web-3.5.12.tar.gz for instance. --- ganglia-web.spec.in | 2 -- 1 file changed, 2 deletions(-) diff --git a/ganglia-web.spec.in b/ganglia-web.spec.in index 6207ffa8..380d5114 100644 --- a/ganglia-web.spec.in +++ b/ganglia-web.spec.in @@ -7,7 +7,6 @@ License: BSD Vendor: Ganglia Development Team Group: System Environment/Base Source: %{name}-%{version}.tar.gz -Source1: apache.conf Buildroot: %{_tmppath}/%{name}-%{version}-buildroot Obsoletes: ganglia-webfrontend Requires: php >= 5, php-gd @@ -29,7 +28,6 @@ written in the PHP5 language and uses the Dwoo templating engine. %prep %setup -n %{name}-%{version} -%__cp %{SOURCE1} . %build From c20d87874c30eebef32f5649cc5dc180995680c9 Mon Sep 17 00:00:00 2001 From: olahaye74 Date: Fri, 28 Feb 2014 23:50:45 +0100 Subject: [PATCH 02/35] [build & packaging] Reworked the install and packaging so it results in the same installation whatever the build is (manual, deb pkg or rpm pkg) - Use Makefile to make install for deb and rpm as well including config files. - Include specfile in the dist-gzip so a rpmbuild -tb (tarbuild) can be used - Added customization for rpmbuild using --with tags (see spec file for usage and more details). - Use adequate default values for rpm. The apache_user is not www-data. it's apache... - Use /etc/ganglia-webfrontend to store conf.php what ever the distro is. Make sure that a link is set to apache config dir using triggers or postinstall. - Use /etc/ganglia-webfrontend to store conf.php what ever the distro is. Use a generic conf.php in /usr/share/ganglia-webfrontend to load the file from /etc/ganglia-webfrontend. - Replaced vargwebstatedir with vargwebdir in conf_default.php.in and Makefile. - filters dir was not created under debian pkg or manual make install - Added possibility to have a httpd_group different from user. For instance you can choose to install files under apache.daemon identity. If not specified, a group with the same name as the user is used. - Added comment in the install section of the Makefile so it's clear. - Reworked dist-gzip target so version.php and spec file are generated while other .in files remain so they can be tuned by make or make install. - Make sure that sharedstatedir is not owned by rpm package (conflict on fc-18+), and updated %file section accordingly. - Removed the manual install and specific config files in debian packaging. Using standard make install DESTDIR=... --- Makefile | 83 +++++++++++++++++++++++++------- conf_default.php.in | 2 +- conf_redirect.php.in | 5 ++ debian/apache.conf | 9 ---- debian/conf_debian.php | 5 -- debian/rules | 7 +-- ganglia-web.spec.in | 106 ++++++++++++++++++++++++++++------------- 7 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 conf_redirect.php.in delete mode 100644 debian/apache.conf delete mode 100644 debian/conf_debian.php diff --git a/Makefile b/Makefile index 4a8d46b7..cf3f3209 100644 --- a/Makefile +++ b/Makefile @@ -13,9 +13,20 @@ GWEB_STATEDIR = /var/lib/ganglia-web # Gmetad rootdir (parent location of rrd folder) GMETAD_ROOTDIR = /var/lib/ganglia -APACHE_USER = www-data +#APACHE_USER = www-data +#APACHE_GROUP = www-data ########################################################## +ifndef APACHE_USER +$(warning APACHE_USER is not set using www-data) +APACHE_USER = www-data +endif + +ifndef APACHE_GROUP +$(warning APACHE_GROUP is not set, using $(APACHE_USER)) +APACHE_GROUP = $(APACHE_USER) +endif + # Gweb version GWEB_MAJOR_VERSION = 3 GWEB_MINOR_VERSION = 5 @@ -27,7 +38,12 @@ 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 +# TARGETS: Files that needs to be patched with user chosen variables +TARGETS = conf_default.php conf_redirect.php apache.conf + +# DIST_GZIP_TARGETS targets that needs to be patched once before dist-gzip +# basicaly version and default values. +DIST_GZIP_TARGETS = ganglia-web.spec version.php all: default @@ -37,10 +53,13 @@ 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 + sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebdir@|$(GWEB_STATEDIR)|g" conf_default.php.in > conf_default.php + +conf_redirect.php: conf_redirect.php.in + sed -e "s|@etcdir@|$(GCONFDIR)|" conf_redirect.php.in > conf_redirect.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 + sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ -e "s|@vargwebdir@|$(GWEB_STATEDIR)|" -e "s|@GDESTDIR@|$(GDESTDIR)|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 @@ -49,21 +68,52 @@ 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) + rsync --exclude "rpmbuild" \ + --exclude "debian/ganglia-webfrontend" \ + --exclude "*.gz" \ + --exclude "$(DIST_DIR)" \ + --exclude ".git*" \ + --exclude "version.php.in" \ + --exclude "ganglia-web.spec.in" \ + --exclude "apache.conf" \ + --exclude "conf_default.php" \ + --exclude "*~" \ + --exclude "#*#" \ + -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 + # Create dwoo sharedstattedir tree + mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/compiled + mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/cache + mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/filters + # Install ganglia-webfrontend low level conf + rsync -a $(DIST_DIR)/conf $(DESTDIR)/$(GWEB_STATEDIR) + # Set apache daemon ownership to the sharedstattedir files. + chown -R $(APACHE_USER):$(APACHE_GROUP) $(DESTDIR)/$(GWEB_STATEDIR) + # Install php files + mkdir -p $(DESTDIR)/$(GDESTDIR) + rsync --exclude "conf" \ + --exclude conf_redirect.php \ + --exclude "*.in" \ + --exclude "*.spec" \ + -a $(DIST_DIR)/* $(DESTDIR)/$(GDESTDIR) + # Intall file that redirect conf.php to /etc/ganglia-webfrontend + # so it can be seen as a config file under debian packages. + # Do the same for all distro to avoid specific case for debian. + cp -f conf_redirect.php $(DESTDIR)/$(GDESTDIR)/conf.php + # Install the generated conf_default.php file. + cp -f conf_default.php $(DESTDIR)/$(GDESTDIR)/conf_default.php + # Create the /etc/ganglia-webfrontend directory. + mkdir -p $(DESTDIR)/$(GCONFDIR) + cp -f apache.conf $(DESTDIR)/$(GCONFDIR)/ + # Install the editable copy of conf_default.php + cp -f conf_default.php $(DESTDIR)/$(GCONFDIR)/conf.php + +dist-gzip: dist-dir $(DIST_GZIP_TARGETS) if [ -f $(DIST_TARBALL) ]; then \ rm -rf $(DIST_TARBALL) ;\ fi ;\ + cp -pf $(DIST_GZIP_TARGETS) $(DIST_DIR) tar -czf $(DIST_TARBALL) $(DIST_DIR)/* rpm: dist-gzip ganglia-web.spec apache.conf @@ -73,9 +123,8 @@ rpm: dist-gzip ganglia-web.spec apache.conf 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 + ln -s $(DIST_TARBALL) rpmbuild/SOURCES + rpmbuild --define '_topdir $(PWD)/rpmbuild' --with web_prefixdir=$(GDESTDIR) -bb ganglia-web.spec uninstall: rm -rf $(DESTDIR)/$(GDESTDIR) $(DESTDIR)/$(GWEB_STATEDIR) diff --git a/conf_default.php.in b/conf_default.php.in index 68e3c134..7cb575cc 100644 --- a/conf_default.php.in +++ b/conf_default.php.in @@ -10,7 +10,7 @@ # Gmetad-webfrontend version. Used to check for updates. # $conf['gweb_root'] = dirname(__FILE__); -$conf['gweb_confdir'] = "@vargwebstatedir@"; +$conf['gweb_confdir'] = "@vargwebdir@"; include_once $conf['gweb_root'] . "/version.php"; diff --git a/conf_redirect.php.in b/conf_redirect.php.in new file mode 100644 index 00000000..e3908841 --- /dev/null +++ b/conf_redirect.php.in @@ -0,0 +1,5 @@ + diff --git a/debian/apache.conf b/debian/apache.conf deleted file mode 100644 index 88b4c4bf..00000000 --- a/debian/apache.conf +++ /dev/null @@ -1,9 +0,0 @@ -Alias /ganglia /usr/share/ganglia-webfrontend - - - AllowOverride All - Order allow,deny - Allow from all - Deny from none - - diff --git a/debian/conf_debian.php b/debian/conf_debian.php deleted file mode 100644 index a0fdc625..00000000 --- a/debian/conf_debian.php +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/debian/rules b/debian/rules index b9623dc2..c6b619b1 100755 --- a/debian/rules +++ b/debian/rules @@ -26,9 +26,4 @@ override_dh_auto_install: dh_installdirs # Add here commands to install the package into debian/ganglia. $(MAKE) install APACHE_USER=www-data DESTDIR=$(CURDIR)/debian/ganglia-webfrontend - # Install webfrontend - mkdir -p debian/ganglia-webfrontend/etc/apache2/conf.d/ - cp -f debian/apache.conf \ - debian/ganglia-webfrontend/etc/ganglia-webfrontend - cp -f debian/conf_debian.php \ - debian/ganglia-webfrontend/usr/share/ganglia-webfrontend/conf.php + #mkdir -p debian/ganglia-webfrontend/etc/apache2/conf.d/ diff --git a/ganglia-web.spec.in b/ganglia-web.spec.in index 380d5114..6d1f0527 100644 --- a/ganglia-web.spec.in +++ b/ganglia-web.spec.in @@ -1,3 +1,33 @@ + +# The following options are supported: +# --with httpd_user= # defaults to: apache +# --with httpd_group= # defaults to: apache +#  --with web_prefixdir= # defaults to: @GDESTDIR@ +#  --with gweb_statedir= # defaults to: @vargwebdir@ +#  --with gweb_confdir= # defaults to: @etcdir@ + +# example: rpmbuild -tb ganglia-web-3.5.12.tar.gz --with httpd_user=www-data --with httpd_group=www-data --with web_prefixdir=/srv/www/ganglia + +# Default value for web_prefixdir depending on distro. +%if 0%{?suse_version} +%define web_prefixdir /srv/www/htdocs/ganglia +%else +%define web_prefixdir @GDESTDIR@ +%endif + +# Default value for httpd user and group. +%define httpd_user apache +%define httpd_group apache +%define gweb_statedir @vargwebdir@ +%define gweb_confdir @etcdir@ + +# Read the provided --with tags if any (overriding default values). +%{?_with_httpd_user:%define httpd_user %(set -- %{_with_httpd_user}; echo $1 | grep -v with | sed 's/=//')} +%{?_with_httpd_group:%define httpd_group %(set -- %{_with_httpd_group}; echo $1 | grep -v with | sed 's/=//')} +%{?_with_web_prefixdir:%define web_prefixdir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} +%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} +%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} + Summary: Ganglia Web Frontend Name: ganglia-web Version: @GWEB_VERSION@ @@ -10,15 +40,6 @@ Source: %{name}-%{version}.tar.gz Buildroot: %{_tmppath}/%{name}-%{version}-buildroot Obsoletes: ganglia-webfrontend Requires: php >= 5, php-gd -%if 0%{?suse_version} -%define web_prefixdir /srv/www/htdocs/ganglia -%else -%define web_prefixdir %{custom_web_prefixdir} -%endif - -%{!?custom_web_prefixdir: %define web_prefixdir /var/www/html/ganglia} - -Prefix: %{web_prefixdir} BuildArchitectures: noarch %description @@ -30,35 +51,49 @@ written in the PHP5 language and uses the Dwoo templating engine. %setup -n %{name}-%{version} %build +# Update all needed files +%__make GDESTDIR=%{web_prefixdir} \ + APACHE_USER=%{httpd_user} \ + APACHE_GROUP=%{httpd_group} \ + GWEB_STATEDIR=%{gweb_statedir} \ + GCONFDIR=%{gweb_confdir} %install # Flush any old RPM build root %__rm -rf $RPM_BUILD_ROOT -%__mkdir -p $RPM_BUILD_ROOT/%{web_prefixdir} -%__cp -rf * $RPM_BUILD_ROOT/%{web_prefixdir} -%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/conf -%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/apache.conf -%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/filters -%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/conf -%__cp -rf conf/* $RPM_BUILD_ROOT@vargwebdir@/conf -%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo -%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/compiled -%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/cache -%__install -d -m 0755 $RPM_BUILD_ROOT@etcdir@ -%__cp -f apache.conf $RPM_BUILD_ROOT@etcdir@/ +%__make install GDESTDIR=%{web_prefixdir} \ + APACHE_USER=%{httpd_user} \ + APACHE_GROUP=%{httpd_group} \ + GWEB_STATEDIR=%{gweb_statedir} \ + GCONFDIR=%{gweb_confdir} \ + DESTDIR=$RPM_BUILD_ROOT + +#%__mkdir -p $RPM_BUILD_ROOT/%{web_prefixdir} +#%__cp -rf * $RPM_BUILD_ROOT/%{web_prefixdir} +#%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/conf +#%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/apache.conf +#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/filters +#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/conf +#%__cp -rf conf/* $RPM_BUILD_ROOT@vargwebdir@/conf +#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo +#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/compiled +#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/cache +#%__install -d -m 0755 $RPM_BUILD_ROOT@etcdir@ +#%__cp -f apache.conf $RPM_BUILD_ROOT@etcdir@/ %files %defattr(-,root,root) -%attr(0755,nobody,nobody)@vargwebdir@/filters -%dir %attr(0755,@varapacheuser@,@varapacheuser@)@vargwebdir@/conf -%dir %attr(0755,@varapacheuser@,@varapacheuser@)@vargwebdir@/dwoo -%attr(0755,@varapacheuser@,@varapacheuser@)@vargwebdir@/dwoo/compiled -%attr(0755,@varapacheuser@,@varapacheuser@)@vargwebdir@/dwoo/cache -%{web_prefixdir}/ -%config(noreplace) %{web_prefixdir}/conf_default.php -@vargwebdir@/conf/* -%config(noreplace) @etcdir@/apache.conf +%dir %attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir} +%dir %attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir}/filters +%dir %attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir}/conf +%{gweb_statedir}/conf/* +%dir %attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir}/dwoo +%attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir}/dwoo/compiled +%attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir}/dwoo/cache +%{web_prefixdir}/* +%config(noreplace) %{gweb_confdir}/apache.conf +%config(noreplace) %{gweb_confdir}/conf.php %clean %__rm -rf $RPM_BUILD_ROOT @@ -68,18 +103,25 @@ written in the PHP5 language and uses the Dwoo templating engine. %triggerin -- httpd if [ $1 -eq 1 -a $2 -eq 1 ]; then if [ ! -e /etc/httpd/conf.d/ganglia-web.conf ] ; then - ln -s @etcdir@/apache.conf /etc/httpd/conf.d/ganglia-web.conf + ln -s %{gweb_confdir}/apache.conf /etc/httpd/conf.d/ganglia-web.conf fi fi %triggerun -- httpd if [ $2 -eq 0 ]; then - if [ -h /etc/httpd/conf.d/ganglia-web -a "`readlink /etc/httpd/conf/httpd.conf`" = "@etcdir@/apache.conf" ]; then + if [ -h /etc/httpd/conf.d/ganglia-web -a "`readlink /etc/httpd/conf/httpd.conf`" = "%{gweb_confdir}/apache.conf" ]; then rm /etc/httpd/conf.d/ganglia-web.conf fi fi %changelog +* Fri Feb 28 2014 Olivier Lahaye +- Added the ability to change Makefile variables using --with switch for the + following variables: + httpd_user, httpd_group, web_prefixdir, gweb_confdir, gweb_statedir +- Reworked the web_prefixdir setup from the rpmbuild command line. Moved from + --define '%custom_web_prefixdir ' to --with web_prefixdir= + Also fixed a bug that prevented to fix the web_prefixdir on SuSE Linux. * Tue Jun 04 2013 Wesley Hirsch - Added default apache configuration * Thu Mar 17 2011 Bernard Li From 3e6bbc0cfced6013eb115a3c706e4ce6eb0bf10d Mon Sep 17 00:00:00 2001 From: olahaye74 Date: Wed, 5 Mar 2014 19:33:11 +0100 Subject: [PATCH 03/35] Fix --with values unwanted dashes conversion to underscores. --- ganglia-web.spec.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ganglia-web.spec.in b/ganglia-web.spec.in index 6d1f0527..8f32e415 100644 --- a/ganglia-web.spec.in +++ b/ganglia-web.spec.in @@ -22,11 +22,11 @@ %define gweb_confdir @etcdir@ # Read the provided --with tags if any (overriding default values). -%{?_with_httpd_user:%define httpd_user %(set -- %{_with_httpd_user}; echo $1 | grep -v with | sed 's/=//')} -%{?_with_httpd_group:%define httpd_group %(set -- %{_with_httpd_group}; echo $1 | grep -v with | sed 's/=//')} -%{?_with_web_prefixdir:%define web_prefixdir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} -%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} -%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_web_prefixdir}; echo $1 | grep -v with | sed 's/=//')} +%{?_with_httpd_user:%define httpd_user %(set -- %{_with_httpd_user}; echo $2 | cut -d= -f2)} +%{?_with_httpd_group:%define httpd_group %(set -- %{_with_httpd_group}; echo $2 | cut -d= -f2)} +%{?_with_web_prefixdir:%define web_prefixdir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} +%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} +%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} Summary: Ganglia Web Frontend Name: ganglia-web From 576664243839878f00961d38dc8616e6c49813b6 Mon Sep 17 00:00:00 2001 From: olahaye74 Date: Wed, 5 Mar 2014 19:41:15 +0100 Subject: [PATCH 04/35] fix a stupid copy-past error from me. --- ganglia-web.spec.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ganglia-web.spec.in b/ganglia-web.spec.in index 8f32e415..e55bae53 100644 --- a/ganglia-web.spec.in +++ b/ganglia-web.spec.in @@ -25,8 +25,8 @@ %{?_with_httpd_user:%define httpd_user %(set -- %{_with_httpd_user}; echo $2 | cut -d= -f2)} %{?_with_httpd_group:%define httpd_group %(set -- %{_with_httpd_group}; echo $2 | cut -d= -f2)} %{?_with_web_prefixdir:%define web_prefixdir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} -%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} -%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} +%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_gweb_statedir}; echo $2 | cut -d= -f2)} +%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_gweb_confdir}; echo $2 | cut -d= -f2)} Summary: Ganglia Web Frontend Name: ganglia-web From c01651b51bace05e8bc3d0613801dc0a63c6e23f Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 3 Jun 2014 15:23:52 -0700 Subject: [PATCH 05/35] No need to fold since gmetad returns upper-case --- ganglia.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ganglia.php b/ganglia.php index 3de8b565..fad078af 100644 --- a/ganglia.php +++ b/ganglia.php @@ -401,6 +401,9 @@ function Gmetad () $start = gettimeofday(); + xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); + while(!feof($fp)) { $data = fread($fp, 16384); From 15ec29ceccab1676d67b5ce95b450e246a0e4486 Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 3 Jun 2014 15:45:24 -0700 Subject: [PATCH 06/35] Lock the cache file while writing to prevent partial reads --- lib/Cache/Driver_Json.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Cache/Driver_Json.php b/lib/Cache/Driver_Json.php index 2725cfb4..68e9eb35 100644 --- a/lib/Cache/Driver_Json.php +++ b/lib/Cache/Driver_Json.php @@ -7,10 +7,10 @@ function g_cache_exists() { function g_cache_serialize($data) { global $conf; - file_put_contents($conf['cachefile'], serialize($data)); - file_put_contents($conf['cachefile'] . "_cluster_data", serialize($data["cluster"])); - file_put_contents($conf['cachefile'] . "_host_list", serialize($data["hosts"])); - file_put_contents($conf['cachefile'] . "_metric_list", serialize(array_keys($data["metrics"]))); + file_put_contents($conf['cachefile'], serialize($data), LOCK_EX); + file_put_contents($conf['cachefile'] . "_cluster_data", serialize($data["cluster"]), LOCK_EX); + file_put_contents($conf['cachefile'] . "_host_list", serialize($data["hosts"]), LOCK_EX); + file_put_contents($conf['cachefile'] . "_metric_list", serialize(array_keys($data["metrics"])), LOCK_EX); } // end function g_cache_serialize function g_cache_deserialize($index) { From 316e2e132bf8af250fed6400c71cde6d21ea0362 Mon Sep 17 00:00:00 2001 From: olahaye74 Date: Tue, 22 Jul 2014 14:28:39 +0200 Subject: [PATCH 07/35] [Makefile + rpm spec file] Make Makefile more smart and more verbose, adapted specfile for this. Now Makefile has a rule that install files without setting ownership. this permit to build an rpm without being root. using --define magro for specfile tunning. --- Makefile | 125 ++++++++++++++++++++++++++++---------------- ganglia-web.spec.in | 57 ++++++-------------- 2 files changed, 96 insertions(+), 86 deletions(-) diff --git a/Makefile b/Makefile index ab172f2f..c1b310c8 100644 --- a/Makefile +++ b/Makefile @@ -18,12 +18,12 @@ GMETAD_ROOTDIR = /var/lib/ganglia ########################################################## ifndef APACHE_USER -$(warning APACHE_USER is not set using www-data) +$(info APACHE_USER is not set using www-data) APACHE_USER = www-data endif ifndef APACHE_GROUP -$(warning APACHE_GROUP is not set, using $(APACHE_USER)) +$(info APACHE_GROUP is not set, using $(APACHE_USER)) APACHE_GROUP = $(APACHE_USER) endif @@ -46,25 +46,38 @@ all: default default: $(TARGETS) clean: - rm -rf $(TARGETS) $(DIST_DIR) $(DIST_TARBALL) rpmbuild + @echo -n "Cleaning build files ............................... " + @rm -rf $(TARGETS) $(DIST_DIR) $(DIST_TARBALL) rpmbuild + @echo "DONE." conf_default.php: conf_default.php.in - sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebdir@|$(GWEB_STATEDIR)|g" conf_default.php.in > conf_default.php + @echo -n "Generating conf_default.php ........................ " + @sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebdir@|$(GWEB_STATEDIR)|g" conf_default.php.in > conf_default.php + @echo "DONE." conf_redirect.php: conf_redirect.php.in - sed -e "s|@etcdir@|$(GCONFDIR)|" conf_redirect.php.in > conf_redirect.php + @echo -n "Generating conf_redirect.php ....................... " + @sed -e "s|@etcdir@|$(GCONFDIR)|" conf_redirect.php.in > conf_redirect.php + @echo "DONE." ganglia-web.spec: ganglia-web.spec.in - sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ -e "s|@vargwebdir@|$(GWEB_STATEDIR)|" -e "s|@GDESTDIR@|$(GDESTDIR)|g" -e "s|@etcdir@|$(GCONFDIR)|g" ganglia-web.spec.in > ganglia-web.spec + @echo -n "Generating ganglia-web.spec ........................ " + @sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ -e "s|@vargwebdir@|$(GWEB_STATEDIR)|" -e "s|@GDESTDIR@|$(GDESTDIR)|g" -e "s|@etcdir@|$(GCONFDIR)|g" ganglia-web.spec.in > ganglia-web.spec + @echo "DONE." version.php: version.php.in - sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ version.php.in > version.php + @echo -n "Generating version.php ............................. " + @sed -e s/@GWEB_VERSION@/$(GWEB_VERSION)/ version.php.in > version.php + @echo "DONE." apache.conf: apache.conf.in - sed -e "s|@GDESTDIR@|$(GDESTDIR)|g" apache.conf.in > apache.conf + @echo -n "Generating apache.conf ............................. " + @sed -e "s|@GDESTDIR@|$(GDESTDIR)|g" apache.conf.in > apache.conf + @echo "DONE." dist-dir: default - rsync --exclude "rpmbuild" \ + @echo -n "Filling dist dir ................................... " + @rsync --exclude "rpmbuild" \ --exclude "debian/ganglia-webfrontend" \ --exclude "*.gz" \ --exclude "$(DIST_DIR)" \ @@ -76,52 +89,72 @@ dist-dir: default --exclude "*~" \ --exclude "#*#" \ -a . $(DIST_DIR) - -install: dist-dir - # Create dwoo sharedstattedir tree - mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/compiled - mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/cache - mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/filters - # Install ganglia-webfrontend low level conf - rsync -a $(DIST_DIR)/conf $(DESTDIR)/$(GWEB_STATEDIR) - # Set apache daemon ownership to the sharedstattedir files. - chown -R $(APACHE_USER):$(APACHE_GROUP) $(DESTDIR)/$(GWEB_STATEDIR) - # Install php files - mkdir -p $(DESTDIR)/$(GDESTDIR) - rsync --exclude "conf" \ + @echo "DONE." + +install: install-files + @echo -n "Setting ownership to the sharedstattedir files ..... " + @chown -R $(APACHE_USER):$(APACHE_GROUP) $(DESTDIR)/$(GWEB_STATEDIR) + @echo "DONE." + + +install-files: dist-dir + @echo -n "Creating dwoo sharedstattedir tree ................. " + @mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/compiled + @mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/dwoo/cache + @mkdir -p $(DESTDIR)/$(GWEB_STATEDIR)/filters + @echo "DONE." + @echo -n "Installing ganglia-webfrontend low level conf ...... " + @rsync -a $(DIST_DIR)/conf $(DESTDIR)/$(GWEB_STATEDIR) + @echo "DONE." + @echo -n "Installing php files ............................... " + @mkdir -p $(DESTDIR)/$(GDESTDIR) + @rsync --exclude "conf" \ --exclude conf_redirect.php \ --exclude "*.in" \ --exclude "*.spec" \ -a $(DIST_DIR)/* $(DESTDIR)/$(GDESTDIR) - # Intall file that redirect conf.php to /etc/ganglia-webfrontend - # so it can be seen as a config file under debian packages. - # Do the same for all distro to avoid specific case for debian. - cp -f conf_redirect.php $(DESTDIR)/$(GDESTDIR)/conf.php - # Install the generated conf_default.php file. - cp -f conf_default.php $(DESTDIR)/$(GDESTDIR)/conf_default.php - # Create the /etc/ganglia-webfrontend directory. - mkdir -p $(DESTDIR)/$(GCONFDIR) - cp -f apache.conf $(DESTDIR)/$(GCONFDIR)/ - # Install the editable copy of conf_default.php - cp -f conf_default.php $(DESTDIR)/$(GCONFDIR)/conf.php + @echo "DONE." + @echo -n "Intalling redirect conf.php ........................ " + @# so it can be seen as a config file under debian packages. + @# Do the same for all distro to avoid specific case for debian. + @cp -f conf_redirect.php $(DESTDIR)/$(GDESTDIR)/conf.php + @echo "DONE." + @echo -n "Installing the generated conf_default.php file ..... " + @cp -f conf_default.php $(DESTDIR)/$(GDESTDIR)/conf_default.php + @echo "DONE." + @echo -n "Creating the etc/ganglia-webfrontend directory ..... " + @mkdir -p $(DESTDIR)/$(GCONFDIR) + @echo "DONE." + @echo -n "Installing apache.conf ............................. " + @cp -f apache.conf $(DESTDIR)/$(GCONFDIR)/ + @echo "DONE." + @echo -n "Installing the editable copy of conf_default.php ... " + @cp -f conf_default.php $(DESTDIR)/$(GCONFDIR)/conf.php + @echo "DONE." dist-gzip: dist-dir $(DIST_GZIP_TARGETS) - if [ -f $(DIST_TARBALL) ]; then \ - rm -rf $(DIST_TARBALL) ;\ - fi ;\ - cp -pf $(DIST_GZIP_TARGETS) $(DIST_DIR) - tar -czf $(DIST_TARBALL) $(DIST_DIR)/* + @echo -n "Creating tarball ................................... " + @if [ -f $(DIST_TARBALL) ]; then \ + rm -rf $(DIST_TARBALL) ;\ + fi + @cp -pf $(DIST_GZIP_TARGETS) $(DIST_DIR) + @tar -czf $(DIST_TARBALL) $(DIST_DIR)/* + @echo "DONE." 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 - ln -s $(DIST_TARBALL) rpmbuild/SOURCES - rpmbuild --define '_topdir $(PWD)/rpmbuild' --with web_prefixdir=$(GDESTDIR) -bb ganglia-web.spec + @echo -n "Creating binary rpm ................................ " + @rm -rf rpmbuild + @mkdir rpmbuild + @mkdir rpmbuild/SOURCES + @mkdir rpmbuild/BUILD + @mkdir rpmbuild/RPMS + @mkdir rpmbuild/SRPMS + @ln -s ../../$(DIST_TARBALL) rpmbuild/SOURCES/$(DIST_TARBALL) + @rpmbuild --define '_topdir $(PWD)/rpmbuild' --define 'web_prefixdir $(GDESTDIR)' -bb ganglia-web.spec + @echo "DONE." uninstall: + @echo -n "Uninstalling ganglia-web. (conf files untouched) ... " rm -rf $(DESTDIR)/$(GDESTDIR) $(DESTDIR)/$(GWEB_STATEDIR) + @echo "DONE." diff --git a/ganglia-web.spec.in b/ganglia-web.spec.in index e55bae53..873df81c 100644 --- a/ganglia-web.spec.in +++ b/ganglia-web.spec.in @@ -1,32 +1,24 @@ # The following options are supported: -# --with httpd_user= # defaults to: apache -# --with httpd_group= # defaults to: apache -#  --with web_prefixdir= # defaults to: @GDESTDIR@ -#  --with gweb_statedir= # defaults to: @vargwebdir@ -#  --with gweb_confdir= # defaults to: @etcdir@ +# --define 'httpd_user ' # defaults to: apache +# --define 'httpd_group ' # defaults to: apache +#  --define 'web_prefixdir ' # defaults to: @GDESTDIR@ +#  --define 'gweb_statedir ' # defaults to: @vargwebdir@ +#  --define 'gweb_confdir ' # defaults to: @etcdir@ -# example: rpmbuild -tb ganglia-web-3.5.12.tar.gz --with httpd_user=www-data --with httpd_group=www-data --with web_prefixdir=/srv/www/ganglia +# example: rpmbuild -tb ganglia-web-3.5.12.tar.gz --define 'httpd_user www-data' --define 'httpd_group www-data' --define 'web_prefixdir /srv/www/ganglia' -# Default value for web_prefixdir depending on distro. +# Default values for Above variables. %if 0%{?suse_version} -%define web_prefixdir /srv/www/htdocs/ganglia +%{!?web_prefixdir: %define web_prefixdir /srv/www/htdocs/ganglia} %else -%define web_prefixdir @GDESTDIR@ +%{!?web_prefixdir: %define web_prefixdir /usr/share/ganglia-webfrontend} %endif -# Default value for httpd user and group. -%define httpd_user apache -%define httpd_group apache -%define gweb_statedir @vargwebdir@ -%define gweb_confdir @etcdir@ - -# Read the provided --with tags if any (overriding default values). -%{?_with_httpd_user:%define httpd_user %(set -- %{_with_httpd_user}; echo $2 | cut -d= -f2)} -%{?_with_httpd_group:%define httpd_group %(set -- %{_with_httpd_group}; echo $2 | cut -d= -f2)} -%{?_with_web_prefixdir:%define web_prefixdir %(set -- %{_with_web_prefixdir}; echo $2 | cut -d= -f2)} -%{?_with_gweb_statedir:%define gweb_statedir %(set -- %{_with_gweb_statedir}; echo $2 | cut -d= -f2)} -%{?_with_gweb_confdir:%define gweb_confdir %(set -- %{_with_gweb_confdir}; echo $2 | cut -d= -f2)} +%{!?httpd_user: %define httpd_user apache} +%{!?httpd_group: %define httpd_group apache} +%{!?gweb_statedir: %define gweb_statedir @vargwebdir@} +%{!?gweb_confdir: %define gweb_confdir @etcdir@} Summary: Ganglia Web Frontend Name: ganglia-web @@ -48,7 +40,7 @@ ganglia, and to provide historical graphs of collected metrics. This website is written in the PHP5 language and uses the Dwoo templating engine. %prep -%setup -n %{name}-%{version} +%setup -q -n %{name}-%{version} %build # Update all needed files @@ -62,26 +54,13 @@ written in the PHP5 language and uses the Dwoo templating engine. # Flush any old RPM build root %__rm -rf $RPM_BUILD_ROOT -%__make install GDESTDIR=%{web_prefixdir} \ +%__make install-files GDESTDIR=%{web_prefixdir} \ APACHE_USER=%{httpd_user} \ APACHE_GROUP=%{httpd_group} \ GWEB_STATEDIR=%{gweb_statedir} \ GCONFDIR=%{gweb_confdir} \ DESTDIR=$RPM_BUILD_ROOT -#%__mkdir -p $RPM_BUILD_ROOT/%{web_prefixdir} -#%__cp -rf * $RPM_BUILD_ROOT/%{web_prefixdir} -#%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/conf -#%__rm -rf $RPM_BUILD_ROOT/%{web_prefixdir}/apache.conf -#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/filters -#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/conf -#%__cp -rf conf/* $RPM_BUILD_ROOT@vargwebdir@/conf -#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo -#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/compiled -#%__install -d -m 0755 $RPM_BUILD_ROOT@vargwebdir@/dwoo/cache -#%__install -d -m 0755 $RPM_BUILD_ROOT@etcdir@ -#%__cp -f apache.conf $RPM_BUILD_ROOT@etcdir@/ - %files %defattr(-,root,root) %dir %attr(0755,%{httpd_user},%{httpd_group})%{gweb_statedir} @@ -116,12 +95,10 @@ fi %changelog * Fri Feb 28 2014 Olivier Lahaye -- Added the ability to change Makefile variables using --with switch for the +- Added the ability to change Makefile variables using --define switch for the following variables: httpd_user, httpd_group, web_prefixdir, gweb_confdir, gweb_statedir -- Reworked the web_prefixdir setup from the rpmbuild command line. Moved from - --define '%custom_web_prefixdir ' to --with web_prefixdir= - Also fixed a bug that prevented to fix the web_prefixdir on SuSE Linux. +- Also fixed a bug that prevented to fix the web_prefixdir on SuSE Linux. * Tue Jun 04 2013 Wesley Hirsch - Added default apache configuration * Thu Mar 17 2011 Bernard Li From 08beb2b6e279c335a0e20bab2340c68a117386f2 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Fri, 22 Aug 2014 15:12:54 +0000 Subject: [PATCH 08/35] Added the capability to control the timezone used to display rrd metric graphs and reports. This is accomplished by specifying a tz query parameter to the ganaglia-web url. The timezone parameter takes the standard Linux form, e.g. tz=UTC, tz=America/New_York --- events.php | 18 ++++++++++++++++-- graph.php | 8 +++++++- header.php | 56 +++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/events.php b/events.php index f8f9432d..acd157b6 100644 --- a/events.php +++ b/events.php @@ -76,10 +76,24 @@ function refreshOverlayEvent() { center: '', right: 'today prev next basicDay basicWeek month'}, eventRender: function(event, element) { + var fmt = "HH:mm:ss"; + + if ('end_time' in event) { + var startDate = new Date(event.start_time * 1000); + var endDate = new Date(event.end_time * 1000); + if (startDate.getFullYear() != endDate.getFullYear()) { + fmt = "MM/dd/yyyy " + fmt; + } else { + if ((startDate.getDate() != endDate.getDate()) || + (startDate.getMonth() != endDate.getMonth())) + fmt = "MM/dd " + fmt; + } + } + var tipText = event.title + - '
Start: ' + $.fullCalendar.formatDate(new Date(event.start_time * 1000), "HH:mm:ss"); + '
Start: ' + $.fullCalendar.formatDate(new Date(event.start_time * 1000), fmt); if ('end_time' in event) - tipText += '
End: ' + $.fullCalendar.formatDate(new Date(event.end_time * 1000), "HH:mm:ss"); + tipText += '
End: ' + $.fullCalendar.formatDate(new Date(event.end_time * 1000), fmt); element.qtip({ content : { diff --git a/graph.php b/graph.php index 0a6383c7..4f850829 100644 --- a/graph.php +++ b/graph.php @@ -1,6 +1,8 @@ getOffset($origin_dt) - + $remote_dtz->getOffset($remote_dt); + return $offset; +} + function make_size_menu($clustergraphsize, $context) { global $conf; @@ -323,24 +349,28 @@ function make_node_menu($self, $start_timestamp = null; $end_timestamp = null; if ($cs) { - if (! is_numeric($cs)) { - $start_timestamp = strtotime($cs); - } else { - $start_timestamp = $cs; - } + if (! is_numeric($cs)) { + $start_timestamp = strtotime($cs); + } else { + $start_timestamp = $cs; + } - if ($ce) { - if (! is_numeric($ce)) { - $end_timestamp = strtotime($ce); - } else { - $end_timestamp = $ce; - } + if ($ce) { + if (! is_numeric($ce)) { + $end_timestamp = strtotime($ce); } else { - $end_timestamp = $start_timestamp - $conf['time_ranges'][$range]; + $end_timestamp = $ce; } + } else { + $end_timestamp = $start_timestamp - $conf['time_ranges'][$range]; + } } else { + if (isset($_SESSION['tz'])) { + $end_timestamp = time() - get_timezone_offset($_SESSION['tz'], null); + } else { $end_timestamp = time(); - $start_timestamp = $end_timestamp - $conf['time_ranges'][$range]; + } + $start_timestamp = $end_timestamp - $conf['time_ranges'][$range]; } $data->assign("start_timestamp", $start_timestamp); From dd378f0d5b961c8502933be42bb8a6f4821b1215 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Mon, 25 Aug 2014 21:59:32 +0000 Subject: [PATCH 09/35] Use moment for date/time manipulation in Javascript Use jstz to determine browser timezone. In the future my hope is that we can do this with the moment library. --- js/jstz-1.0.4.min.js | 2 ++ js/moment-timezone-with-data.min.js | 7 +++++++ js/moment.min.js | 6 ++++++ 3 files changed, 15 insertions(+) create mode 100644 js/jstz-1.0.4.min.js create mode 100644 js/moment-timezone-with-data.min.js create mode 100644 js/moment.min.js diff --git a/js/jstz-1.0.4.min.js b/js/jstz-1.0.4.min.js new file mode 100644 index 00000000..96e3dd8a --- /dev/null +++ b/js/jstz-1.0.4.min.js @@ -0,0 +1,2 @@ +/*! jstz - v1.0.4 - 2012-12-12 */ +(function(e){var t=function(){"use strict";var e="s",n=function(e){var t=-e.getTimezoneOffset();return t!==null?t:0},r=function(e,t,n){var r=new Date;return e!==undefined&&r.setFullYear(e),r.setDate(n),r.setMonth(t),r},i=function(e){return n(r(e,0,2))},s=function(e){return n(r(e,5,2))},o=function(e){var t=e.getMonth()>7?s(e.getFullYear()):i(e.getFullYear()),r=n(e);return t-r!==0},u=function(){var t=i(),n=s(),r=i()-s();return r<0?t+",1":r>0?n+",1,"+e:t+",0"},a=function(){var e=u();return new t.TimeZone(t.olson.timezones[e])};return{determine:a,date_is_dst:o}}();t.TimeZone=function(e){"use strict";var n=null,r=function(){return n},i=function(){var e=t.olson.ambiguity_list[n],r=e.length,i=0,s=e[0];for(;i96?a-87:a>64?a-29:a-48}function c(a){var c,d=0,e=a.split("."),f=e[0],g=e[1]||"",h=1,i=0,j=1;for(45===a.charCodeAt(0)&&(d=1,j=-1),d;dc;c++)a[c]=Math.round((a[c-1]||0)+6e4*a[c]);a[b-1]=1/0}function f(a,b){var c,d=[];for(c=0;ce;e++)if(b=g[e],c=g[e+1],d=g[e?e-1:e],c>b&&u.moveAmbiguousForward?b=c:b>d&&u.moveInvalidForward&&(b=d),f0)for(c in Hb)d=Hb[c],e=b[d],"undefined"!=typeof e&&(a[d]=e);return a}function o(a){return 0>a?Math.ceil(a):Math.floor(a)}function p(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&A(a[d])!==A(b[d]))&&g++;return g+f}function x(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=ic[a]||jc[b]||b}return a}function y(a){var b,d,e={};for(d in a)c(a,d)&&(b=x(d),b&&(e[b]=a[d]));return e}function z(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}sb[b]=function(e,f){var g,h,i=sb._locale[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=sb().utc().set(d,a);return i.call(sb._locale,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function A(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function B(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function C(a,b,c){return gb(sb([a,11,31+b-c]),b,c).week}function D(a){return E(a)?366:365}function E(a){return a%4===0&&a%100!==0||a%400===0}function F(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[Ab]<0||a._a[Ab]>11?Ab:a._a[Bb]<1||a._a[Bb]>B(a._a[zb],a._a[Ab])?Bb:a._a[Cb]<0||a._a[Cb]>23?Cb:a._a[Db]<0||a._a[Db]>59?Db:a._a[Eb]<0||a._a[Eb]>59?Eb:a._a[Fb]<0||a._a[Fb]>999?Fb:-1,a._pf._overflowDayOfYear&&(zb>b||b>Bb)&&(b=Bb),a._pf.overflow=b)}function G(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function H(a){return a?a.toLowerCase().replace("_","-"):a}function I(a){for(var b,c,d,e,f=0;f0;){if(d=J(e.slice(0,b).join("-")))return d;if(c&&c.length>=b&&w(e,c,!0)>=b-1)break;b--}f++}return null}function J(a){var b=null;if(!Gb[a]&&Ib)try{b=sb.locale(),require("./locale/"+a),sb.locale(b)}catch(c){}return Gb[a]}function K(a,b){return b._isUTC?sb(a).zone(b._offset||0):sb(a).local()}function L(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function M(a){var b,c,d=a.match(Mb);for(b=0,c=d.length;c>b;b++)d[b]=oc[d[b]]?oc[d[b]]:L(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function N(a,b){return a.isValid()?(b=O(b,a.localeData()),kc[b]||(kc[b]=M(b)),kc[b](a)):a.localeData().invalidDate()}function O(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(Nb.lastIndex=0;d>=0&&Nb.test(a);)a=a.replace(Nb,c),Nb.lastIndex=0,d-=1;return a}function P(a,b){var c,d=b._strict;switch(a){case"Q":return Yb;case"DDDD":return $b;case"YYYY":case"GGGG":case"gggg":return d?_b:Qb;case"Y":case"G":case"g":return bc;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?ac:Rb;case"S":if(d)return Yb;case"SS":if(d)return Zb;case"SSS":if(d)return $b;case"DDD":return Pb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Tb;case"a":case"A":return b._locale._meridiemParse;case"X":return Wb;case"Z":case"ZZ":return Ub;case"T":return Vb;case"SSSS":return Sb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Zb:Ob;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return Ob;case"Do":return Xb;default:return c=new RegExp(Y(X(a.replace("\\","")),"i"))}}function Q(a){a=a||"";var b=a.match(Ub)||[],c=b[b.length-1]||[],d=(c+"").match(gc)||["-",0,0],e=+(60*d[1])+A(d[2]);return"+"===d[0]?-e:e}function R(a,b,c){var d,e=c._a;switch(a){case"Q":null!=b&&(e[Ab]=3*(A(b)-1));break;case"M":case"MM":null!=b&&(e[Ab]=A(b)-1);break;case"MMM":case"MMMM":d=c._locale.monthsParse(b),null!=d?e[Ab]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[Bb]=A(b));break;case"Do":null!=b&&(e[Bb]=A(parseInt(b,10)));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=A(b));break;case"YY":e[zb]=sb.parseTwoDigitYear(b);break;case"YYYY":case"YYYYY":case"YYYYYY":e[zb]=A(b);break;case"a":case"A":c._isPm=c._locale.isPM(b);break;case"H":case"HH":case"h":case"hh":e[Cb]=A(b);break;case"m":case"mm":e[Db]=A(b);break;case"s":case"ss":e[Eb]=A(b);break;case"S":case"SS":case"SSS":case"SSSS":e[Fb]=A(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=Q(b);break;case"dd":case"ddd":case"dddd":d=c._locale.weekdaysParse(b),null!=d?(c._w=c._w||{},c._w.d=d):c._pf.invalidWeekday=b;break;case"w":case"ww":case"W":case"WW":case"d":case"e":case"E":a=a.substr(0,1);case"gggg":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=A(b));break;case"gg":case"GG":c._w=c._w||{},c._w[a]=sb.parseTwoDigitYear(b)}}function S(a){var c,d,e,f,g,h,i;c=a._w,null!=c.GG||null!=c.W||null!=c.E?(g=1,h=4,d=b(c.GG,a._a[zb],gb(sb(),1,4).year),e=b(c.W,1),f=b(c.E,1)):(g=a._locale._week.dow,h=a._locale._week.doy,d=b(c.gg,a._a[zb],gb(sb(),g,h).year),e=b(c.w,1),null!=c.d?(f=c.d,g>f&&++e):f=null!=c.e?c.e+g:g),i=hb(d,e,f,h,g),a._a[zb]=i.year,a._dayOfYear=i.dayOfYear}function T(a){var c,d,e,f,g=[];if(!a._d){for(e=V(a),a._w&&null==a._a[Bb]&&null==a._a[Ab]&&S(a),a._dayOfYear&&(f=b(a._a[zb],e[zb]),a._dayOfYear>D(f)&&(a._pf._overflowDayOfYear=!0),d=cb(f,0,a._dayOfYear),a._a[Ab]=d.getUTCMonth(),a._a[Bb]=d.getUTCDate()),c=0;3>c&&null==a._a[c];++c)a._a[c]=g[c]=e[c];for(;7>c;c++)a._a[c]=g[c]=null==a._a[c]?2===c?1:0:a._a[c];a._d=(a._useUTC?cb:bb).apply(null,g),null!=a._tzm&&a._d.setUTCMinutes(a._d.getUTCMinutes()+a._tzm)}}function U(a){var b;a._d||(b=y(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],T(a))}function V(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function W(a){if(a._f===sb.ISO_8601)return void $(a);a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=""+a._i,h=g.length,i=0;for(d=O(a._f,a._locale).match(Mb)||[],b=0;b0&&a._pf.unusedInput.push(f),g=g.slice(g.indexOf(c)+c.length),i+=c.length),oc[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),R(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=h-i,g.length>0&&a._pf.unusedInput.push(g),a._isPm&&a._a[Cb]<12&&(a._a[Cb]+=12),a._isPm===!1&&12===a._a[Cb]&&(a._a[Cb]=0),T(a),F(a)}function X(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function Y(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Z(a){var b,c,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,void(a._d=new Date(0/0));for(f=0;fg)&&(e=g,c=b));m(a,c||b)}function $(a){var b,c,d=a._i,e=cc.exec(d);if(e){for(a._pf.iso=!0,b=0,c=ec.length;c>b;b++)if(ec[b][1].exec(d)){a._f=ec[b][0]+(e[6]||" ");break}for(b=0,c=fc.length;c>b;b++)if(fc[b][1].exec(d)){a._f+=fc[b][0];break}d.match(Ub)&&(a._f+="Z"),W(a)}else a._isValid=!1}function _(a){$(a),a._isValid===!1&&(delete a._isValid,sb.createFromInputFallback(a))}function ab(b){var c,d=b._i;d===a?b._d=new Date:v(d)?b._d=new Date(+d):null!==(c=Jb.exec(d))?b._d=new Date(+c[1]):"string"==typeof d?_(b):u(d)?(b._a=d.slice(0),T(b)):"object"==typeof d?U(b):"number"==typeof d?b._d=new Date(d):sb.createFromInputFallback(b)}function bb(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function cb(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function db(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function eb(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function fb(a,b,c){var d=sb.duration(a).abs(),e=xb(d.as("s")),f=xb(d.as("m")),g=xb(d.as("h")),h=xb(d.as("d")),i=xb(d.as("M")),j=xb(d.as("y")),k=e0,k[4]=c,eb.apply({},k)}function gb(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=sb(a).add(f,"d"),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function hb(a,b,c,d,e){var f,g,h=cb(a,0,1).getUTCDay();return h=0===h?7:h,c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:D(a-1)+g}}function ib(b){var c=b._i,d=b._f;return b._locale=b._locale||sb.localeData(b._l),null===c||d===a&&""===c?sb.invalid({nullInput:!0}):("string"==typeof c&&(b._i=c=b._locale.preparse(c)),sb.isMoment(c)?new k(c,!0):(d?u(d)?Z(b):W(b):ab(b),new k(b)))}function jb(a,b){var c,d;if(1===b.length&&u(b[0])&&(b=b[0]),!b.length)return sb();for(c=b[0],d=1;d=0?"+":"-";return b+p(Math.abs(a),6)},gg:function(){return p(this.weekYear()%100,2)},gggg:function(){return p(this.weekYear(),4)},ggggg:function(){return p(this.weekYear(),5)},GG:function(){return p(this.isoWeekYear()%100,2)},GGGG:function(){return p(this.isoWeekYear(),4)},GGGGG:function(){return p(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.localeData().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return A(this.milliseconds()/100)},SS:function(){return p(A(this.milliseconds()/10),2)},SSS:function(){return p(this.milliseconds(),3)},SSSS:function(){return p(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+":"+p(A(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+p(A(a/60),2)+p(A(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},pc={},qc=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];mc.length;)ub=mc.pop(),oc[ub+"o"]=i(oc[ub],ub);for(;nc.length;)ub=nc.pop(),oc[ub+ub]=h(oc[ub],2);oc.DDDD=h(oc.DDD,3),m(j.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=sb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=sb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY LT",LLLL:"dddd, MMMM D, YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return gb(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),sb=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=b,g._f=c,g._l=e,g._strict=f,g._isUTC=!1,g._pf=d(),ib(g)},sb.suppressDeprecationWarnings=!1,sb.createFromInputFallback=f("moment construction falls back to js Date. This is discouraged and will be removed in upcoming major release. Please refer to https://github.com/moment/moment/issues/1407 for more info.",function(a){a._d=new Date(a._i)}),sb.min=function(){var a=[].slice.call(arguments,0);return jb("isBefore",a)},sb.max=function(){var a=[].slice.call(arguments,0);return jb("isAfter",a)},sb.utc=function(b,c,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=b,g._f=c,g._strict=f,g._pf=d(),ib(g).utc()},sb.unix=function(a){return sb(1e3*a)},sb.duration=function(a,b){var d,e,f,g,h=a,i=null;return sb.isDuration(a)?h={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(h={},b?h[b]=a:h.milliseconds=a):(i=Kb.exec(a))?(d="-"===i[1]?-1:1,h={y:0,d:A(i[Bb])*d,h:A(i[Cb])*d,m:A(i[Db])*d,s:A(i[Eb])*d,ms:A(i[Fb])*d}):(i=Lb.exec(a))?(d="-"===i[1]?-1:1,f=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*d},h={y:f(i[2]),M:f(i[3]),d:f(i[4]),h:f(i[5]),m:f(i[6]),s:f(i[7]),w:f(i[8])}):"object"==typeof h&&("from"in h||"to"in h)&&(g=r(sb(h.from),sb(h.to)),h={},h.ms=g.milliseconds,h.M=g.months),e=new l(h),sb.isDuration(a)&&c(a,"_locale")&&(e._locale=a._locale),e},sb.version=vb,sb.defaultFormat=dc,sb.ISO_8601=function(){},sb.momentProperties=Hb,sb.updateOffset=function(){},sb.relativeTimeThreshold=function(b,c){return lc[b]===a?!1:c===a?lc[b]:(lc[b]=c,!0)},sb.lang=f("moment.lang is deprecated. Use moment.locale instead.",function(a,b){return sb.locale(a,b)}),sb.locale=function(a,b){var c;return a&&(c="undefined"!=typeof b?sb.defineLocale(a,b):sb.localeData(a),c&&(sb.duration._locale=sb._locale=c)),sb._locale._abbr},sb.defineLocale=function(a,b){return null!==b?(b.abbr=a,Gb[a]||(Gb[a]=new j),Gb[a].set(b),sb.locale(a),Gb[a]):(delete Gb[a],null)},sb.langData=f("moment.langData is deprecated. Use moment.localeData instead.",function(a){return sb.localeData(a)}),sb.localeData=function(a){var b;if(a&&a._locale&&a._locale._abbr&&(a=a._locale._abbr),!a)return sb._locale;if(!u(a)){if(b=J(a))return b;a=[a]}return I(a)},sb.isMoment=function(a){return a instanceof k||null!=a&&c(a,"_isAMomentObject")},sb.isDuration=function(a){return a instanceof l};for(ub=qc.length-1;ub>=0;--ub)z(qc[ub]);sb.normalizeUnits=function(a){return x(a)},sb.invalid=function(a){var b=sb.utc(0/0);return null!=a?m(b._pf,a):b._pf.userInvalidated=!0,b},sb.parseZone=function(){return sb.apply(null,arguments).parseZone()},sb.parseTwoDigitYear=function(a){return A(a)+(A(a)>68?1900:2e3)},m(sb.fn=k.prototype,{clone:function(){return sb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=sb(this).utc();return 00:!1},parsingFlags:function(){return m({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(a){return this.zone(0,a)},local:function(a){return this._isUTC&&(this.zone(0,a),this._isUTC=!1,a&&this.add(this._d.getTimezoneOffset(),"m")),this},format:function(a){var b=N(this,a||sb.defaultFormat);return this.localeData().postformat(b)},add:s(1,"add"),subtract:s(-1,"subtract"),diff:function(a,b,c){var d,e,f=K(a,this),g=6e4*(this.zone()-f.zone());return b=x(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-sb(this).startOf("month")-(f-sb(f).startOf("month")))/d,e-=6e4*(this.zone()-sb(this).startOf("month").zone()-(f.zone()-sb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:o(e)},from:function(a,b){return sb.duration({to:this,from:a}).locale(this.locale()).humanize(!b)},fromNow:function(a){return this.from(sb(),a)},calendar:function(a){var b=a||sb(),c=K(b,this).startOf("day"),d=this.diff(c,"days",!0),e=-6>d?"sameElse":-1>d?"lastWeek":0>d?"lastDay":1>d?"sameDay":2>d?"nextDay":7>d?"nextWeek":"sameElse";return this.format(this.localeData().calendar(e,this))},isLeapYear:function(){return E(this.year())},isDST:function(){return this.zone()+sb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+sb(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+K(a,this).startOf(b)},min:f("moment().min is deprecated, use moment.min instead. https://github.com/moment/moment/issues/1548",function(a){return a=sb.apply(null,arguments),this>a?this:a}),max:f("moment().max is deprecated, use moment.max instead. https://github.com/moment/moment/issues/1548",function(a){return a=sb.apply(null,arguments),a>this?this:a}),zone:function(a,b){var c,d=this._offset||0;return null==a?this._isUTC?d:this._d.getTimezoneOffset():("string"==typeof a&&(a=Q(a)),Math.abs(a)<16&&(a=60*a),!this._isUTC&&b&&(c=this._d.getTimezoneOffset()),this._offset=a,this._isUTC=!0,null!=c&&this.subtract(c,"m"),d!==a&&(!b||this._changeInProgress?t(this,sb.duration(d-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,sb.updateOffset(this,!0),this._changeInProgress=null)),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?sb(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return B(this.year(),this.month())},dayOfYear:function(a){var b=xb((sb(this).startOf("day")-sb(this).startOf("year"))/864e5)+1;return null==a?b:this.add(a-b,"d")},quarter:function(a){return null==a?Math.ceil((this.month()+1)/3):this.month(3*(a-1)+this.month()%3)},weekYear:function(a){var b=gb(this,this.localeData()._week.dow,this.localeData()._week.doy).year;return null==a?b:this.add(a-b,"y")},isoWeekYear:function(a){var b=gb(this,1,4).year;return null==a?b:this.add(a-b,"y")},week:function(a){var b=this.localeData().week(this);return null==a?b:this.add(7*(a-b),"d")},isoWeek:function(a){var b=gb(this,1,4).week;return null==a?b:this.add(7*(a-b),"d")},weekday:function(a){var b=(this.day()+7-this.localeData()._week.dow)%7;return null==a?b:this.add(a-b,"d")},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},isoWeeksInYear:function(){return C(this.year(),1,4)},weeksInYear:function(){var a=this.localeData()._week;return C(this.year(),a.dow,a.doy)},get:function(a){return a=x(a),this[a]()},set:function(a,b){return a=x(a),"function"==typeof this[a]&&this[a](b),this},locale:function(b){return b===a?this._locale._abbr:(this._locale=sb.localeData(b),this)},lang:f("moment().lang() is deprecated. Use moment().localeData() instead.",function(b){return b===a?this.localeData():(this._locale=sb.localeData(b),this)}),localeData:function(){return this._locale}}),sb.fn.millisecond=sb.fn.milliseconds=nb("Milliseconds",!1),sb.fn.second=sb.fn.seconds=nb("Seconds",!1),sb.fn.minute=sb.fn.minutes=nb("Minutes",!1),sb.fn.hour=sb.fn.hours=nb("Hours",!0),sb.fn.date=nb("Date",!0),sb.fn.dates=f("dates accessor is deprecated. Use date instead.",nb("Date",!0)),sb.fn.year=nb("FullYear",!0),sb.fn.years=f("years accessor is deprecated. Use year instead.",nb("FullYear",!0)),sb.fn.days=sb.fn.day,sb.fn.months=sb.fn.month,sb.fn.weeks=sb.fn.week,sb.fn.isoWeeks=sb.fn.isoWeek,sb.fn.quarters=sb.fn.quarter,sb.fn.toJSON=sb.fn.toISOString,m(sb.duration.fn=l.prototype,{_bubble:function(){var a,b,c,d=this._milliseconds,e=this._days,f=this._months,g=this._data,h=0;g.milliseconds=d%1e3,a=o(d/1e3),g.seconds=a%60,b=o(a/60),g.minutes=b%60,c=o(b/60),g.hours=c%24,e+=o(c/24),h=o(ob(e)),e-=o(pb(h)),f+=o(e/30),e%=30,h+=o(f/12),f%=12,g.days=e,g.months=f,g.years=h},abs:function(){return this._milliseconds=Math.abs(this._milliseconds),this._days=Math.abs(this._days),this._months=Math.abs(this._months),this._data.milliseconds=Math.abs(this._data.milliseconds),this._data.seconds=Math.abs(this._data.seconds),this._data.minutes=Math.abs(this._data.minutes),this._data.hours=Math.abs(this._data.hours),this._data.months=Math.abs(this._data.months),this._data.years=Math.abs(this._data.years),this},weeks:function(){return o(this.days()/7)},valueOf:function(){return this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*A(this._months/12)},humanize:function(a){var b=fb(this,!a,this.localeData());return a&&(b=this.localeData().pastFuture(+this,b)),this.localeData().postformat(b)},add:function(a,b){var c=sb.duration(a,b);return this._milliseconds+=c._milliseconds,this._days+=c._days,this._months+=c._months,this._bubble(),this},subtract:function(a,b){var c=sb.duration(a,b);return this._milliseconds-=c._milliseconds,this._days-=c._days,this._months-=c._months,this._bubble(),this},get:function(a){return a=x(a),this[a.toLowerCase()+"s"]()},as:function(a){var b,c;if(a=x(a),b=this._days+this._milliseconds/864e5,"month"===a||"year"===a)return c=this._months+12*ob(b),"month"===a?c:c/12;switch(b+=pb(this._months/12),a){case"week":return b/7;case"day":return b;case"hour":return 24*b;case"minute":return 24*b*60;case"second":return 24*b*60*60;case"millisecond":return 24*b*60*60*1e3;default:throw new Error("Unknown unit "+a)}},lang:sb.fn.lang,locale:sb.fn.locale,toIsoString:f("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",function(){return this.toISOString()}),toISOString:function(){var a=Math.abs(this.years()),b=Math.abs(this.months()),c=Math.abs(this.days()),d=Math.abs(this.hours()),e=Math.abs(this.minutes()),f=Math.abs(this.seconds()+this.milliseconds()/1e3);return this.asSeconds()?(this.asSeconds()<0?"-":"")+"P"+(a?a+"Y":"")+(b?b+"M":"")+(c?c+"D":"")+(d||e||f?"T":"")+(d?d+"H":"")+(e?e+"M":"")+(f?f+"S":""):"P0D"},localeData:function(){return this._locale}}),sb.duration.fn.toString=sb.duration.fn.toISOString;for(ub in hc)c(hc,ub)&&qb(ub.toLowerCase());sb.duration.fn.asMilliseconds=function(){return this.as("ms")},sb.duration.fn.asSeconds=function(){return this.as("s")},sb.duration.fn.asMinutes=function(){return this.as("m")},sb.duration.fn.asHours=function(){return this.as("h")},sb.duration.fn.asDays=function(){return this.as("d")},sb.duration.fn.asWeeks=function(){return this.as("weeks")},sb.duration.fn.asMonths=function(){return this.as("M")},sb.duration.fn.asYears=function(){return this.as("y")},sb.locale("en",{ordinal:function(a){var b=a%10,c=1===A(a%100/10)?"th":1===b?"st":2===b?"nd":3===b?"rd":"th";return a+c}}),Ib?module.exports=sb:"function"==typeof define&&define.amd?(define("moment",function(a,b,c){return c.config&&c.config()&&c.config().noGlobal===!0&&(wb.moment=tb),sb}),rb(!0)):rb()}).call(this); \ No newline at end of file From 118a94b02ec95c336b18259b1cd87f697144cd3f Mon Sep 17 00:00:00 2001 From: pcpiela Date: Mon, 25 Aug 2014 22:02:10 +0000 Subject: [PATCH 10/35] Add a timezone-picker to the time manipulation toolbar. Some cleanup work in the area of date/time manipulation. --- functions.php | 10 ++++ graph.php | 13 ++--- header.php | 109 ++++++++++++++++++----------------- templates/default/header.tpl | 78 +++++++++++++++---------- 4 files changed, 118 insertions(+), 92 deletions(-) diff --git a/functions.php b/functions.php index ff2195a0..9b382fde 100644 --- a/functions.php +++ b/functions.php @@ -1398,4 +1398,14 @@ function my_passthru($command) { unlink($tf); } +// Get timestamp of textual date/time specified relative to gweb timezone +function tzTimeToTimestamp($tzTime) { + if (isset($_SESSION['tz']) && ($_SESSION['tz'] != '')) { + $dtz = new DateTimeZone($_SESSION['tz']); + $dt = new DateTime($tzTime, $dtz); + return $dt->getTimestamp(); + } else { + return strtotime($tzTime); // server timezone + } +} ?> diff --git a/graph.php b/graph.php index 4f850829..71be93d8 100644 --- a/graph.php +++ b/graph.php @@ -503,11 +503,11 @@ function build_graphite_url($rrd_graphite_link, } } - if ($cs) - $start = date("H:i_Ymd", strtotime($cs)); + if ($cs) + $start = date("H:i_Ymd", tzTimeToTimestamp($cs)); if ($ce) - $end = date("H:i_Ymd", strtotime($ce)); + $end = date("H:i_Ymd", tzTimeToTimestamp($ce)); if ($max == 0) $max = ""; @@ -541,12 +541,7 @@ function get_timestamp($time) { } else if (is_numeric($time)) { $timestamp = $time; } else { - $t = strtotime($time); - if ($t !== FALSE) - $timestamp = $t; - else - error_log("get_timestamp: ". - "Unable to convert time ${time} to Unix timestamp"); + $timestamp = tzTimeToTimestamp($time); } return $timestamp; } diff --git a/header.php b/header.php index a29a9366..ae4d86f9 100644 --- a/header.php +++ b/header.php @@ -11,28 +11,6 @@ exit(0); } -/** Returns the offset from the origin timezone to the remote timezone, - * in seconds. -* @param $remote_tz; -* @param $origin_tz; If null the servers current timezone is used as the -* origin. -* @return int; -*/ -function get_timezone_offset($remote_tz, $origin_tz = null) { - if ($origin_tz === null) { - if (!is_string($origin_tz = date_default_timezone_get())) { - return false; // A UTC timestamp was returned -- bail out! - } - } - $origin_dtz = new DateTimeZone($origin_tz); - $remote_dtz = new DateTimeZone($remote_tz); - $origin_dt = new DateTime("now", $origin_dtz); - $remote_dt = new DateTime("now", $remote_dtz); - $offset = $origin_dtz->getOffset($origin_dt) - - $remote_dtz->getOffset($remote_dt); - return $offset; -} - function make_size_menu($clustergraphsize, $context) { global $conf; @@ -129,6 +107,30 @@ function make_range_menu($physical, $jobrange, $cs, $ce, $range) { return $range_menu; } +function make_custom_time_selector($cs, $ce) { + $examples = "Feb 27 2007 00:00, 2/27/2007, 27.2.2007, now -1 week," + . " -2 days, start + 1 hour, etc."; + + $custom_time = "or from \n"; + $custom_time .= "\n"; + return $custom_time; +} + +function make_timezone_picker() { + $picker = "Timezone: "; + $picker .= ''; + return $picker; +} + function make_alt_view($context, $clustername, $hostname, $get_metric_string) { global $conf; @@ -312,13 +314,17 @@ function make_node_menu($self, $tpl = new Dwoo_Template_File( template("$header.tpl") ); $data = new Dwoo_Data(); -if (isset($_GET["hide-hf"]) && filter_input(INPUT_GET, "hide-hf", FILTER_VALIDATE_BOOLEAN, array("flags" => FILTER_NULL_ON_FAILURE))) { +if (isset($_GET["hide-hf"]) && + filter_input(INPUT_GET, + "hide-hf", + FILTER_VALIDATE_BOOLEAN, + array("flags" => FILTER_NULL_ON_FAILURE))) { $data->assign("hide_header", true); } -// Server offset used in generating pretty dates and times when zooming -$data->assign("server_utc_offset", date('Z')); -// +// Server timezone used in generating pretty dates and times when zooming +$data->assign("server_timezone", date_default_timezone_get()); + $data->assign("page_title", $title); $data->assign("refresh", $conf['default_refresh']); @@ -340,24 +346,25 @@ function make_node_menu($self, $get_metric_string = "m={$user['metricname']}&r=$range&s=$sort_url&hc=${conf['hostcols']}&mc=${conf['metriccols']}"; if ($jobrange and $jobstart) - $get_metric_string .= "&jr=$jobrange&js=$jobstart"; + $get_metric_string .= "&jr=$jobrange&js=$jobstart"; if ($cs) - $get_metric_string .= "&cs=" . rawurlencode($cs); + $get_metric_string .= "&cs=" . rawurlencode($cs); if ($ce) - $get_metric_string .= "&ce=" . rawurlencode($ce); + $get_metric_string .= "&ce=" . rawurlencode($ce); +// Timestamps are used for graph zoom calculations $start_timestamp = null; $end_timestamp = null; if ($cs) { if (! is_numeric($cs)) { - $start_timestamp = strtotime($cs); + $start_timestamp = tzTimeToTimestamp($cs); } else { $start_timestamp = $cs; } if ($ce) { if (! is_numeric($ce)) { - $end_timestamp = strtotime($ce); + $end_timestamp = tzTimeToTimestamp($ce); } else { $end_timestamp = $ce; } @@ -365,11 +372,7 @@ function make_node_menu($self, $end_timestamp = $start_timestamp - $conf['time_ranges'][$range]; } } else { - if (isset($_SESSION['tz'])) { - $end_timestamp = time() - get_timezone_offset($_SESSION['tz'], null); - } else { - $end_timestamp = time(); - } + $end_timestamp = time(); $start_timestamp = $end_timestamp - $conf['time_ranges'][$range]; } @@ -428,7 +431,7 @@ function make_node_menu($self, } $custom_time = ""; - +$timezone_picker = ""; if (in_array($context, array ("meta", "cluster", "cluster-summary", @@ -436,26 +439,24 @@ function make_node_menu($self, "views", "decompose_graph", "compare_hosts"))) { - $examples = "Feb 27 2007 00:00, 2/27/2007, 27.2.2007, now -1 week," - . " -2 days, start + 1 hour, etc."; - $custom_time = "or from \n"; - $custom_time .= "\n"; -# $custom_time .= $calendar; - $data->assign("custom_time", $custom_time); - -# $tpl->assign("custom_time_head", $calendar_head); - $data->assign("custom_time_head", ""); + $custom_time = make_custom_time_selector($cs, $ce); + $timezone_picker = make_timezone_picker(); + + #$tpl->assign("custom_time_head", $calendar_head); + $data->assign("custom_time_head", ""); } else { - $data->assign("custom_time_head", ""); + $data->assign("custom_time_head", ""); } - $data->assign("custom_time", $custom_time); +$data->assign("timezone_picker", $timezone_picker); + +if (isset($_SESSION['tz']) && ($_SESSION['tz'] != '')) { + $data->assign("timezone_option", "browser"); + $data->assign("timezone_value", $_SESSION['tz']); +} else { + $data->assign("timezone_option", "server"); + $data->assign("timezone_value", ""); +} if($conf['auth_system'] == 'enabled') { $data->assign('auth_system_enabled', true); diff --git a/templates/default/header.tpl b/templates/default/header.tpl index 7722b495..c3c5202e 100644 --- a/templates/default/header.tpl +++ b/templates/default/header.tpl @@ -29,9 +29,13 @@ + + + + @@ -14,7 +14,7 @@ - + Ganglia:: {$page_title} - + @@ -13,7 +13,7 @@ - + From a3fd5b24a42f8394c024a5e4054e1f36b4714441 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Sun, 7 Sep 2014 10:41:33 -0400 Subject: [PATCH 17/35] Upgrade to version 1.4.13 of the jquery.scrollTo plugin --- js/jquery.scrollTo-1.4.2.js | 215 ------------------------------ js/jquery.scrollTo-1.4.3.1-min.js | 7 - js/jquery.scrollTo.js | 187 ++++++++++++++++++++++++++ js/jquery.scrollTo.min.js | 7 + templates/default/header.tpl | 2 +- 5 files changed, 195 insertions(+), 223 deletions(-) delete mode 100644 js/jquery.scrollTo-1.4.2.js delete mode 100644 js/jquery.scrollTo-1.4.3.1-min.js create mode 100644 js/jquery.scrollTo.js create mode 100644 js/jquery.scrollTo.min.js diff --git a/js/jquery.scrollTo-1.4.2.js b/js/jquery.scrollTo-1.4.2.js deleted file mode 100644 index eec31e19..00000000 --- a/js/jquery.scrollTo-1.4.2.js +++ /dev/null @@ -1,215 +0,0 @@ -/** - * 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 ); \ No newline at end of file diff --git a/js/jquery.scrollTo-1.4.3.1-min.js b/js/jquery.scrollTo-1.4.3.1-min.js deleted file mode 100644 index 8b5f447d..00000000 --- a/js/jquery.scrollTo-1.4.3.1-min.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 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); \ No newline at end of file diff --git a/js/jquery.scrollTo.js b/js/jquery.scrollTo.js new file mode 100644 index 00000000..fc59ee46 --- /dev/null +++ b/js/jquery.scrollTo.js @@ -0,0 +1,187 @@ +/*! + * jQuery.scrollTo + * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com + * Licensed under MIT + * http://flesler.blogspot.com/2007/10/jqueryscrollto.html + * @projectDescription Easy element scrolling using jQuery. + * @author Ariel Flesler + * @version 1.4.13 + */ +;(function (define) { + 'use strict'; + + define(['jquery'], function ($) { + + var $scrollTo = $.scrollTo = function( target, duration, settings ) { + return $(window).scrollTo( target, duration, settings ); + }; + + $scrollTo.defaults = { + axis:'xy', + duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1, + limit:true + }; + + // 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 /webkit/i.test(navigator.userAgent) || 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.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() { + // Null target yields nothing, just like jQuery does + if (target == null) return; + + 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/Absolute selector, no break! + targ = win ? $(targ) : $(targ, this); + if (!targ.length) return; + case 'object': + // DOMElement / jQuery + if (targ.is || targ.style) + // Get the real position of the target + toff = (targ = $(targ)).offset(); + } + + var offset = $.isFunction(settings.offset) && settings.offset(elem, targ) || settings.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] += 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 (settings.limit && /^\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, targ, 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 $.isFunction(val) || typeof val == 'object' ? val : { top:val, left:val }; + } + + // AMD requirement + return $scrollTo; + }) +}(typeof define === 'function' && define.amd ? define : function (deps, factory) { + if (typeof module !== 'undefined' && module.exports) { + // Node + module.exports = factory(require('jquery')); + } else { + factory(jQuery); + } +})); diff --git a/js/jquery.scrollTo.min.js b/js/jquery.scrollTo.min.js new file mode 100644 index 00000000..78ef4230 --- /dev/null +++ b/js/jquery.scrollTo.min.js @@ -0,0 +1,7 @@ +/** + * Copyright (c) 2007-2014 Ariel Flesler - afleslergmailcom | http://flesler.blogspot.com + * Licensed under MIT + * @author Ariel Flesler + * @version 1.4.13 + */ +;(function(k){'use strict';k(['jquery'],function($){var j=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};j.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:!0};j.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(f,g,h){if(typeof g=='object'){h=g;g=0}if(typeof h=='function')h={onAfter:h};if(f=='max')f=9e9;h=$.extend({},j.defaults,h);g=g||h.duration;h.queue=h.queue&&h.axis.length>1;if(h.queue)g/=2;h.offset=both(h.offset);h.over=both(h.over);return this._scrollable().each(function(){if(f==null)return;var d=this,$elem=$(d),targ=f,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=win?$(targ):$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}var e=$.isFunction(h.offset)&&h.offset(d,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=j.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=e[pos]||0;if(h.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*h.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&h.queue){if(old!=attr[key])animate(h.onAfterFirst);delete attr[key]}});animate(h.onAfter);function animate(a){$elem.animate(attr,g,h.easing,a&&function(){a.call(this,targ,h)})}}).end()};j.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 $.isFunction(a)||typeof a=='object'?a:{top:a,left:a}}return j})}(typeof define==='function'&&define.amd?define:function(a,b){if(typeof module!=='undefined'&&module.exports){module.exports=b(require('jquery'))}else{b(jQuery)}})); \ No newline at end of file diff --git a/templates/default/header.tpl b/templates/default/header.tpl index 0fc77438..ca4f05a9 100644 --- a/templates/default/header.tpl +++ b/templates/default/header.tpl @@ -23,7 +23,7 @@ - + From e60a91f9452348f3ec0f7f4271378b0da410cf72 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Tue, 9 Sep 2014 13:11:22 +0000 Subject: [PATCH 18/35] When selecting a custom date-time range using the date-time picker only display the "now" button if the gweb timezone is set to "browser" --- graph.php | 2 +- js/ganglia.js | 20 -------------------- templates/default/header.tpl | 16 ++++++++++++++++ 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/graph.php b/graph.php index 45a58522..5041d2e5 100644 --- a/graph.php +++ b/graph.php @@ -936,7 +936,7 @@ function rrdgraph_cmd_build($rrdtool_graph, } $command = ''; - if (isset($_SESSION['tz'])) + if (isset($_SESSION['tz']) && ($_SESSION['tz'] != '')) $command .= "TZ='" . $_SESSION['tz'] . "' "; $command .= diff --git a/js/ganglia.js b/js/ganglia.js index 594f9be4..13f44330 100644 --- a/js/ganglia.js +++ b/js/ganglia.js @@ -25,26 +25,6 @@ $(function(){ search_field_q.val(search_value); } - var datepicker_cs = $( "#datepicker-cs" ); - if (datepicker_cs[0]) - datepicker_cs.datetimepicker({ - showOn: "button", - constrainInput: false, - buttonImage: "img/calendar.gif", - buttonImageOnly: true, - hideNowButton: true - }); - - $( "#datepicker-cs").datetimepicker(); - var datepicker_ce = $( "#datepicker-ce" ); - if (datepicker_ce[0]) - datepicker_ce.datetimepicker({ - showOn: "button", - constrainInput: false, - buttonImage: "img/calendar.gif", - buttonImageOnly: true, - hideNowButton: true - }); }); function selectTab(tab_index) { diff --git a/templates/default/header.tpl b/templates/default/header.tpl index ca4f05a9..bbc9bcea 100644 --- a/templates/default/header.tpl +++ b/templates/default/header.tpl @@ -231,6 +231,22 @@ $("#timezone-picker").val("{$timezone_option}").trigger('chosen:updated'); } + var dateTimePickerOptions = { + showOn: "button", + constrainInput: false, + buttonImage: "img/calendar.gif", + buttonImageOnly: true, + showButtonPanel: ("{$timezone_option}" == 'browser') + }; + + var datepicker_cs = $("#datepicker-cs"); + if (datepicker_cs[0]) + datepicker_cs.datetimepicker(dateTimePickerOptions); + + var datepicker_ce = $("#datepicker-ce"); + if (datepicker_ce[0]) + datepicker_ce.datetimepicker(dateTimePickerOptions); + initShowEvent(); initTimeShift(); }); From 8a01064cb97f4f4e5cc36e1179065aebbb49c701 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Mon, 22 Sep 2014 22:51:21 +0000 Subject: [PATCH 19/35] Enable custom time range selection by drag select for view and host metric group graphs. --- templates/default/header.tpl | 138 ++++++++++++++++--------------- templates/default/host_view.tpl | 5 ++ templates/default/views_view.tpl | 1 + 3 files changed, 77 insertions(+), 67 deletions(-) diff --git a/templates/default/header.tpl b/templates/default/header.tpl index bbc9bcea..84e287a4 100644 --- a/templates/default/header.tpl +++ b/templates/default/header.tpl @@ -81,6 +81,76 @@ ganglia_form.submit(); } + function setStartAndEnd(startTimestamp, endTimestamp) { + // we're getting local start/end times. + + var start = Math.floor(startTimestamp * 1000); + var end = Math.floor(endTimestamp * 1000); + if ($("#tz").val() == "") { + start = moment.tz(start, server_timezone); + end = moment.tz(end, server_timezone); + } else { + start = moment(start); + end = moment(end); + } + // Generate RRD friendly date/time strings + $("#datepicker-cs").val(start.format('MM/D/YYYY HH:mm')); + $("#datepicker-ce").val(end.format('MM/D/YYYY HH:mm')); + } + + gangZoomDone = function done(startTime, endTime) { + setStartAndEnd(startTime, endTime); + document.forms['ganglia_form'].submit(); + } + + gangZoomCancel = function (startTime, endTime) { + setStartAndEnd(startTime, endTime); + } + + g_gangZoomDefaults = { + startTime: {$start_timestamp}, + endTime: {$end_timestamp}, + done: gangZoomDone, + cancel: gangZoomCancel + } + + function initCustomTimeRangeDragSelect(context) { + $(".host_small_zoomable", context).gangZoom($.extend({ + paddingLeft: 67, + paddingRight: 30, + paddingTop: 38, + paddingBottom: 25 + }, g_gangZoomDefaults)); + + $(".host_medium_zoomable", context).gangZoom($.extend({ + paddingLeft: 67, + paddingRight: 30, + paddingTop: 38, + paddingBottom: 40 + }, g_gangZoomDefaults)); + + $(".host_default_zoomable", context).gangZoom($.extend({ + paddingLeft: 66, + paddingRight: 30, + paddingTop: 37, + paddingBottom: 50 + }, g_gangZoomDefaults)); + + $(".host_large_zoomable", context).gangZoom($.extend({ + paddingLeft: 66, + paddingRight: 29, + paddingTop: 37, + paddingBottom: 56 + }, g_gangZoomDefaults)); + + $(".cluster_zoomable", context).gangZoom($.extend({ + paddingLeft: 67, + paddingRight: 30, + paddingTop: 37, + paddingBottom: 50 + }, g_gangZoomDefaults)); + } + $(function() { var range_menu = $("#range_menu"); if (range_menu[0]) @@ -149,73 +219,7 @@ $("#metrics-picker").val("{$metric_name}"); $(".header_btn").button(); - gangZoomDone = function done(startTime, endTime) { - setStartAndEnd(startTime, endTime); - document.forms['ganglia_form'].submit(); - } - - gangZoomCancel = function (startTime, endTime) { - setStartAndEnd(startTime, endTime); - } - - gangZoomDefaults = { - startTime: {$start_timestamp}, - endTime: {$end_timestamp}, - done: gangZoomDone, - cancel: gangZoomCancel - } - - $(".host_small_zoomable").gangZoom($.extend({ - paddingLeft: 67, - paddingRight: 30, - paddingTop: 38, - paddingBottom: 25 - }, gangZoomDefaults)); - - $(".host_medium_zoomable").gangZoom($.extend({ - paddingLeft: 67, - paddingRight: 30, - paddingTop: 38, - paddingBottom: 40 - }, gangZoomDefaults)); - - $(".host_default_zoomable").gangZoom($.extend({ - paddingLeft: 66, - paddingRight: 30, - paddingTop: 37, - paddingBottom: 50 - }, gangZoomDefaults)); - - $(".host_large_zoomable").gangZoom($.extend({ - paddingLeft: 66, - paddingRight: 29, - paddingTop: 37, - paddingBottom: 56 - }, gangZoomDefaults)); - - $(".cluster_zoomable").gangZoom($.extend({ - paddingLeft: 67, - paddingRight: 30, - paddingTop: 37, - paddingBottom: 50 - }, gangZoomDefaults)); - - function setStartAndEnd(startTimestamp, endTimestamp) { - // we're getting local start/end times. - - var start = Math.floor(startTimestamp * 1000); - var end = Math.floor(endTimestamp * 1000); - if ($("#tz").val() == "") { - start = moment.tz(start, server_timezone); - end = moment.tz(end, server_timezone); - } else { - start = moment(start); - end = moment(end); - } - // Generate RRD friendly date/time strings - $("#datepicker-cs").val(start.format('MM/D/YYYY HH:mm')); - $("#datepicker-ce").val(end.format('MM/D/YYYY HH:mm')); - } + initCustomTimeRangeDragSelect($(document.documentElement)); if ($("#timezone-picker").length) { $("#timezone-picker").chosen({ max_selected_options:1, diff --git a/templates/default/host_view.tpl b/templates/default/host_view.tpl index 69513dbd..e7d4d9a8 100644 --- a/templates/default/host_view.tpl +++ b/templates/default/host_view.tpl @@ -125,6 +125,7 @@ function toggleMetricGroup(mgId, mgDiv) { mgDiv.html(data); mgInitEventBtns(mgDiv); mgInitTimeshiftBtns(mgDiv); + mgInitCustomTimeRangeDragSelect(mgDiv); mgDiv.show(); }); } @@ -159,6 +160,10 @@ function refreshHostView() { }); } +function mgInitCustomTimeRangeDragSelect(mgDiv) { + initCustomTimeRangeDragSelect(mgDiv); +} + function mgInitEventBtns(mgDiv) { var checked = $("#show_all_events").attr("checked"); mgDiv.find("[id^=" + SHOW_EVENTS_BASE_ID + "]").each(function() { diff --git a/templates/default/views_view.tpl b/templates/default/views_view.tpl index 90537e12..17c0ad30 100644 --- a/templates/default/views_view.tpl +++ b/templates/default/views_view.tpl @@ -142,6 +142,7 @@ function(data) { $("#views-content").html(data); initShowEvent(); + initCustomTimeRangeDragSelect($("#views-content")); if (viewCommonYaxis) refreshView(); }); From 49a308077b0e8d8d4ac9768f2fed35439c0bd8ba Mon Sep 17 00:00:00 2001 From: pcpiela Date: Thu, 25 Sep 2014 20:18:45 +0000 Subject: [PATCH 20/35] Added support for specifying "maxrows" as a querystring parameter to graph.php. This enables Flot graphs to better control how data points are generated based on the width of the graph. --- graph.php | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/graph.php b/graph.php index 5041d2e5..a8430864 100644 --- a/graph.php +++ b/graph.php @@ -1089,9 +1089,9 @@ function output_data_to_external_format($rrdtool_graph_series, } // This command will export values for the specified format in XML - $command = $rrdtool . - " xport --start '" . $rrdtool_graph_start . - "' --end '" . $rrdtool_graph_end . "' " + + $maxRows = ''; + if ($step) { /* Allow a custom step, if it was specified by the user. Also, we need to specify a --maxrows in case the number @@ -1101,11 +1101,20 @@ function output_data_to_external_format($rrdtool_graph_series, to guard against "underflow" because rrdxport craps out when --maxrows is less than 10. */ - . ($step ? - " --step '" . $step . "' --maxrows '" - . max(10, round(($rrdtool_graph_end - - $rrdtool_graph_start) / $step)) . "' " : "") - . $rrd_options . " " . $rrdtool_graph_args; + + $maxRows = max(10, round(($rrdtool_graph_end - + $rrdtool_graph_start) / $step)); + } else if (isset($_GET['maxrows']) && is_numeric($_GET['maxrows'])) { + $maxRows = max(10, $_GET['maxrows']); + } + + $command = $rrdtool . + " xport --start '" . $rrdtool_graph_start . + "' --end '" . $rrdtool_graph_end . "' " . + ($step ? " --step '" . $step . "' " : '') . + ($maxRows ? " --maxrows '" . $maxRows . "' " : '') . + $rrd_options . " " . + $rrdtool_graph_args; // Read in the XML $string = ""; From 7cd62655c0625d6285293c7a3b51626e76baf0f4 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Thu, 25 Sep 2014 20:22:08 +0000 Subject: [PATCH 21/35] Inspect graphs now control how many data points are generated based on their width. A value of maxrows is passed in the querystring to graph.php. --- inspect_graph.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inspect_graph.php b/inspect_graph.php index 02cb1a29..a0e57b3a 100644 --- a/inspect_graph.php +++ b/inspect_graph.php @@ -291,7 +291,7 @@ function onEventsReceived(overlay_events) { } $.ajax({ - url: dataurl, + url: dataurl + '&maxrows=' + placeHolder.width(), method: 'GET', dataType: 'json', success: onDataReceived @@ -393,7 +393,7 @@ function hoverHandler(event, pos, item) { function refresh() { $.ajax({ - url: dataurl, + url: dataurl + '&maxrows=' + placeHolder.width(), method: 'GET', dataType: 'json', success: onDataReceived}); From b53eadfae69a1242295eb3716b33988dfa765f23 Mon Sep 17 00:00:00 2001 From: pcpiela Date: Thu, 25 Sep 2014 20:24:39 +0000 Subject: [PATCH 22/35] Minor cleanup --- templates/default/header.tpl | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/templates/default/header.tpl b/templates/default/header.tpl index 84e287a4..d10c8bfe 100644 --- a/templates/default/header.tpl +++ b/templates/default/header.tpl @@ -39,12 +39,13 @@ function refreshHeader() { $.get('header.php?date_only=1', function(datetime) { - var title = $("#page_title").text(); + var pageTitle = $("#page_title"); + var title = pageTitle.text(); var l = title.lastIndexOf(" at "); if (l != -1) title = title.substring(0, l); title += " at " + datetime; - $("#page_title").text(title); + pageTitle.text(title); }); } @@ -115,35 +116,35 @@ } function initCustomTimeRangeDragSelect(context) { - $(".host_small_zoomable", context).gangZoom($.extend({ + context.find(".host_small_zoomable").gangZoom($.extend({ paddingLeft: 67, paddingRight: 30, paddingTop: 38, paddingBottom: 25 }, g_gangZoomDefaults)); - $(".host_medium_zoomable", context).gangZoom($.extend({ + context.find(".host_medium_zoomable").gangZoom($.extend({ paddingLeft: 67, paddingRight: 30, paddingTop: 38, paddingBottom: 40 }, g_gangZoomDefaults)); - $(".host_default_zoomable", context).gangZoom($.extend({ + context.find(".host_default_zoomable").gangZoom($.extend({ paddingLeft: 66, paddingRight: 30, paddingTop: 37, paddingBottom: 50 }, g_gangZoomDefaults)); - $(".host_large_zoomable", context).gangZoom($.extend({ + context.find(".host_large_zoomable").gangZoom($.extend({ paddingLeft: 66, paddingRight: 29, paddingTop: 37, paddingBottom: 56 }, g_gangZoomDefaults)); - $(".cluster_zoomable", context).gangZoom($.extend({ + context.find(".cluster_zoomable").gangZoom($.extend({ paddingLeft: 67, paddingRight: 30, paddingTop: 37, @@ -221,9 +222,10 @@ initCustomTimeRangeDragSelect($(document.documentElement)); - if ($("#timezone-picker").length) { - $("#timezone-picker").chosen({ max_selected_options:1, - disable_search:true}). + var tzPicker = $("#timezone-picker"); + if (tzPicker.length) { + tzPicker.chosen({ max_selected_options:1, + disable_search:true}). on('change', function(evt, params) { if (params.selected == 'browser') { $("#tz").val(tz.name()); @@ -232,7 +234,7 @@ } ganglia_form.submit(); }); - $("#timezone-picker").val("{$timezone_option}").trigger('chosen:updated'); + tzPicker.val("{$timezone_option}").trigger('chosen:updated'); } var dateTimePickerOptions = { From 0cc7cccb6a110f575cde35579dd47c28e0abc52f Mon Sep 17 00:00:00 2001 From: pcpiela Date: Fri, 26 Sep 2014 22:35:35 +0000 Subject: [PATCH 23/35] Fixed an issue in conf_default.php.in that assumed version.php would be in the local directory instead of the ganglia-web root directory. --- Makefile | 2 +- conf_default.php.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c1b310c8..96f499db 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ clean: conf_default.php: conf_default.php.in @echo -n "Generating conf_default.php ........................ " - @sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebdir@|$(GWEB_STATEDIR)|g" conf_default.php.in > conf_default.php + @sed -e "s|@vargmetadir@|$(GMETAD_ROOTDIR)|" -e "s|@vargwebdir@|$(GWEB_STATEDIR)|g" -e "s|@vargwebroot@|$(GDESTDIR)|g" conf_default.php.in > conf_default.php @echo "DONE." conf_redirect.php: conf_redirect.php.in diff --git a/conf_default.php.in b/conf_default.php.in index afbbf102..6991e8b0 100644 --- a/conf_default.php.in +++ b/conf_default.php.in @@ -9,7 +9,7 @@ # # Gmetad-webfrontend version. Used to check for updates. # -$conf['gweb_root'] = dirname(__FILE__); +$conf['gweb_root'] = "@vargwebroot@"; $conf['gweb_confdir'] = "@vargwebdir@"; include_once $conf['gweb_root'] . "/version.php"; From f51f0ac71b8a7d2aa85d9d1cb304207fc141dbd7 Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 3 Jun 2014 13:15:48 -0700 Subject: [PATCH 24/35] Don't global unused variables. --- ganglia.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ganglia.php b/ganglia.php index 5b65e4d1..f6a990b9 100644 --- a/ganglia.php +++ b/ganglia.php @@ -187,7 +187,7 @@ function start_cluster ($parser, $tagname, $attrs) function start_everything ($parser, $tagname, $attrs) { - global $index_array, $hosts, $metrics, $cluster, $self, $grid, $hosts_up, $hosts_down, $debug; + global $index_array, $self, $grid, $debug; static $hostname, $cluster_name; if ($debug) print "
DEBUG: parser start everything [$tagname]\n"; From c206ceb3b51ba7c40cc2c826d555bb22b38743cc Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 3 Jun 2014 13:32:46 -0700 Subject: [PATCH 25/35] Get rid of global debug call to save time in production. --- ganglia.php | 62 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/ganglia.php b/ganglia.php index f6a990b9..564aec25 100644 --- a/ganglia.php +++ b/ganglia.php @@ -77,14 +77,17 @@ function preamble($ganglia) $version[$component] = $ganglia['VERSION']; } +function debug_start_meta ($parser, $tagname, $attrs) +{ + print "
DEBUG: parser start meta [$tagname]\n"; + start_meta($parser, $tagname, $attrs); +} function start_meta ($parser, $tagname, $attrs) { - global $metrics, $grid, $self, $debug; + global $metrics, $grid, $self; static $sourcename, $metricname; - if ($debug) print "
DEBUG: parser start meta [$tagname]\n"; - switch ($tagname) { case "GANGLIA_XML": @@ -93,7 +96,6 @@ function start_meta ($parser, $tagname, $attrs) case "GRID": case "CLUSTER": - if ($debug) print "
DEBUG: parser start meta GRID|CLUSTER\n"; # Our grid will be first. if (!$sourcename) $self = $attrs['NAME']; @@ -119,13 +121,17 @@ function start_meta ($parser, $tagname, $attrs) } } +function debug_start_cluster ($parser, $tagname, $attrs) +{ + print "
DEBUG: parser start cluster [$tagname]\n"; + start_cluster($parser, $tagname, $attrs); +} function start_cluster ($parser, $tagname, $attrs) { - global $metrics, $cluster, $self, $grid, $hosts_up, $hosts_down, $debug; + global $metrics, $cluster, $self, $grid, $hosts_up, $hosts_down; static $hostname; - if ($debug) print "
DEBUG: parser start cluster [$tagname]\n"; switch ($tagname) { case "GANGLIA_XML": @@ -185,13 +191,17 @@ function start_cluster ($parser, $tagname, $attrs) } } +function debug_start_everything ($parser, $tagname, $attrs) +{ + print "
DEBUG: parser start everything [$tagname]\n"; + start_everything($parser, $tagname, $attrs); +} + function start_everything ($parser, $tagname, $attrs) { - global $index_array, $self, $grid, $debug; + global $index_array, $self, $grid; static $hostname, $cluster_name; - if ($debug) print "
DEBUG: parser start everything [$tagname]\n"; - switch ($tagname) { case "GANGLIA_XML": @@ -226,6 +236,11 @@ function start_everything ($parser, $tagname, $attrs) } +function debug_start_cluster_summary ($parser, $tagname, $attrs) +{ + start_cluster_summary($parser, $tagname, $attrs); +} + function start_cluster_summary ($parser, $tagname, $attrs) { global $metrics, $cluster, $self, $grid; @@ -257,6 +272,12 @@ function start_cluster_summary ($parser, $tagname, $attrs) } +function debug_start_host ($parser, $tagname, $attrs) +{ + start_host($parser, $tagname, $attrs); +} + + function start_host ($parser, $tagname, $attrs) { global $metrics, $cluster, $hosts_up, $hosts_down, $self, $grid; @@ -340,7 +361,16 @@ function Gmetad () $ip = func_get_arg(0); } - if ($debug) print "
DEBUG: Creating parser\n"; + if ($debug) + { + print "
DEBUG: Creating parser\n"; + $debug_prefix = "debug_"; + } + else + { + $debug_prefix = ""; + } + if ( $context == "compare_hosts" or $context == "views" or $context == "decompose_graph") return TRUE; $parser = xml_parser_create(); @@ -351,26 +381,26 @@ function Gmetad () case "control": case "tree": default: - xml_set_element_handler($parser, "start_meta", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_meta", "end_all"); $request = "/?filter=summary"; break; case "physical": case "cluster": - xml_set_element_handler($parser, "start_cluster", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_cluster", "end_all"); $request = "/$clustername"; break; case "index_array": case "views": - xml_set_element_handler($parser, "start_everything", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_everything", "end_all"); $request = "/"; break; case "cluster-summary": - xml_set_element_handler($parser, "start_cluster_summary", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_cluster_summary", "end_all"); $request = "/$clustername?filter=summary"; break; case "node": case "host": - xml_set_element_handler($parser, "start_host", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_host", "end_all"); $request = "/$clustername/$hostname"; $strip_extra = false; break; @@ -387,7 +417,7 @@ function Gmetad () if ($port == 8649) { # We are connecting to a gmond. Non-interactive. - xml_set_element_handler($parser, "start_cluster", "end_all"); + xml_set_element_handler($parser, $debug_prefix . "start_cluster", "end_all"); } else { From 52a02b33e580167eeed54018d5efb764e10bc546 Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 3 Jun 2014 14:03:43 -0700 Subject: [PATCH 26/35] Micro-optimize globals inside the XML parsing functions --- ganglia.php | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/ganglia.php b/ganglia.php index 564aec25..c08c0387 100644 --- a/ganglia.php +++ b/ganglia.php @@ -8,6 +8,8 @@ # information as we need to make the page. # +error_reporting(E_ALL); + $gweb_root = dirname(__FILE__); include_once($gweb_root . "/version.php"); @@ -85,7 +87,6 @@ function debug_start_meta ($parser, $tagname, $attrs) function start_meta ($parser, $tagname, $attrs) { - global $metrics, $grid, $self; static $sourcename, $metricname; switch ($tagname) @@ -96,6 +97,7 @@ function start_meta ($parser, $tagname, $attrs) case "GRID": case "CLUSTER": + global $grid, $self; # Our grid will be first. if (!$sourcename) $self = $attrs['NAME']; @@ -107,11 +109,13 @@ function start_meta ($parser, $tagname, $attrs) break; case "METRICS": + global $metrics; $metricname = rawurlencode($attrs['NAME']); $metrics[$sourcename][$metricname] = $attrs; break; case "HOSTS": + global $grid; $grid[$sourcename]['HOSTS_UP'] = $attrs['UP']; $grid[$sourcename]['HOSTS_DOWN'] = $attrs['DOWN']; break; @@ -129,7 +133,6 @@ function debug_start_cluster ($parser, $tagname, $attrs) function start_cluster ($parser, $tagname, $attrs) { - global $metrics, $cluster, $self, $grid, $hosts_up, $hosts_down; static $hostname; switch ($tagname) @@ -137,16 +140,20 @@ function start_cluster ($parser, $tagname, $attrs) case "GANGLIA_XML": preamble($attrs); break; + case "GRID": + global $self, $grid; $self = $attrs['NAME']; $grid = $attrs; break; case "CLUSTER": + global $cluster; $cluster = $attrs; break; case "HOST": + global $metrics, $cluster, $hosts_up, $hosts_down; $hostname = $attrs['NAME']; if (host_alive($attrs, $cluster)) @@ -182,6 +189,7 @@ function start_cluster ($parser, $tagname, $attrs) break; case "METRIC": + global $metrics; $metricname = rawurlencode($attrs['NAME']); $metrics[$hostname][$metricname] = $attrs; break; @@ -199,7 +207,6 @@ function debug_start_everything ($parser, $tagname, $attrs) function start_everything ($parser, $tagname, $attrs) { - global $index_array, $self, $grid; static $hostname, $cluster_name; switch ($tagname) @@ -208,6 +215,7 @@ function start_everything ($parser, $tagname, $attrs) preamble($attrs); break; case "GRID": + global $self, $grid; $self = $attrs['NAME']; $grid = $attrs; break; @@ -218,6 +226,7 @@ function start_everything ($parser, $tagname, $attrs) break; case "HOST": + global $index_array; $hostname = $attrs['NAME']; # For some reason this occasionally will end up marking live hosts not alive # causing them to miss out from aggregate graphs @@ -225,9 +234,10 @@ function start_everything ($parser, $tagname, $attrs) $index_array['cluster'][$hostname][] = $cluster_name; case "METRIC": + global $index_array; $metricname = rawurlencode($attrs['NAME']); - if ( $metricname != $hostname ) - $index_array['metrics'][$metricname][] = $hostname; + if ( $metricname != $hostname ) + $index_array['metrics'][$metricname][] = $hostname; break; default: @@ -243,26 +253,28 @@ function debug_start_cluster_summary ($parser, $tagname, $attrs) function start_cluster_summary ($parser, $tagname, $attrs) { - global $metrics, $cluster, $self, $grid; - switch ($tagname) { case "GANGLIA_XML": preamble($attrs); break; case "GRID": + global $self, $grid; $self = $attrs['NAME']; $grid = $attrs; case "CLUSTER": + global $cluster; $cluster = $attrs; break; case "HOSTS": + global $cluster; $cluster['HOSTS_UP'] = $attrs['UP']; $cluster['HOSTS_DOWN'] = $attrs['DOWN']; break; case "METRICS": + global $metrics; $metrics[$attrs['NAME']] = $attrs; break; @@ -280,7 +292,6 @@ function debug_start_host ($parser, $tagname, $attrs) function start_host ($parser, $tagname, $attrs) { - global $metrics, $cluster, $hosts_up, $hosts_down, $self, $grid; static $metricname; switch ($tagname) @@ -289,14 +300,17 @@ function start_host ($parser, $tagname, $attrs) preamble($attrs); break; case "GRID": + global $self, $grid; $self = $attrs['NAME']; $grid = $attrs; break; case "CLUSTER": + global $cluster; $cluster = $attrs; break; case "HOST": + global $cluster, $hosts_up, $hosts_down; if (host_alive($attrs, $cluster)) $hosts_up = $attrs; else @@ -304,6 +318,7 @@ function start_host ($parser, $tagname, $attrs) break; case "METRIC": + global $metrics; $metricname = rawurlencode($attrs['NAME']); $metrics[$metricname] = $attrs; break; @@ -312,6 +327,7 @@ function start_host ($parser, $tagname, $attrs) break; case "EXTRA_ELEMENT": + global $metrics; if ( isset($attrs['NAME']) && isset($attrs['VAL']) && ($attrs['NAME'] == "GROUP")) { if ( isset($metrics[$metricname]['GROUP']) ) { $group_array = array_merge( (array)$attrs['VAL'], $metrics[$metricname]['GROUP'] ); From 325b5690bafc7b3b7c8939d92ac644a671d052bf Mon Sep 17 00:00:00 2001 From: pcpiela Date: Fri, 10 Oct 2014 13:46:48 +0000 Subject: [PATCH 27/35] Only initiate "zoom" when left mouse button is depressed. This change avoids interference with right mouse click to initiate alternate action such as getting image properties. --- js/jquery.gangZoom.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/jquery.gangZoom.js b/js/jquery.gangZoom.js index c5e37c49..1dbb6c9e 100644 --- a/js/jquery.gangZoom.js +++ b/js/jquery.gangZoom.js @@ -81,6 +81,9 @@ }); self.img.mousedown(function (event) { + if (event.which != 1) + return; + event.preventDefault(); self.shouldStopClick = false; self.stopped = false; From ef768e40fbaf271bbdaecec8a14c42a27dbd232b Mon Sep 17 00:00:00 2001 From: pcpiela Date: Mon, 13 Oct 2014 13:20:50 +0000 Subject: [PATCH 28/35] Switch to koenpunt fork that allows for dynamically adding items to menu lists. We exploit this functionality in the creation of aggregate graphs. --- css/chosen.css | 68 ++++++----- css/chosen.min.css | 4 +- js/chosen.jquery.js | 243 ++++++++++++++++++++++++++++------------ js/chosen.jquery.min.js | 4 +- 4 files changed, 215 insertions(+), 104 deletions(-) diff --git a/css/chosen.css b/css/chosen.css index d203a071..681fcfd1 100644 --- a/css/chosen.css +++ b/css/chosen.css @@ -1,3 +1,27 @@ +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.1.0 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.1.0 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + /* @group Base */ .chosen-container { position: relative; @@ -6,8 +30,9 @@ font-size: 13px; zoom: 1; *display: inline; + -moz-user-select: -moz-none; + -ms-user-select: none; -webkit-user-select: none; - -moz-user-select: none; user-select: none; } .chosen-container .chosen-drop { @@ -15,8 +40,8 @@ top: 100%; left: -9999px; z-index: 1010; - -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; box-sizing: border-box; width: 100%; border: 1px solid #aaa; @@ -42,11 +67,10 @@ border: 1px solid #aaa; border-radius: 5px; background-color: #fff; - background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); - background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); - background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: linear-gradient(to bottom, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); background-clip: padding-box; box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1); color: #444; @@ -105,8 +129,8 @@ white-space: nowrap; } .chosen-container-single .chosen-search input[type="text"] { - -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; box-sizing: border-box; margin: 1px 0; padding: 4px 20px 4px 5px; @@ -115,11 +139,7 @@ outline: 0; border: 1px solid #aaa; background: white url('chosen-sprite.png') no-repeat 100% -20px; - background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat 100% -20px; font-size: 1em; font-family: sans-serif; line-height: normal; @@ -152,6 +172,7 @@ padding: 5px 6px; list-style: none; line-height: 15px; + -webkit-touch-callout: none; } .chosen-container .chosen-results li.active-result { display: list-item; @@ -164,10 +185,9 @@ } .chosen-container .chosen-results li.highlighted { background-color: #3875d7; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); - background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%); background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%); background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%); background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); color: #fff; } @@ -193,8 +213,8 @@ .chosen-container-multi .chosen-choices { position: relative; overflow: hidden; - -webkit-box-sizing: border-box; -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; box-sizing: border-box; margin: 0; padding: 0; @@ -203,10 +223,9 @@ height: 1%; border: 1px solid #aaa; background-color: #fff; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); background-image: -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); background-image: -o-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); cursor: text; } @@ -243,10 +262,9 @@ border: 1px solid #aaa; border-radius: 3px; background-color: #e4e4e4; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-clip: padding-box; box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05); @@ -271,11 +289,10 @@ padding-right: 5px; border: 1px solid #ccc; background-color: #e4e4e4; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); - background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); - background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(to bottom, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); color: #666; } .chosen-container-multi .chosen-choices li.search-choice-focus { @@ -306,10 +323,9 @@ border-bottom-right-radius: 0; -moz-border-radius-bottomleft: 0; border-bottom-left-radius: 0; - background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); - background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%); background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%); background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%); background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); box-shadow: 0 1px 0 #fff inset; } @@ -398,11 +414,7 @@ .chosen-rtl .chosen-search input[type="text"] { padding: 4px 5px 4px 20px; background: white url('chosen-sprite.png') no-repeat -30px -20px; - background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); - background: url('chosen-sprite.png') no-repeat -30px -20px, -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, -o-linear-gradient(#eeeeee 1%, #ffffff 15%); - background: url('chosen-sprite.png') no-repeat -30px -20px, linear-gradient(#eeeeee 1%, #ffffff 15%); + background: url('chosen-sprite.png') no-repeat -30px -20px; direction: rtl; } .chosen-rtl.chosen-container-single .chosen-single div b { diff --git a/css/chosen.min.css b/css/chosen.min.css index 3f3f5dde..37f771f8 100644 --- a/css/chosen.min.css +++ b/css/chosen.min.css @@ -1,3 +1,3 @@ -/* Chosen v1.0.0 | (c) 2011-2013 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ +/* Chosen v1.1.0 | (c) 2011-2013 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ -.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;zoom:1;*display:inline;-webkit-user-select:none;-moz-user-select:none;user-select:none}.chosen-container .chosen-drop{position:absolute;top:100%;left:-9999px;z-index:1010;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%;border:1px solid #aaa;border-top:0;background:#fff;box-shadow:0 4px 5px rgba(0,0,0,.15)}.chosen-container.chosen-with-drop .chosen-drop{left:0}.chosen-container a{cursor:pointer}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:23px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),color-stop(100%,#f4f4f4));background:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:#fff url(chosen-sprite.png) no-repeat 100% -20px;background:url(chosen-sprite.png) no-repeat 100% -20px,-webkit-gradient(linear,50% 0,50% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background:url(chosen-sprite.png) no-repeat 100% -20px,-webkit-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat 100% -20px,-moz-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat 100% -20px,-o-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat 100% -20px,linear-gradient(#eee 1%,#fff 15%);font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;left:-9999px}.chosen-container .chosen-results{position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:-webkit-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-moz-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-o-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0;width:100%;height:auto!important;height:1%;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background-image:-webkit-linear-gradient(#eee 1%,#fff 15%);background-image:-moz-linear-gradient(#eee 1%,#fff 15%);background-image:-o-linear-gradient(#eee 1%,#fff 15%);background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:5px;height:15px;outline:0;border:0!important;background:transparent!important;box-shadow:none;color:#666;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-multi .chosen-choices li.search-field .default{color:#999}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 0 3px 5px;padding:3px 20px 3px 5px;border:1px solid #aaa;border-radius:3px;background-color:#e4e4e4;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-clip:padding-box;box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#eee),color-stop(80%,#fff));background-image:-webkit-linear-gradient(#eee 20%,#fff 80%);background-image:-moz-linear-gradient(#eee 20%,#fff 80%);background-image:-o-linear-gradient(#eee 20%,#fff 80%);background-image:linear-gradient(#eee 20%,#fff 80%);box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:0;background:transparent}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#111!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single-nosearch .chosen-search,.chosen-rtl .chosen-drop{left:9999px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:0}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:#fff url(chosen-sprite.png) no-repeat -30px -20px;background:url(chosen-sprite.png) no-repeat -30px -20px,-webkit-gradient(linear,50% 0,50% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background:url(chosen-sprite.png) no-repeat -30px -20px,-webkit-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat -30px -20px,-moz-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat -30px -20px,-o-linear-gradient(#eee 1%,#fff 15%);background:url(chosen-sprite.png) no-repeat -30px -20px,linear-gradient(#eee 1%,#fff 15%);direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-resolution:144dpi){.chosen-rtl .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-container-single .chosen-search input[type=text],.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}} \ No newline at end of file +.chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;zoom:1;*display:inline;-moz-user-select:-moz-none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.chosen-container .chosen-drop{position:absolute;top:100%;left:-9999px;z-index:1010;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;border:1px solid #aaa;border-top:0;background:#fff;box-shadow:0 4px 5px rgba(0,0,0,.15)}.chosen-container.chosen-with-drop .chosen-drop{left:0}.chosen-container a{cursor:pointer}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:23px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:linear-gradient(to bottom,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:#fff url(chosen-sprite.png) no-repeat 100% -20px;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;left:-9999px}.chosen-container .chosen-results{position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-moz-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-o-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-webkit-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0;width:100%;height:auto!important;height:1%;border:1px solid #aaa;background-color:#fff;background-image:-moz-linear-gradient(#eee 1%,#fff 15%);background-image:-o-linear-gradient(#eee 1%,#fff 15%);background-image:-webkit-linear-gradient(#eee 1%,#fff 15%);background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:5px;height:15px;outline:0;border:0!important;background:transparent!important;box-shadow:none;color:#666;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-multi .chosen-choices li.search-field .default{color:#999}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 0 3px 5px;padding:3px 20px 3px 5px;border:1px solid #aaa;border-radius:3px;background-color:#e4e4e4;background-image:-moz-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-webkit-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-clip:padding-box;box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-moz-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-webkit-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(to bottom,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;background-image:-moz-linear-gradient(#eee 20%,#fff 80%);background-image:-o-linear-gradient(#eee 20%,#fff 80%);background-image:-webkit-linear-gradient(#eee 20%,#fff 80%);background-image:linear-gradient(#eee 20%,#fff 80%);box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:0;background:transparent}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#111!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single-nosearch .chosen-search,.chosen-rtl .chosen-drop{left:9999px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:0}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:#fff url(chosen-sprite.png) no-repeat -30px -20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-resolution:144dpi){.chosen-rtl .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-container-single .chosen-search input[type=text],.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}} \ No newline at end of file diff --git a/js/chosen.jquery.js b/js/chosen.jquery.js index 10fa0e55..2475a6a9 100644 --- a/js/chosen.jquery.js +++ b/js/chosen.jquery.js @@ -1,12 +1,15 @@ -// Chosen, a Select Box Enhancer for jQuery and Prototype -// by Patrick Filler for Harvest, http://getharvest.com -// -// Version 1.0.0 -// Full source at https://github.com/harvesthq/chosen -// Copyright (c) 2011 Harvest http://getharvest.com - -// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md -// This file is generated by `grunt build`, do not edit it by hand. +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.1.0 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + (function() { var $, AbstractChosen, Chosen, SelectParser, _ref, __hasProp = {}.hasOwnProperty, @@ -28,7 +31,6 @@ SelectParser.prototype.add_group = function(group) { var group_position, option, _i, _len, _ref, _results; - group_position = this.parsed.length; this.parsed.push({ array_index: group_position, @@ -77,7 +79,6 @@ SelectParser.prototype.escapeExpression = function(text) { var map, unsafe_chars; - if ((text == null) || text === false) { return ""; } @@ -103,7 +104,6 @@ SelectParser.select_to_array = function(select) { var child, parser, _i, _len, _ref; - parser = new SelectParser(); _ref = select.childNodes; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -130,7 +130,6 @@ AbstractChosen.prototype.set_default_values = function() { var _this = this; - this.click_test_action = function(evt) { return _this.test_active_click(evt); }; @@ -141,7 +140,6 @@ this.mouse_on_container = false; this.results_showing = false; this.result_highlighted = null; - this.result_single_selected = null; this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false; this.disable_search_threshold = this.options.disable_search_threshold || 0; this.disable_search = this.options.disable_search || false; @@ -152,7 +150,10 @@ this.max_selected_options = this.options.max_selected_options || Infinity; this.inherit_select_classes = this.options.inherit_select_classes || false; this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true; - return this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true; + this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true; + this.create_option = this.options.create_option || false; + this.persistent_create_option = this.options.persistent_create_option || false; + return this.skip_no_results = this.options.skip_no_results || false; }; AbstractChosen.prototype.set_default_text = function() { @@ -163,7 +164,8 @@ } else { this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text; } - return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text; + this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text; + return this.create_option_text = this.form_field.getAttribute("data-create_option_text") || this.options.create_option_text || AbstractChosen.default_create_option_text; }; AbstractChosen.prototype.mouse_enter = function() { @@ -176,7 +178,6 @@ AbstractChosen.prototype.input_focus = function(evt) { var _this = this; - if (this.is_multiple) { if (!this.active_field) { return setTimeout((function() { @@ -192,7 +193,6 @@ AbstractChosen.prototype.input_blur = function(evt) { var _this = this; - if (!this.mouse_on_container) { this.active_field = false; return setTimeout((function() { @@ -203,7 +203,6 @@ AbstractChosen.prototype.results_option_build = function(options) { var content, data, _i, _len, _ref; - content = ''; _ref = this.results_data; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -225,8 +224,7 @@ }; AbstractChosen.prototype.result_add_option = function(option) { - var classes, style; - + var classes, option_el; if (!option.search_match) { return ''; } @@ -249,18 +247,30 @@ if (option.classes !== "") { classes.push(option.classes); } - style = option.style.cssText !== "" ? " style=\"" + option.style + "\"" : ""; - return "
  • " + option.search_text + "
  • "; + option_el = document.createElement("li"); + option_el.className = classes.join(" "); + option_el.style.cssText = option.style; + option_el.setAttribute("data-option-array-index", option.array_index); + option_el.innerHTML = option.search_text; + return this.outerHTML(option_el); }; AbstractChosen.prototype.result_add_group = function(group) { + var group_el; if (!(group.search_match || group.group_match)) { return ''; } if (!(group.active_options > 0)) { return ''; } - return "
  • " + group.search_text + "
  • "; + group_el = document.createElement("li"); + group_el.className = "group-result"; + group_el.innerHTML = group.search_text; + return this.outerHTML(group_el); + }; + + AbstractChosen.prototype.append_option = function(option) { + return this.select_append_option(option); }; AbstractChosen.prototype.results_update_field = function() { @@ -269,13 +279,27 @@ this.results_reset_cleanup(); } this.result_clear_highlight(); - this.result_single_selected = null; this.results_build(); if (this.results_showing) { return this.winnow_results(); } }; + AbstractChosen.prototype.reset_single_select_options = function() { + var result, _i, _len, _ref, _results; + _ref = this.results_data; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + result = _ref[_i]; + if (result.selected) { + _results.push(result.selected = false); + } else { + _results.push(void 0); + } + } + return _results; + }; + AbstractChosen.prototype.results_toggle = function() { if (this.results_showing) { return this.results_hide(); @@ -293,15 +317,16 @@ }; AbstractChosen.prototype.winnow_results = function() { - var escapedSearchText, option, regex, regexAnchor, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref; - + var eregex, escapedSearchText, exact_result, option, regex, regexAnchor, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref; this.no_results_clear(); results = 0; + exact_result = false; searchText = this.get_search_text(); escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); regexAnchor = this.search_contains ? "" : "^"; regex = new RegExp(regexAnchor + escapedSearchText, 'i'); zregex = new RegExp(escapedSearchText, 'i'); + eregex = new RegExp('^' + escapedSearchText + '$', 'i'); _ref = this.results_data; for (_i = 0, _len = _ref.length; _i < _len; _i++) { option = _ref[_i]; @@ -325,6 +350,7 @@ if (option.search_match && !option.group) { results += 1; } + exact_result = eregex.test(option.html); if (option.search_match) { if (searchText.length) { startpos = option.search_text.search(zregex); @@ -343,16 +369,20 @@ this.result_clear_highlight(); if (results < 1 && searchText.length) { this.update_results_content(""); - return this.no_results(searchText); + if (!(this.create_option && this.skip_no_results)) { + this.no_results(searchText); + } } else { this.update_results_content(this.results_option_build()); - return this.winnow_results_set_highlight(); + this.winnow_results_set_highlight(); + } + if (this.create_option && (results < 1 || (!exact_result && this.persistent_create_option)) && searchText.length) { + return this.show_create_option(searchText); } }; AbstractChosen.prototype.search_string_match = function(search_string, regex) { var part, parts, _i, _len; - if (regex.test(search_string)) { return true; } else if (this.enable_split_word_search && (search_string.indexOf(" ") >= 0 || search_string.indexOf("[") === 0)) { @@ -370,7 +400,6 @@ AbstractChosen.prototype.choices_count = function() { var option, _i, _len, _ref; - if (this.selected_option_count != null) { return this.selected_option_count; } @@ -394,7 +423,6 @@ AbstractChosen.prototype.keyup_checker = function(evt) { var stroke, _ref; - stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; this.search_field_scale(); switch (stroke) { @@ -429,6 +457,13 @@ } }; + AbstractChosen.prototype.clipboard_event_checker = function(evt) { + var _this = this; + return setTimeout((function() { + return _this.results_search(); + }), 50); + }; + AbstractChosen.prototype.container_width = function() { if (this.options.width != null) { return this.options.width; @@ -450,6 +485,32 @@ return true; }; + AbstractChosen.prototype.search_results_touchstart = function(evt) { + this.touch_started = true; + return this.search_results_mouseover(evt); + }; + + AbstractChosen.prototype.search_results_touchmove = function(evt) { + this.touch_started = false; + return this.search_results_mouseout(evt); + }; + + AbstractChosen.prototype.search_results_touchend = function(evt) { + if (this.touch_started) { + return this.search_results_mouseup(evt); + } + }; + + AbstractChosen.prototype.outerHTML = function(element) { + var tmp; + if (element.outerHTML) { + return element.outerHTML; + } + tmp = document.createElement("div"); + tmp.appendChild(element); + return tmp.innerHTML; + }; + AbstractChosen.browser_is_supported = function() { if (window.navigator.appName === "Microsoft Internet Explorer") { return document.documentMode >= 8; @@ -471,6 +532,8 @@ AbstractChosen.default_no_result_text = "No results match"; + AbstractChosen.default_create_option_text = "Add Option"; + return AbstractChosen; })(); @@ -484,7 +547,6 @@ } return this.each(function(input_field) { var $this, chosen; - $this = $(this); chosen = $this.data('chosen'); if (options === 'destroy' && chosen) { @@ -512,7 +574,6 @@ Chosen.prototype.set_up_html = function() { var container_classes, container_props; - container_classes = ["chosen-container"]; container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single")); if (this.inherit_select_classes && this.form_field.className) { @@ -558,7 +619,6 @@ Chosen.prototype.register_observers = function() { var _this = this; - this.container.bind('mousedown.chosen', function(evt) { _this.container_mousedown(evt); }); @@ -583,6 +643,15 @@ this.search_results.bind('mousewheel.chosen DOMMouseScroll.chosen', function(evt) { _this.search_results_mousewheel(evt); }); + this.search_results.bind('touchstart.chosen', function(evt) { + _this.search_results_touchstart(evt); + }); + this.search_results.bind('touchmove.chosen', function(evt) { + _this.search_results_touchmove(evt); + }); + this.search_results.bind('touchend.chosen', function(evt) { + _this.search_results_touchend(evt); + }); this.form_field_jq.bind("chosen:updated.chosen", function(evt) { _this.results_update_field(evt); }); @@ -592,6 +661,9 @@ this.form_field_jq.bind("chosen:open.chosen", function(evt) { _this.container_mousedown(evt); }); + this.form_field_jq.bind("chosen:close.chosen", function(evt) { + _this.input_blur(evt); + }); this.search_field.bind('blur.chosen', function(evt) { _this.input_blur(evt); }); @@ -604,6 +676,12 @@ this.search_field.bind('focus.chosen', function(evt) { _this.input_focus(evt); }); + this.search_field.bind('cut.chosen', function(evt) { + _this.clipboard_event_checker(evt); + }); + this.search_field.bind('paste.chosen', function(evt) { + _this.clipboard_event_checker(evt); + }); if (this.is_multiple) { return this.search_choices.bind('click.chosen', function(evt) { _this.choices_click(evt); @@ -616,7 +694,7 @@ }; Chosen.prototype.destroy = function() { - $(document).unbind("click.chosen", this.click_test_action); + $(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action); if (this.search_field[0].tabIndex) { this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex; } @@ -653,7 +731,7 @@ if (this.is_multiple) { this.search_field.val(""); } - $(document).bind('click.chosen', this.click_test_action); + $(this.container[0].ownerDocument).bind('click.chosen', this.click_test_action); this.results_show(); } else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) { evt.preventDefault(); @@ -671,9 +749,10 @@ }; Chosen.prototype.search_results_mousewheel = function(evt) { - var delta, _ref1, _ref2; - - delta = -((_ref1 = evt.originalEvent) != null ? _ref1.wheelDelta : void 0) || ((_ref2 = evt.originialEvent) != null ? _ref2.detail : void 0); + var delta; + if (evt.originalEvent) { + delta = -evt.originalEvent.wheelDelta || evt.originalEvent.detail; + } if (delta != null) { evt.preventDefault(); if (evt.type === 'DOMMouseScroll') { @@ -690,7 +769,7 @@ }; Chosen.prototype.close_field = function() { - $(document).unbind("click.chosen", this.click_test_action); + $(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action); this.active_field = false; this.results_hide(); this.container.removeClass("chosen-container-active"); @@ -707,7 +786,9 @@ }; Chosen.prototype.test_active_click = function(evt) { - if (this.container.is($(evt.target).closest('.chosen-container'))) { + var active_container; + active_container = $(evt.target).closest('.chosen-container'); + if (active_container.length && this.container[0] === active_container[0]) { return this.active_field = true; } else { return this.close_field(); @@ -722,7 +803,7 @@ this.search_choices.find("li.search-choice").remove(); } else if (!this.is_multiple) { this.single_set_selected_text(); - if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) { + if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold && !this.create_option) { this.search_field[0].readOnly = true; this.container.addClass("chosen-container-single-nosearch"); } else { @@ -741,7 +822,6 @@ Chosen.prototype.result_do_highlight = function(el) { var high_bottom, high_top, maxHeight, visible_bottom, visible_top; - if (el.length) { this.result_clear_highlight(); this.result_highlight = el; @@ -774,13 +854,13 @@ return false; } this.container.addClass("chosen-with-drop"); - this.form_field_jq.trigger("chosen:showing_dropdown", { - chosen: this - }); this.results_showing = true; this.search_field.focus(); this.search_field.val(this.search_field.val()); - return this.winnow_results(); + this.winnow_results(); + return this.form_field_jq.trigger("chosen:showing_dropdown", { + chosen: this + }); }; Chosen.prototype.update_results_content = function(content) { @@ -800,7 +880,6 @@ Chosen.prototype.set_tab_index = function(el) { var ti; - if (this.form_field.tabIndex) { ti = this.form_field.tabIndex; this.form_field.tabIndex = -1; @@ -810,7 +889,6 @@ Chosen.prototype.set_label_behavior = function() { var _this = this; - this.form_field_label = this.form_field_jq.parents("label"); if (!this.form_field_label.length && this.form_field.id.length) { this.form_field_label = $("label[for='" + this.form_field.id + "']"); @@ -838,7 +916,6 @@ Chosen.prototype.search_results_mouseup = function(evt) { var target; - target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); if (target.length) { this.result_highlight = target; @@ -849,7 +926,6 @@ Chosen.prototype.search_results_mouseover = function(evt) { var target; - target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); if (target) { return this.result_do_highlight(target); @@ -865,7 +941,6 @@ Chosen.prototype.choice_build = function(item) { var choice, close_link, _this = this; - choice = $('
  • ', { "class": "search-choice" }).html("" + item.html + ""); @@ -904,8 +979,8 @@ }; Chosen.prototype.results_reset = function() { + this.reset_single_select_options(); this.form_field.options[0].selected = true; - this.selected_option_count = null; this.single_set_selected_text(); this.show_search_field_default(); this.results_reset_cleanup(); @@ -921,10 +996,13 @@ }; Chosen.prototype.result_select = function(evt) { - var high, item, selected_index; - + var high, item; if (this.result_highlight) { high = this.result_highlight; + if (high.hasClass("create-option")) { + this.select_create_option(this.search_field.val()); + return this.results_hide(); + } this.result_clear_highlight(); if (this.is_multiple && this.max_selected_options <= this.choices_count()) { this.form_field_jq.trigger("chosen:maxselected", { @@ -935,14 +1013,8 @@ if (this.is_multiple) { high.removeClass("active-result"); } else { - if (this.result_single_selected) { - this.result_single_selected.removeClass("result-selected"); - selected_index = this.result_single_selected[0].getAttribute('data-option-array-index'); - this.results_data[selected_index].selected = false; - } - this.result_single_selected = high; + this.reset_single_select_options(); } - high.addClass("result-selected"); item = this.results_data[high[0].getAttribute("data-option-array-index")]; item.selected = true; this.form_field.options[item.options_index].selected = true; @@ -981,7 +1053,6 @@ Chosen.prototype.result_deselect = function(pos) { var result_data; - result_data = this.results_data[pos]; if (!this.form_field.options[result_data.options_index].disabled) { result_data.selected = false; @@ -1021,7 +1092,6 @@ Chosen.prototype.winnow_results_set_highlight = function() { var do_high, selected_results; - selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : []; do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first(); if (do_high != null) { @@ -1031,10 +1101,42 @@ Chosen.prototype.no_results = function(terms) { var no_results_html; - no_results_html = $('
  • ' + this.results_none_found + ' ""
  • '); no_results_html.find("span").first().html(terms); - return this.search_results.append(no_results_html); + this.search_results.append(no_results_html); + return this.form_field_jq.trigger("chosen:no_results", { + chosen: this + }); + }; + + Chosen.prototype.show_create_option = function(terms) { + var create_option_html; + create_option_html = $('
  • ' + this.create_option_text + ': "' + terms + '"
  • '); + return this.search_results.append(create_option_html); + }; + + Chosen.prototype.create_option_clear = function() { + return this.search_results.find(".create-option").remove(); + }; + + Chosen.prototype.select_create_option = function(terms) { + if ($.isFunction(this.create_option)) { + return this.create_option.call(this, terms); + } else { + return this.select_append_option({ + value: terms, + text: terms + }); + } + }; + + Chosen.prototype.select_append_option = function(options) { + var option; + option = $('