Full Code of jdorn/php-reports for AI

master 9b385414f062 cached
150 files
1.3 MB
408.9k tokens
423 symbols
1 requests
Download .txt
Showing preview only (1,399K chars total). Download the full file or copy to clipboard to get everything.
Repository: jdorn/php-reports
Branch: master
Commit: 9b385414f062
Files: 150
Total size: 1.3 MB

Directory structure:
gitextract_kc8to4kf/

├── .gitignore
├── .htaccess
├── LICENSE
├── README.md
├── classes/
│   ├── filters/
│   │   ├── barFilter.php
│   │   ├── classFilter.php
│   │   ├── dateFilter.php
│   │   ├── drilldownFilter.php
│   │   ├── geoipFilter.php
│   │   ├── hideFilter.php
│   │   ├── htmlFilter.php
│   │   ├── imgsizeFilter.php
│   │   ├── linkFilter.php
│   │   ├── numberFilter.php
│   │   ├── paddingFilter.php
│   │   ├── preFilter.php
│   │   └── twigFilter.php
│   ├── headers/
│   │   ├── ChartHeader.php
│   │   ├── ColumnsHeader.php
│   │   ├── FilterHeader.php
│   │   ├── FormattingHeader.php
│   │   ├── IncludeHeader.php
│   │   ├── InfoHeader.php
│   │   ├── OptionsHeader.php
│   │   ├── RollupHeader.php
│   │   ├── VariableHeader.php
│   │   └── deprecated/
│   │       ├── CacheHeader.php
│   │       ├── CautionHeader.php
│   │       ├── ColumnHeader.php
│   │       ├── CreatedHeader.php
│   │       ├── DatabaseHeader.php
│   │       ├── DescriptionHeader.php
│   │       ├── DetailHeader.php
│   │       ├── MongodatabaseHeader.php
│   │       ├── NameHeader.php
│   │       ├── NoteHeader.php
│   │       ├── OptionHeader.php
│   │       ├── PlotHeader.php
│   │       ├── StatusHeader.php
│   │       ├── TotalHeader.php
│   │       ├── TotalsHeader.php
│   │       ├── TypeHeader.php
│   │       └── ValueHeader.php
│   ├── report_formats/
│   │   ├── ChartReportFormat.php
│   │   ├── CsvReportFormat.php
│   │   ├── DebugReportFormat.php
│   │   ├── HtmlReportFormat.php
│   │   ├── JsonReportFormat.php
│   │   ├── RawReportFormat.php
│   │   ├── SqlReportFormat.php
│   │   ├── TableReportFormat.php
│   │   ├── TextReportFormat.php
│   │   ├── XlsReportBase.php
│   │   ├── XlsReportFormat.php
│   │   ├── XlsxReportFormat.php
│   │   └── XmlReportFormat.php
│   └── report_types/
│       ├── AdoPivotReportType.php
│       ├── AdoReportType.php
│       ├── MongoReportType.php
│       ├── MysqlReportType.php
│       ├── PdoReportType.php
│       └── PhpReportType.php
├── composer.json
├── config/
│   └── config.php.sample
├── index.php
├── lib/
│   ├── PhpReports/
│   │   ├── FilterBase.php
│   │   ├── HeaderBase.php
│   │   ├── PhpReports.php
│   │   ├── Report.php
│   │   ├── ReportFormatBase.php
│   │   ├── ReportTypeBase.php
│   │   └── ReportValue.php
│   ├── adodb/
│   │   └── pivottable.inc.php
│   └── simplediff/
│       └── SimpleDiff.php
├── public/
│   ├── css/
│   │   ├── bootstrap-multiselect.css
│   │   ├── datepicker.css
│   │   ├── daterangepicker-bs3.css
│   │   ├── jquery.dataTables.css
│   │   ├── prettify.css
│   │   ├── report.css
│   │   ├── report_list.css
│   │   ├── timeline.css
│   │   └── typeahead.js-bootstrap.css
│   └── js/
│       ├── ace/
│       │   ├── ace.js
│       │   ├── ext-searchbox.js
│       │   ├── ext-spellcheck.js
│       │   ├── ext-static_highlight.js
│       │   ├── ext-textarea.js
│       │   ├── mode-diff.js
│       │   ├── mode-html.js
│       │   ├── mode-javascript.js
│       │   ├── mode-json.js
│       │   ├── mode-php.js
│       │   ├── mode-sql.js
│       │   ├── mode-text.js
│       │   ├── mode-xml.js
│       │   ├── mode-yaml.js
│       │   ├── theme-eclipse.js
│       │   ├── worker-javascript.js
│       │   ├── worker-json.js
│       │   └── worker-php.js
│       ├── bootstrap-datepicker.js
│       ├── bootstrap-multiselect.js
│       ├── daterangepicker-1.3.2.js
│       ├── html5shiv.js
│       ├── jquery.browser.js
│       ├── jquery.cookie.js
│       ├── jquery.iframe-auto-height.plugin.1.9.3.js
│       ├── jquery.stickytableheaders.js
│       ├── lang-sql.js
│       ├── prettify.js
│       ├── scripts.js
│       └── timeline.js
├── sample_dashboards/
│   └── timezones.json
├── sample_reports/
│   ├── ado/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   ├── names.ado
│   │   └── names.pivot
│   ├── mongodb/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   └── log-events.js
│   ├── mysql/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   ├── all-orders.sql
│   │   └── drilldown/
│   │       └── customer-orders.sql
│   └── php/
│       ├── README.txt
│       ├── TITLE.txt
│       ├── functions.php
│       ├── timezones.php
│       └── timezones_multiple.php
└── templates/
    └── default/
        ├── csv/
        │   └── report.twig
        ├── html/
        │   ├── blank_page.twig
        │   ├── chart_page.twig
        │   ├── chart_report.twig
        │   ├── content_only.twig
        │   ├── dashboard.twig
        │   ├── dashboard_list.twig
        │   ├── page.twig
        │   ├── report.twig
        │   ├── report_ajax_loading.twig
        │   ├── report_content.twig
        │   ├── report_editor.twig
        │   ├── report_list.twig
        │   ├── report_list_item.twig
        │   ├── report_list_table_of_contents_item.twig
        │   ├── table.twig
        │   └── variable_form.twig
        ├── sql/
        │   └── report.twig
        └── xml/
            ├── page.twig
            └── report.twig

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.idea/
config/config.php
reports/
cache/
classes/local/*.php
templates/local
vendor/


================================================
FILE: .htaccess
================================================
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

DirectoryIndex index.php


================================================
FILE: LICENSE
================================================
                   GNU LESSER GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.


  This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.

  0. Additional Definitions.

  As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.

  "The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.

  An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.

  A "Combined Work" is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".

  The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.

  The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.

  1. Exception to Section 3 of the GNU GPL.

  You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.

  2. Conveying Modified Versions.

  If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:

   a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or

   b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.

  3. Object Code Incorporating Material from Library Header Files.

  The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:

   a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the object code with a copy of the GNU GPL and this license
   document.

  4. Combined Works.

  You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:

   a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.

   c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.

   d) Do one of the following:

       0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.

       1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.

   e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)

  5. Combined Libraries.

  You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:

   a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.

   b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.

  6. Revised Versions of the GNU Lesser General Public License.

  The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.

  Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.

  If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.


================================================
FILE: README.md
================================================
Php Reports
===========

A reporting framework for managing and displaying nice looking, exportable reports from any data source, including SQL and MongoDB.

Major features include:

*   Display a report from any data source that can output tabular data (SQL, MongoDB, PHP, etc.)
*   Output reports in HTML, XML, CSV, JSON, or your own custom format
*   Add customizable parameters to a report (e.g. start date and end date)
*   Add graphs and charts with the Google Data Visualization API
*   Supports multiple database environments (e.g. Production, Staging, and Dev)
*   Fully extendable and customizable

For installation instructions and documentation, check out http://jdorn.github.io/php-reports/

If you have a question, post on the official forum - http://ost.io/@jdorn/php-reports


Basic Introduction
============

Reports are organized and grouped in directories.  Each report is it's own file.

A report consists of headers containing meta-data (e.g. name and description)
and the actual report (SQL queries, javascript, or PHP code).

All reports return rows of data which are then displayed in a sortable/searchable HTML table.

Reports can be exported to a number of formats including CSV, XLS, JSON, and XML.

The Php Reports framework ties together all these different report types, output formats, and meta-data into
a consistent interface.

Example Reports
==============

Here's an example SQL report:

```sql
-- Products That Cost At Least $X
-- VARIABLE: {"name": "min_price"}

SELECT Name, Price FROM Products WHERE Price > "{{min_price}}"
```

The set of SQL comments at the top are the report headers.  The first row is always the report name.

The `VARIABLE` header tells the report framework to prompt the user for a value before running the report.  Once provided
it will be passed into the report body ("{{min_price}}" in this example) and executed.


Here's a MongoDB report:

```js
// List of All Foods
// OPTIONS: {"mongodatabase": "MyDatabase"}
// VARIABLE: {
//   "name": "include_inactive",
//   "display": "Include Inactive?",
//   "type": "select",
//   "options": ["yes","no"]
// }

var query = {'type': 'food'};

if(include_inactive == 'no') {
    query.status = 'active';
}

var result = db.Products.find(query);

printjson(result);
```

As you can see, the structure is very similar.  MongoDB reports use javascript style comments for the headers, but everything else remains the same.

You can populate the 'db' variable by specifying the "mongodatabase" option.


Here's a PHP Report:

```php
<?php
//List of Payment Charges
//This connects to the Stripe Payments api and shows a list of charges
//INCLUDE: /stripe.php
//VARIABLE: {"name": "count", "display": "Number to Display"}

if($count > 100 || $count < 1) throw new Exception("Count must be between 1 and 100");

$charges = Stripe_Charge::all(array("count" => $count));

$rows = array();
foreach($charges as $charge) {
    $rows[] = array(
        'Charge Id'=>$charge->id,
        'Amount'=>number_format($charge->amount/100,2),
        'Date'=>date('Y-m-d',$charge->created)
    );
}

echo json_encode($rows);
?>
```
Again, the header format is very similar.  

The INCLUDE header includes another report within the running one.  Below is example content of /stripe.php:

```php
<?php
//Stripe PHP Included Report
//You can have headers here too; even nested INCLUDE headers!
//Some headers will even bubble up to the parent, such as the VARIABLE header

//include the Stripe API client
require_once('lib/Stripe/Stripe.php');

//set the Stripe api key
Stripe::setApiKey("123456");
?>
```

Hopefully, you can begin to see the power of Php Reports.

For full documentation and information on getting started, check out http://jdorn.github.io/php-reports/


================================================
FILE: classes/filters/barFilter.php
================================================
<?php
class barFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		if(isset($options['width'])) $max = $options['width'];
		else $max = 200;
		
		$width = round($max*($value->getValue()/max($report->options['Values'][$value->key])));
		
		$value->setValue("<div style='width: ".$width."px;' class='bar'></div>"."<span style='color:#999; font-size:.8em; vertical-align:middle;'>".$value->getValue(true)."</span>",true);
		
		return $value;
	}
}


================================================
FILE: classes/filters/classFilter.php
================================================
<?php
class classFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		$value->addClass($options['class']);
		
		return $value;
	}
}


================================================
FILE: classes/filters/dateFilter.php
================================================
<?php
class dateFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		if(!isset($options['format'])) $options['format'] = (isset(PhpReports::$config['default_date_format'])? PhpReports::$config['default_date_format'] : 'Y-m-d H:i:s');
		if(!isset($options['database'])) $options['database'] = $report->options['Database'];
		
		$time = strtotime($value->getValue());
		
		//if the time couldn't be parsed, just return the original value
		if(!$time) {
			return $value;
		}
		
		//if a timezone correction is needed for the database being selected from
		$environment = $report->getEnvironment();
		if(isset($environment[$options['database']]['time_offset'])) {
			$time_offset = -1*$environment[$options['database']]['time_offset'];
			
			$time = strtotime((($time_offset > 0)? '+' : '-').abs($time_offset).' hours',$time);
		}
		
		$value->setValue(date($options['format'],$time));
		
		return $value;
	}
}


================================================
FILE: classes/filters/drilldownFilter.php
================================================
<?php
class drilldownFilter extends linkFilter {	
	public static function filter($value, $options = array(), &$report, &$row) {
		if(!isset($options['macros'])) $options['macros'] = array();
		
		//determine report
		//list of reports to try
		$try = array();
		
		//relative to reportDir
		if($options['report']{0} === '/') {
			$try[] = substr($options['report'],1);
		}
		//relative to parent report
		else {
			$temp = explode('/',$report->report);
			array_pop($temp);
			$try[] = implode('/',$temp).'/'.$options['report'];
			$try[] = $options['report'];
		}
		
		//see if the file exists directly
		$found = false;
		$path = '';
		foreach($try as $report_name) {			
			if(file_exists(PhpReports::$config['reportDir'].'/'.$report_name)) {
				$path = $report_name;
				$found = true;
				break;
			}
		}
		
		//see if the report is missing a file extension
		if(!$found) {
			foreach($try as $report_name) {
				$possible_reports = glob(PhpReports::$config['reportDir'].'/'.$report_name.'.*');
			
				if($possible_reports) {
					$path = substr($possible_reports[0],strlen(PhpReports::$config['reportDir'].'/'));
					$found = true;
					break;
				}
			}
		}
		
		if(!$found) {
			return $value;
		}
				
		$url = PhpReports::$request->base.'/report/html/?report='.$path;

		$macros = array();
		foreach($options['macros'] as $k=>$v) {
			//if the macro needs to be replaced with the value of another column
			if(isset($v['column'])) {
				if(isset($row[$v['column']])) {
					$v = $row[$v['column']];
				}
				else $v = "";
			}
			//if the macro is just a constant
			elseif(isset($v['constant'])) {
				$v = $v['constant'];
			}
			
			$macros[$k] = $v;
		}
		
		$macros = array_merge($report->macros,$macros);
		unset($macros['host']);
		
		foreach($macros as $k=>$v) {									
			if(is_array($v)) {
				foreach($v as $v2) {
					$url .= '&macros['.$k.'][]='.$v2;
				}
			}
			else {
				$url.='&macros['.$k.']='.$v;
			}
		}
		
		$options = array(
			'url'=>$url
		);
		
		return parent::filter($value, $options, $report, $row);
	}
}


================================================
FILE: classes/filters/geoipFilter.php
================================================
<?php
class geoipFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		$record = geoip_record_by_name($value->getValue());
		
		if($record) {
			$display = '';
			
			$display = $record['city'];
			if($record['country_code'] !== 'US') {
				$display .= ' '.$record['country_name'];
			}
			else {
				$display .= ', '.$record['region'];
			}
			
			$value->setValue($display);
			
			$value->chart_value = array('Latitude'=>$record['latitude'],'Longitude'=>$record['longitude'],'Location'=>$display);
		}
		else {
			$value->chart_value = array('Latitude'=>0, 'Longitude'=>0, 'Location'=>'Unknown');
		}
		
		return $value;
	}
}


================================================
FILE: classes/filters/hideFilter.php
================================================
<?php
class hideFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		return false;
	}
}


================================================
FILE: classes/filters/htmlFilter.php
================================================
<?php
class htmlFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		$value->is_html = true;
		return $value;
	}
}


================================================
FILE: classes/filters/imgsizeFilter.php
================================================
<?php
class imgsizeFilter extends FilterBase {	
	static $default_format = '{{ geometry.width }}x{{ geometry.height }} {{ compression }}, {{ fileSize }}';
	
	public static function filter($value, $options = array(), &$report, &$row) {
		$handle = fopen($value->getValue(), 'rb');
		$img = new Imagick();
		$img->readImageFile($handle);
		$data = $img->identifyImage();
		
		if(!isset($options['format'])) $options['format'] = self::$default_format;
		
		$value->setValue(PhpReports::renderString($options['format'], $data));
		
		return $value;
	}
}


================================================
FILE: classes/filters/linkFilter.php
================================================
<?php
class linkFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		if(!$value->getValue()) return $value;
		
		$url = isset($options['url'])? $options['url'] : $value->getValue();
		$attr = (isset($options['blank']) && $options['blank'])? ' target="_blank"' : '';
		$display = isset($options['display'])? $options['display'] : $value->getValue();

		$html = '<a href="'.$url.'"'.$attr.'>'.$display.'</a>';
		
		$value->setValue($html, true);
		
		return $value;
	}
}


================================================
FILE: classes/filters/numberFilter.php
================================================
<?php
/**
 * Created by JetBrains PhpStorm.
 * User: lrzanek
 * Date: 30.08.13
 * Time: 15:37
 * To change this template use File | Settings | File Templates.
 */
class numberFilter extends FilterBase {
    public static function filter($value, $options = array(), &$report, &$row) {
        $decimals = $options['decimals'] ? $options['decimals'] : 0;
        $dec_sepr = $options['decimal_sep'] ? $options['decimal_sep'] : ',';
        $thousand = $options['thousands_sep'] ? $options['thousands_sep'] : ' ';
        if (is_numeric($value->getValue())) {
            $value->setValue(number_format($value->getValue(), $decimals, $dec_sepr, $thousand), true);
        }

        return $value;
    }
}


================================================
FILE: classes/filters/paddingFilter.php
================================================
<?php
class paddingFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		
		if($options['direction'] === 'r') {
			$value->addClass('right');
		}
		elseif($options['direction'] === 'l') {
			$value->addClass('left');
		}
		
		return $value;
	}
}


================================================
FILE: classes/filters/preFilter.php
================================================
<?php
class preFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		$value->setValue("<pre>".$value->getValue(true)."</pre>",true);
		
		return $value;
	}
}


================================================
FILE: classes/filters/twigFilter.php
================================================
<?php
class twigFilter extends FilterBase {	
	public static function filter($value, $options = array(), &$report, &$row) {
		// If this is html
		$html = isset($options['html'])? $options['html'] : false;
		
		$template = isset($options['template'])? $options['template'] : $value->getValue();
		
		$result = PhpReports::renderString($template,array(
			"value"=>$value->getValue(),
			"row"=>$row
		));
		
		$value->setValue($result, $html);
		
		return $value;
	}
}


================================================
FILE: classes/headers/ChartHeader.php
================================================
<?php
class ChartHeader extends HeaderBase {
	static $validation = array(
		'columns'=>array(
			'type'=>'array',
			'default'=>array()
		),
		'dataset'=>array(
			'default'=>0
		),
		'type'=>array(
			'type'=>'enum',
			'values'=>array(
				'LineChart',
				'GeoChart',
				'AnnotatedTimeLine',
				'BarChart',
				'ColumnChart',
				'Timeline',
				'AreaChart',
				'Histogram',
				'ComboChart',
				'BubbleChart',
				'CandlestickChart',
				'Gauge',
				'Map',
				'PieChart',
				'Sankey',
				'ScatterChart',
				'SteppedAreaChart',
				'WordTree',
				'Gauge',
				'Gantt',
				// Material charts
				'Bar',
				'Line',
			),
			'default'=>'LineChart'
		),
		'title'=>array(
			'type'=>'string',
			'default'=>''
		),
		'width'=>array(
			'type'=>'string',
			'default'=>'100%'
		),
		'height'=>array(
			'type'=>'string',
			'default'=>'400px'
		),
		'xhistogram'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'buckets'=>array(
			'type'=>'number',
			'default'=>0
		),
		'omit-totals'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'omit-total'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'rotate-x-labels'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'grid'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'timefmt'=>array(
			'type'=>'string',
			'default'=>''
		),
		'xformat'=>array(
			'type'=>'string',
			'default'=>''
		),
		'yrange'=>array(
			'type'=>'string',
			'default'=>''
		),
		'all'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'colors'=>array(
			'type'=>'array',
			'default'=>array()
		),
		'roles'=>array(
			'type'=>'object',
			'default'=>array()
		),
		'markers'=>array(
			'type'=>'boolean',
			'default'=>false
		),
    'omit-columns'=>array(
        'type'=>'array',
        'default'=>array()
    ),
		'options'=>array(
			'type'=>'object',
			'default'=>array()
		)
	);
	
	public static function init($params, &$report) {
		$report->exportHeader('Chart',$params);

		if(!isset($params['type'])) {
			$params['type'] = 'LineChart';
		}
		
		if(isset($params['omit-total'])) {
			$params['omit-totals'] = $params['omit-total'];
			unset($params['omit-total']);
		}
		
		if(!isset($report->options['Charts'])) $report->options['Charts'] = array();
		
		if(isset($params['width'])) $params['width'] = self::fixDimension($params['width']);
		if(isset($params['height'])) $params['height'] = self::fixDimension($params['height']);
		
		$params['num'] = count($report->options['Charts'])+1;
		$params['Rows'] = array();
		
		$report->options['Charts'][] = $params;
		
		$report->options['has_charts'] = true;

	}
	protected static function fixDimension($dim) {		
		if(preg_match('/^[0-9]+$/',$dim)) $dim .= "px";
		return $dim;
	}
	
	public static function parseShortcut($value) {
		$params = explode(',',$value);
		$value = array();
		foreach($params as $param) {
			$param = trim($param);
			if(strpos($param,'=') !== false) {							
				list($key,$val) = explode('=',$param,2);
				$key = trim($key);
				$val = trim($val);
				
				//some parameters can have multiple values separated by ":"
				if(in_array($key,array('x','y','colors'),true)) {
					$val = explode(':',$val);
				}
			}
			else {
				$key = $param;
				$val = true;
			}
			
			$value[$key] = $val;
		}
		
		if(isset($value['x'])) $value['columns'] = $value['x'];
		else $value['columns'] = array(1);
		
		if(isset($value['y'])) $value['columns'] = array_merge($value['columns'],$value['y']);
		else $value['all'] = true;
		
		unset($value['x']);
		unset($value['y']);
		
		return $value;
	}
	
	protected static function getRowInfo(&$rows, $params, $num, &$report) {
		$cols = array();
		
		//expand columns
		$chart_rows = array();
		foreach($rows as $k=>$row) {			
			$vals = array();
			
			if($k===0) {
				$i=1;
				$unsorted = 1000;
				foreach($row['values'] as $key=>$value) {
					if (($temp = array_search($row['values'][$key]->i, $report->options['Charts'][$num]['columns']))!==false) {
						$cols[$temp] = $key;
					} elseif (($temp = array_search($row['values'][$key]->key, $report->options['Charts'][$num]['columns']))!==false) {
						$cols[$temp] = $key;
					}
					//if all columns are included, add after any specifically defined ones
					elseif($report->options['Charts'][$num]['all']) {
						$cols[$unsorted] = $key;
						$unsorted ++;
					}
				}
				
				ksort($cols);
			}
			
			foreach($cols as $key) {
				if(isset($row['values'][$key]->chart_value) && is_array($row['values'][$key]->chart_value)) {
					foreach($row['values'][$key]->chart_value as $ckey=>$cval) {
						$temp = new ReportValue($row['values'][$key]->i, $ckey, trim($cval,'%$ '));
						$temp->setValue($cval);
						$vals[] = $temp;
					}
				} else {
					$temp = new ReportValue($row['values'][$key]->i, $row['values'][$key]->key, $row['values'][$key]->original_value);
					$temp->setValue(trim($row['values'][$key]->getValue(),'%$ '));
					$vals[] = $temp;
				}
			}
			
			$chart_rows[] = $vals;
		}
		
		//determine column types
		$types = array();
		foreach($chart_rows as $i=>$row) {
			foreach($row as $k=>$v) {
				$type = self::determineDataType($v->original_value);
				//if the value is null, it doesn't influence the column type
				if(!$type) {
					$chart_rows[$i][$k]->setValue(null);
					continue;
				}
				//if we don't know the column type yet, set it to this row's value
				elseif(!isset($types[$k])) $types[$k] = $type;
				//if any row has a string value for the column, the whole column is a string type
				elseif($type === 'string') $types[$k] = 'string';
				//if the column is currently a date and this row is a time/datetime, set the column to datetime type
				elseif($types[$k] === 'date' && in_array($type,array('timeofday','datetime'))) $types[$k] = 'datetime';
				//if the column is currently a time and this row is a date/datetime, set the column to datetime type
				elseif($types[$k] === 'timeofday' && in_array($type,array('date','datetime'))) $types[$k] = 'datetime';
				//if the column is currently a date and this row is a number set the column type to number
				elseif($types[$k] === 'date' && $type === 'number') $types[$k] = 'number';
			}
		}
		
		$report->options['Charts'][$num]['datatypes'] = $types;
		
		//build chart rows
		$report->options['Charts'][$num]['Rows'] = array();
		
		foreach($chart_rows as $i=>&$row) {
			$vals = array();
			foreach($row as $key=>$val) {			
				if(is_null($val->getValue())) {
					$val->datatype = 'null';
				}
				elseif($types[$key] === 'datetime') {
					$val->setValue(date('m/d/Y H:i:s',strtotime($val->getValue())));
					$val->datatype = 'datetime';
				}
				elseif($types[$key] === 'timeofday') {
					$val->setValue(date('H:i:s',strtotime($val->getValue())));
					$val->datatype = 'timeofday';
				}
				elseif($types[$key] === 'date') {
					$val->setValue(date('m/d/Y',strtotime($val->getValue())));
					$val->datatype = 'date';
				}
				elseif($types[$key] === 'number') {
					$val->setValue(round(floatval(preg_replace('/[^-0-9\.]*/','',$val->getValue())),6));
					$val->datatype = 'number';
				}
				else {
					$val->datatype = 'string';
				}
				
				$vals[] = $val;
			}
			
			$report->options['Charts'][$num]['Rows'][] = array(
				'values'=>$vals,
				'first'=>!$report->options['Charts'][$num]['Rows']
			);
		}
	}
	
	protected static function generateHistogramRows($rows, $column, $num_buckets) {
		$column_key = null;
		
		//if a name is given as the column, determine the column index
		if(!is_numeric($column)) {
			foreach($rows[0]['values'] as $k=>$v) {
				if($v->key == $column) {
					$column = $k;
					$column_key = $v->key;
					break;
				}
			}
		}
		//if an index is given, convert to 0-based
		else {
			$column --;
			$column_key = $rows[0]['values'][$column]->key;
		}
		
		//get a list of values for the histogram
		$vals = array();
		foreach($rows as &$row) {
			$vals[] = floatval(preg_replace('/[^0-9.]*/','',$row['values'][$column]->getValue()));
		}
		sort($vals);
		
		//determine buckets
		$count = count($vals);
		$buckets = array();
		$min = $vals[0];
		$max = $vals[$count-1];
		$step = ($max-$min)/$num_buckets;
		$old_limit = $min;
		
		for($i=1;$i<$num_buckets+1;$i++) {
			$limit = $old_limit + $step;
			
			$buckets[round($old_limit,2)." - ".round($limit,2)] = count(array_filter($vals,function($val) use($old_limit,$limit) {
				return $val >= $old_limit && $val < $limit;
			}));
			$old_limit = $limit;
		}
		
		//build chart rows
		$chart_rows = array();
		foreach($buckets as $name=>$count) {
			$chart_rows[] = array(
				'values'=>array(
					new ReportValue(1,$name,$name),
					new ReportValue(2,'value',$count)
				),
				'first'=>!$chart_rows
			);
		}
		return $chart_rows;
	}
	
	protected static function determineDataType($value) {
		if(is_null($value)) return null;
		elseif($value === '') return null;
		elseif(preg_match('/^([$%(\-+\s])*([0-9,]+(\.[0-9]+)?|\.[0-9]+)([$%(\-+\s])*$/',$value)) return 'number';
		elseif(preg_match('/^[0-2][0-9]:[0-5][0-9]:[0-5][0-9]$/',$value)) return 'timeofday';
		elseif(preg_match('/^[0-9]+(\/|-)[0-9]+/',$value) && strtotime($value)) {
			if(date('H:i:s',strtotime($value))==='00:00:00') return 'date';
			else return 'datetime';
		}
		else return 'string';
	}

	public static function beforeRender(&$report) {	
		// Expand out multiple datasets into their own charts
		$new_charts = array();
		foreach($report->options['Charts'] as $num=>$params) {
			$copy = $params;
			
			// If chart is for multiple datasets
			if(is_array($params['dataset'])) {
				foreach($params['dataset'] as $dataset) {
					$copy['dataset'] = $dataset;
					$copy['num'] = count($new_charts)+1;
					$new_charts[] = $copy;
				}
			}
			// If chart is for all datasets
			elseif($params['dataset']===true) {
				foreach($report->options['DataSets'] as $j=>$dataset) {
					$copy['dataset'] = $j;
					$copy['num'] = count($new_charts)+1;
					$new_charts[] = $copy;
				}
			}
			// If chart is for one dataset
			else {
				$copy['num'] = count($new_charts)+1;
				$new_charts[] = $copy;
			}
		}
		
		$report->options['Charts'] = $new_charts;
		
		foreach($report->options['Charts'] as $num=>&$params) {
			self::_processChart($num,$params,$params['dataset'],$report);
		}
	}
	protected static function _processChart($num, &$params, $dataset, &$report) {
		if(isset($params['xhistogram']) && $params['xhistogram']) {
			$rows = self::generateHistogramRows($report->options['DataSets'][$dataset]['rows'],$params['columns'][0],$params['buckets']);
			$params['columns'] = array(1,2);
		}
		else {
			$rows = array();
			if(isset($report->options['DataSets'])) {
				$rows = $report->options['DataSets'][$dataset]['rows'];
			}

			if(count($rows)) {
				if(!$params['columns']) $params['columns'] = range(1,count($rows[0]['values']));
			}
		}
		
		self::getRowInfo($rows, $params, $num, $report);
	}
}


================================================
FILE: classes/headers/ColumnsHeader.php
================================================
<?php
class ColumnsHeader extends HeaderBase {
	public static function init($params, &$report) {
		foreach($params['columns'] as $column=>$options) {
			if(!isset($options['type'])) throw new Exception("Must specify column type for column $column");
			$type = $options['type'];
			unset($options['type']);
			$report->addFilter($params['dataset'],$column,$type,$options);
		}
	}
	
	public static function parseShortcut($value) {
		if(preg_match('/^[0-9]+\:/',$value)) {
			$dataset = substr($value,0,strpos($value,':'));
			$value = substr($value,strlen($dataset)+1);
		}
		else {
			$dataset = 0;
		}
		
		$parts = explode(',',$value);
		$params = array();
		$i = 1;
		foreach($parts as $part) {
			$type = null;
			$options = null;
			
			$part = trim($part);
			//special cases
			//'rpadN' or 'lpadN' where N is number of spaces to pad
			if(substr($part,1,3)==='pad') {
				$type = 'padding';
				
				$options = array(
					'direction'=>$part[0],
					'spaces'=>intval(substr($part,4))
				);
			}
			//link or link(display) or link_blank or link_blank(display)
			elseif(substr($part,0,4)==='link') {
				//link(display) or link_blank(display)
				if(strpos($part,'(') !== false) {
					list($type,$display) = explode('(',substr($part,0,-1),2);
				}
				else {
					$type = $part;
					$display = 'link';
				}
				
				$blank = ($type == 'link_blank');
				$type = 'link';
				
				$options = array(
					'display'=>$display,
					'blank'=>$blank
				);
			}
			//synonyms for 'html'
			elseif(in_array($part,array('html','raw'))) {
				$type = 'html';
			}
			//url synonym for link
			elseif($part === 'url') {
				$type = 'link';
				$options = array(
					'blank'=>false
				);
			}
			elseif($part === 'bar') {
				$type = 'bar';
				$options = array();
			}
			elseif($part === 'pre') {
				$type = 'pre';
			}
			//normal case
			else {
				$type = 'class';
				$options = array(
					'class'=>$part
				);
			}
			
			$options['type'] = $type;
			
			$params[$i] = $options;
			
			$i++;
		}
		
		return array(
			'dataset'=>$dataset,
			'columns'=>$params
		);
	}
}


================================================
FILE: classes/headers/FilterHeader.php
================================================
<?php
class FilterHeader extends HeaderBase {
	static $validation = array(
		'column'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'filter'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'params'=>array(
			'type'=>'object',
			'default'=>array()
		),
		'dataset'=>array(
			'default'=>0
		)
	);
	
	public static function init($params, &$report) {
		$report->addFilter($params['dataset'],$params['column'],$params['filter'],$params['params']);
	}
	
	//in format: column, params
	//params can be a JSON object or "filter"
	//filter classes are defined in class/filters/
	//examples:
	//	"4,geoip" - apply a geoip filter to the 4th column
	//	'Ip,{"filter":"geoip"}' - apply a geoip filter to the "Ip" column
	public static function parseShortcut($value) {
		if(strpos($value,',') === false) {
			$col = "1";
			$filter = $value;
		}
		else {
			list($col,$filter) = explode(',',$value,2);
			$col = trim($col);
		}
		$filter = trim($filter);
		
		return array(
			'column'=>$col,
			'filter'=>$filter,
			'params'=>array()
		);
	}
}


================================================
FILE: classes/headers/FormattingHeader.php
================================================
<?php
class FormattingHeader extends HeaderBase {
	static $validation = array(
		'limit'=>array(
			'type'=>'number',
			'default'=>null
		),
		'noborder'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'vertical'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'table'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'showcount'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'font'=>array(
			'type'=>'string'
		),
		'nodata'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'selectable'=>array(
			'type'=>'string'
		),
		'dataset'=>array(
			'required'=>true,
			'default'=>true
		)
	);
	
	public static function init($params, &$report) {
		if(!isset($report->options['Formatting'])) $report->options['Formatting'] = array();
		$report->options['Formatting'][] = $params;
	}
	
	public static function parseShortcut($value) {
		$options = explode(',',$value);
		
		$params = array();
		
		foreach($options as $v) {
			if(strpos($v,'=')!==false) {
				list($k,$v) = explode('=',$v,2);
				$v = trim($v);
			}
			else {
				$k = $v;
				$v=true;
			}
			
			$k = trim($k);
			
			$params[$k] = $v;
		}
		
		return $params;
	}
	
	public static function beforeRender(&$report) {
		$formatting = array();
		// Expand out by dataset
		foreach($report->options['Formatting'] as $params) {
			$copy = $params;
			unset($copy['dataset']);

			if(isset($report->options['DataSets'])) {
				// Multiple datasets defined
				if(is_array($params['dataset'])) {
					foreach($params['dataset'] as $i) {
						if(isset($report->options['DataSets'][$i])) {
							if(!isset($formatting[$i])) $formatting[$i] = array();
							foreach($copy as $k=>$v) {
								$formatting[$i][$k] = $v;
							}
						}
					}
				}
				// All datasets
				elseif($params['dataset']===true) {
					foreach($report->options['DataSets'] as $i=>$dataset) {
						if(!isset($formatting[$i])) $formatting[$i] = array();
						foreach($copy as $k=>$v) {
							$formatting[$i][$k] = $v;
						}
					}
				}
				// Single dataset
				else {
					if(!isset($report->options['DataSets'][$params['dataset']])) continue;
					if(!isset($formatting[$params['dataset']])) $formatting[$params['dataset']] = array();
					foreach($copy as $k=>$v) {
						$formatting[$params['dataset']][$k] = $v;
					}
				}
			}
		}
		
		$report->options['Formatting'] = $formatting;
		
		// Apply formatting options for each dataset
		foreach($formatting as $i=>$params) {
			if(isset($params['limit']) && $params['limit']) {
				$report->options['DataSets'][$i]['rows'] = array_slice($report->options['DataSets'][$i]['rows'],0,intval($params['limit']));
			}
			if(isset($params['selectable']) && $params['selectable']) {
				$selected = array();
				
				// New style "selected_{{DATASET}}" querystring
				if(isset($_GET['selected_'.$i])) {
					$selected = $_GET['selected_'.$i];
				}
				// Old style "selected" querystring
				elseif(isset($_GET['selected'])) {	
					$selected = $_GET['selected'];
				}
				
				if($selected) {
					$selected_key = null;
					foreach($report->options['DataSets'][$i]['rows'][0]['values'] as $key=>$value) {
						if($value->key == $params['selectable']) {
							$selected_key = $key;
							break;
						}
					}
			
					if($selected_key !== null) {
						foreach($report->options['DataSets'][$i]['rows'] as $key=>$row) {
							
							if(!in_array($row['values'][$selected_key]->getValue(),$selected)) {
								unset($report->options['DataSets'][$i]['rows'][$key]);
							}
						}
						$report->options['DataSets'][$i]['rows'] = array_values($report->options['DataSets'][$i]['rows']);
					}
				}
			}
			if(isset($params['vertical']) && $params['vertical']) {
				$rows = array();
				foreach($report->options['DataSets'][$i]['rows'] as $row) {
					foreach($row['values'] as $value) {
						if(!isset($rows[$value->key])) {
							$header = new ReportValue(1, 'key', $value->key);
							$header->class = 'left lpad';
							$header->is_header = true;
							
							$rows[$value->key] = array(
								'values'=>array(
									$header
								),
								'first'=>!$rows
							);
						}
						
						$rows[$value->key]['values'][] = $value;
					}
				}
				
				$rows = array_values($rows);
				
				$report->options['DataSets'][$i]['vertical'] = $rows;
			}
			
			unset($params['vertical']);
			foreach($params as $k=>$v) {
				$report->options['DataSets'][$i][$k] = $v;
			}
		}
	}
}


================================================
FILE: classes/headers/IncludeHeader.php
================================================
<?php
class IncludeHeader extends HeaderBase {
	static $validation = array(
		'report'=>array(
			'required'=>true,
			'type'=>'string'
		)
	);
	
	public static function init($params, &$report) {
		if($params['report'][0] === '/') {
			$report_path = substr($params['report'],1);
		}
		else {
			$report_path = dirname($report->report).'/'.$params['report'];
		}
		
		
		if(!file_exists(PhpReports::$config['reportDir'].'/'.$report_path)) {
			$possible_reports = glob(PhpReports::$config['reportDir'].'/'.$report_path.'.*');
			
			if($possible_reports) {
				$report_path = substr($possible_reports[0],strlen(PhpReports::$config['reportDir'].'/'));
			}
			else {
				throw new Exception("Unknown report in INCLUDE header '$report_path'");
			}
		}
		
		$included_report = new Report($report_path);
		
		//parse any exported headers from the included report
		foreach($included_report->exported_headers as $header) {
			$report->parseHeader($header['name'],$header['params']);
		}
		
		if(!isset($report->options['Includes'])) $report->options['Includes'] = array();
		
		$report->options['Includes'][] = $included_report;
	}
	
	public static function parseShortcut($value) {
		return array(
			'report'=>$value
		);
	}
}


================================================
FILE: classes/headers/InfoHeader.php
================================================
<?php
class InfoHeader extends HeaderBase {
	static $validation = array(
		'name'=>array(
			'type'=>'string'
		),
		'description'=>array(
			'type'=>'string'
		),
		'created'=>array(
			'type'=>'string',
			'pattern'=>'/^[0-9]{4}-[0-9]{2}-[0-9]{2}/'
		),
		'note'=>array(
			'type'=>'string'
		),
		'type'=>array(
			'type'=>'string'
		),
		'status'=>array(
			'type'=>'string'
		)
	);
	
	public static function init($params, &$report) {
		foreach($params as $key=>$value) {
			$report->options[ucfirst($key)] = $value;
		}
	}
	
	// Accepts shortcut format:
	// name=My Report,description=This is My Report
	public static function parseShortcut($value) {
		$parts = explode(',',$value);
		
		$params = array();
		
		foreach($parts as $v) {
			if(strpos($v,'=')!==false) {
				list($k,$v) = explode('=',$v,2);
				$v = trim($v);
			}
			else {
				$k = $v;
				$v=true;
			}
			
			$k = trim($k);
			
			$params[$k] = $v;
		}
		
		return $params;
	}
}


================================================
FILE: classes/headers/OptionsHeader.php
================================================
<?php
class OptionsHeader extends HeaderBase {
	static $validation = array(
		'limit'=>array(
			'type'=>'number',
			'default'=>null
		),
		'access'=>array(
			'type'=>'enum',
			'values'=>array('rw','readonly'),
			'default'=>'readonly'
		),
		'noborder'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'noreport'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'vertical'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'ignore'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'table'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'showcount'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'font'=>array(
			'type'=>'string'
		),
		'stop'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'nodata'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'version'=>array(
			'type'=>'number',
			'default'=>1
		),
		'selectable'=>array(
			'type'=>'string'
		),
		'mongodatabase'=>array(
			'type'=>'string'
		),
		'database'=>array(
			'type'=>'string'
		),
		'cache'=>array(
			'min'=>0,
			'type'=>'number'
		),
		'ttl'=>array(
			'min'=>0,
			'type'=>'number'
		),
		'default_dataset'=>array(
			'type'=>'number',
			'default'=>0
		),
		'has_charts'=>array(
			'type'=>'boolean'
		)
	);
	
	public static function init($params, &$report) {
		//legacy support for the 'ttl' cache parameter
		if(isset($params['ttl'])) {
			$params['cache'] = $params['ttl'];
			unset($params['ttl']);
		}

		if(isset($params['has_charts']) && $params['has_charts']) {
			if(!isset($report->options['Charts'])) $report->options['Charts'] = array();
		}
		
		// Some parameters were moved to a 'FORMATTING' header
		// We need to catch those and add the header to the report
		$formatting_header = array();
		
		foreach($params as $key=>$value) {
			// This is a FORMATTING parameter
			if(in_array($key,array('limit','noborder','vertical','table','showcount','font','nodata','selectable'))) {
				$formatting_header[$key] = $value;
				continue;
			}
			
			//some of the keys need to be uppercase (for legacy reasons)
			if(in_array($key,array('database','mongodatabase','cache'))) $key = ucfirst($key);
			
			$report->options[$key] = $value;
			
			//if the value is different from the default, it can be exported
			if(!isset(self::$validation[$key]['default']) || ($value && $value !== self::$validation[$key]['default'])) {
				//only export some of the options
				if(in_array($key,array('access','Cache'),true)) {
					$report->exportHeader('Options',array($key=>$value));
				}
			}
		}
		
		if($formatting_header) {
			$formatting_header['dataset'] = true;
			$report->parseHeader('Formatting',$formatting_header);
		}
	}
	
	public static function parseShortcut($value) {
		$options = explode(',',$value);
		
		$params = array();
		
		foreach($options as $v) {
			if(strpos($v,'=')!==false) {
				list($k,$v) = explode('=',$v,2);
				$v = trim($v);
			}
			else {
				$k = $v;
				$v=true;
			}
			
			$k = trim($k);
			
			$params[$k] = $v;
		}
		
		return $params;
	}
}


================================================
FILE: classes/headers/RollupHeader.php
================================================
<?php
class RollupHeader extends HeaderBase {
	static $validation = array(
		'columns'=>array(
			'required'=>true,
			'type'=>'object',
			'default'=>array()
		),
		'dataset'=>array(
			'required'=>false,
			'default'=>0
		)
	);
	
	public static function init($params, &$report) {
		//make sure at least 1 column is defined
		if(empty($params['columns'])) throw new Exception("Rollup header needs at least 1 column defined");
		
		if(!isset($report->options['Rollup'])) $report->options['Rollup'] = array();
		
		// If more than one dataset is defined, add the rollup header multiple times
		if(is_array($params['dataset'])) {
			$new_params = $params;
			foreach($params['dataset'] as $dataset) {
				$new_params['dataset'] = $dataset;
				$report->options['Rollup'][] = $new_params;
			}
		}
		// Otherwise, just add one rollup header
		else {
			$report->options['Rollup'][] = $params;
		}
	}
	
	public static function beforeRender(&$report) {			
		//cache for Twig parameters for each dataset/column
		$twig_params = array();
		
		// Now that we know how many datasets we have, expand out Rollup headers with dataset->true
		$new_rollups = array();
		foreach($report->options['Rollup'] as $i=>$rollup) {
			if($rollup['dataset']===true && isset($report->options['DataSets'])) {
				$copy = $rollup;
				foreach($report->options['DataSets'] as $i=>$dataset) {
					$copy['dataset'] = $i;
					$new_rollups[] = $copy;
				}
			}
			else {
				$new_rollups[] = $rollup;
			}
		}
		$report->options['Rollup'] = $new_rollups;
		
		// First get all the values
		foreach($report->options['Rollup'] as $rollup) {			
			// If we already got twig parameters for this dataset, skip it
			if(isset($twig_params[$rollup['dataset']])) continue;
			$twig_params[$rollup['dataset']] = array();
			if(isset($report->options['DataSets'])) {
				if(isset($report->options['DataSets'][$rollup['dataset']])) {
					foreach($report->options['DataSets'][$rollup['dataset']]['rows'] as $row) {
						foreach($row['values'] as $value) {
							if(!isset($twig_params[$rollup['dataset']][$value->key])) $twig_params[$rollup['dataset']][$value->key] = array('values'=>array());
							$twig_params[$rollup['dataset']][$value->key]['values'][] = $value->getValue();
						}
					}
				}
			}
		}

		// Then, calculate other statistical properties
		foreach($twig_params as $dataset=>&$tp) {
			foreach($tp as $column=>&$params) {
				//get non-null values and sort them
				$real_values = array_filter($params['values'],function($a) {if($a === null || $a==='') return false; return true; });
				sort($real_values);
				
				$params['sum'] = array_sum($real_values);
				$params['count'] = count($real_values);
				if($params['count']) {
					$params['mean'] = $params['average'] = $params['sum'] / $params['count'];

					// Median for odd number of rows
					if($params['count']%2) {
						$params['median'] = $real_values[floor($params['count']/2)];
					}
					// Median for even number of rows
					else {
						// If the 2 middle entries are numeric, average them
						if(is_numeric($real_values[$params['count']/2-1]) && is_numeric($real_values[$params['count']/2])) {
							$params['median'] = ($real_values[$params['count']/2-1] + $real_values[$params['count']/2])/2;
						}
						// If they are not numeric, pick one of the middle entries
						else {
							$params['median'] = $real_values[$params['count']/2-1];
						}
					}

					$params['min'] = $real_values[0];
					$params['max'] = $real_values[$params['count']-1];
				}
				else {
					$params['mean'] = $params['average'] = $params['median'] = $params['min'] = $params['max'] = 0;
				}
				
				$devs = array();
				if (empty($real_values)) {
					$params['stdev'] = 0;
				} else 	if (function_exists('stats_standard_deviation')) {
					$params['stdev'] = stats_standard_deviation($real_values);
				} else {
					foreach($real_values as $v) $devs[] = pow($v - $params['mean'], 2);
					$params['stdev'] = sqrt(array_sum($devs) / (count($devs)));
				}
			}
		}
		
		//render each rollup row
		foreach($report->options['Rollup'] as $rollup) {
			if(!isset($report->options['DataSets'][$rollup['dataset']]['footer'])) $report->options['DataSets'][$rollup['dataset']]['footer'] = array();
			$columns = $rollup['columns'];
			$row = array(
				'values'=>array(),
				'rollup'=>true
			);
			
			foreach($twig_params[$rollup['dataset']] as $column=>$p) {
				if(isset($columns[$column])) {
					$p = array_merge($p,array('row'=>$twig_params[$rollup['dataset']]));

					$row['values'][] = new ReportValue(-1,$column,PhpReports::renderString($columns[$column],$p));
				}
				else {
					$row['values'][] = new ReportValue(-1,$column,null);
				}
			}
			$report->options['DataSets'][$rollup['dataset']]['footer'][] = $row;
		}
	}
}


================================================
FILE: classes/headers/VariableHeader.php
================================================
<?php
class VariableHeader extends HeaderBase {
	
	static $validation = array(
		'name'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'display'=>array(
			'type'=>'string'
		),
		'type'=>array(
			'type'=>'enum',
			'values'=>array('text','select','textarea','date','daterange'),
			'default'=>'text'
		),
		'options'=>array(
			'type'=>'array'
		),
		'default'=>array(
		
		),
		'empty'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'multiple'=>array(
			'type'=>'boolean',
			'default'=>false
		),
		'database_options'=>array(
			'type'=>'object'
		),
		'description'=>array(
			'type'=>'string'
		),
		'format'=>array(
			'type'=>'string',
			'default'=>'Y-m-d H:i:s'
		),
		'modifier_options'=>array(
			'type'=>'array'
		),
		'time_offset'=>array(
			'type'=>'number'
		),
	);
	
	public static function init($params, &$report) {		
		if(!isset($params['display']) || !$params['display']) $params['display'] = $params['name'];
		
		if(!preg_match('/^[a-zA-Z][a-zA-Z0-9_\-]*$/',$params['name'])) throw new Exception("Invalid variable name: $params[name]");
		
		//add to options
		if(!isset($report->options['Variables'])) $report->options['Variables'] = array();
		$report->options['Variables'][$params['name']] = $params;
		
		//add to macros
		if(!isset($report->macros[$params['name']]) && isset($params['default'])) {
			$report->addMacro($params['name'],$params['default']);
			
			$report->macros[$params['name']] = $params['default'];
			
			if(!isset($params['empty']) || !$params['empty']) {
				$report->is_ready = false;
			}
		}
		elseif(!isset($report->macros[$params['name']])) {
			$report->addMacro($params['name'],'');
			
			if(!isset($params['empty']) || !$params['empty']) {
				$report->is_ready = false;
			}
		}
		
		//convert newline separated strings to array for vars that support multiple values
		if($params['multiple'] && !is_array($report->macros[$params['name']])) $report->addMacro($params['name'],explode("\n",$report->macros[$params['name']]));
		
		$report->exportHeader('Variable',$params);
	}
	
	public static function parseShortcut($value) {
		list($var,$params) = explode(',',$value,2);
		$var = trim($var);
		$params = trim($params);
		
		$parts = explode(',',$params);
		$params = array(
			'name'=>$var,
			'display'=>trim($parts[0])
		);
		
		unset($parts[0]);
		
		$extra = implode(',',$parts);
		
		//just "name, label"
		if(!$extra) return $params;
		
		//if the 3rd item is "LIST", use multi-select
		if(preg_match('/^\s*LIST\s*\b/',$extra)) {
			$params['multiple'] = true;
			$extra = array_pop(explode(',',$extra,2));
		}
		
		//table.column, where clause, ALL
		if(preg_match('/^\s*[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\s*,[^,]+,\s*ALL\s*$/', $extra)) {
			list($table_column, $where, $all) = explode(',',$extra, 3);
			list($table,$column) = explode('.',$table_column,2);
			
			$params['type'] = 'select';
			
			$var_params = array(
				'table'=>$table,
				'column'=>$column,
				'all'=>true,
				'where'=>$where
			);
			
			$params['database_options'] = $var_params;
		}
		
		//table.column, ALL
		elseif(preg_match('/^\s*[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\s*,\s*ALL\s*$/', $extra)) {
			list($table_column, $all) = explode(',',$extra, 2);
			list($table,$column) = explode('.',$table_column,2);
			
			$params['type'] = 'select';
			
			$var_params = array(
				'table'=>$table,
				'column'=>$column,
				'all'=>true
			);
			
			$params['database_options'] = $var_params;
		}
		
		//table.column, where clause
		elseif(preg_match('/^\s*[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\s*,[^,]+$/', $extra)) {
			list($table_column, $where) = explode(',',$extra, 2);
			list($table,$column) = explode('.',$table_column,2);
			
			$params['type'] = 'select';
			
			$var_params = array(
				'table'=>$table,
				'column'=>$column,
				'where'=>$where
			);
			
			$params['database_options'] = $var_params;
		}
		
		//table.column
		elseif(preg_match('/^\s*[a-zA-Z0-9_\-]+\.[a-zA-Z0-9_\-]+\s*$/', $extra)) {
			list($table,$column) = explode('.',$extra,2);
			
			$params['type'] = 'select';
			
			$var_params = array(
				'table'=>$table,
				'column'=>$column
			);
			
			$params['database_options'] = $var_params;
		}
		
		//option1|option2
		elseif(preg_match('/^\s*([a-zA-Z0-9_\- ]+\|)+[a-zA-Z0-9_\- ]+$/',$extra)) {
			$options = explode('|',$extra);
			
			$params['type'] = 'select';				
			$params['options'] = $options;
		}
		
		return $params;
	}
	
	public static function afterParse(&$report) {
		$classname = $report->options['Type'].'ReportType';
		
		foreach($report->options['Variables'] as $var=>$params) {
			//if it's a select variable and the options are pulled from a database
			if(isset($params['database_options'])) {
				$classname::openConnection($report);
				$params['options'] = $classname::getVariableOptions($params['database_options'],$report);
				
				$report->options['Variables'][$var] = $params;
			}

			//if the type is daterange, parse start and end with strtotime
			if($params['type'] === 'daterange' && !empty($report->macros[$params['name']][0]) && !empty($report->macros[$params['name']][1])) {
				$start = date_create($report->macros[$params['name']][0]);
				if(!$start) throw new Exception($params['display']." must have a valid start date.");
				date_time_set($start,0,0,0);
				$report->macros[$params['name']]['start'] = date_format($start,$params['format']);

				$end = date_create($report->macros[$params['name']][1]);
				if(!$end) throw new Exception($params['display']." must have a valid end date.");
				date_time_set($end,23,59,59);
				$report->macros[$params['name']]['end'] = date_format($end,$params['format']);
			}
		}
	}
	
	public static function beforeRun(&$report) {
		foreach($report->options['Variables'] as $var=>$params) {			
			//if the type is date, parse with strtotime
			if($params['type'] === 'date' && $report->macros[$params['name']]) {
				
				$time = strtotime($report->macros[$params['name']]);
				if(!$time) throw new Exception($params['display']." must be a valid datetime value.");
				
				$report->macros[$params['name']] = date($params['format'],$time);
			}
		}
	}
}


================================================
FILE: classes/headers/deprecated/CacheHeader.php
================================================
<?php
class CacheHeader extends OptionsHeader {	
	public static function init($params, &$report) {
		trigger_error("CACHE header is deprecated.  Use the OPTIONS header with the 'cache' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		//if a cache ttl is being set
		if(is_numeric($value)) {
			return array(
				'cache'=>intval($value)
			);
		}
		//if cache is being turned off
		else {
			return array(
				'cache'=>0
			);
		}
	}
}


================================================
FILE: classes/headers/deprecated/CautionHeader.php
================================================
<?php
class CautionHeader extends HeaderBase {
	static $validation = array(
		'value'=>array(
			'required'=>true,
			'type'=>'string'
		)
	);
	
	public static function init($params, &$report) {
		trigger_error("CAUTION header is deprecated.",E_USER_DEPRECATED);
		
		$report->options['Caution'] = $params['value'];
			
		$report->exportHeader('Caution',$params);
	}
	
	public static function parseShortcut($value) {
		return array(
			'value'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/ColumnHeader.php
================================================
<?php
class ColumnHeader extends ColumnsHeader {
	public static function init($params, &$report) {
		trigger_error("COLUMN header is deprecated.  Use the COLUMNS header instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
}


================================================
FILE: classes/headers/deprecated/CreatedHeader.php
================================================
<?php
class CreatedHeader extends InfoHeader {
	public static function init($params, &$report) {
		trigger_error("CREATED header is deprecated.  Use the INFO header with the 'created' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'created'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/DatabaseHeader.php
================================================
<?php
class DatabaseHeader extends OptionsHeader {
	public static function init($params, &$report) {
		trigger_error("DATABASE header is deprecated.  Use the OPTIONS header with the 'database' parameter instead. (".$report->report.")",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'database'=>trim($value)
		);
	}
}


================================================
FILE: classes/headers/deprecated/DescriptionHeader.php
================================================
<?php
class DescriptionHeader extends InfoHeader {
	public static function init($params, &$report) {
		trigger_error("DESCRIPTION header is deprecated.  Use the INFO header with the 'description' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'description'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/DetailHeader.php
================================================
<?php
class DetailHeader extends HeaderBase {
	static $validation = array(
		'report'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'column'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'macros'=>array(
			'type'=>'object'
		)
	);
	
	public static function init($params, &$report) {
		trigger_error("DETAIL header is deprecated.  Use the FILTER header with the 'drilldown' filter instead.",E_USER_DEPRECATED);
		
		$report->addFilter($params['column'],'drilldown',$params);
	}
	
	public static function parseShortcut($value) {
		$parts = explode(',',$value,3);
		
		if(count($parts) < 2) {
			throw new Exception("Cannot parse DETAIL header '$value'");
		}
		
		$col = trim($parts[0]);
		$report_name = trim($parts[1]);
		
		if(isset($parts[2])) {
			$parts[2] = trim($parts[2]);
			$macros = array();
			$temp = explode(',',$parts[2]);
			foreach($temp as $macro) {
				$macro = trim($macro);
				if(strpos($macro,'=') !== false) {
					list($key,$val) = explode('=',$macro,2);
					$key = trim($key);
					$val = trim($val);
					
					if(in_array($val[0],array('"',"'"))) {
						$val = array(
							'constant'=>trim($val,'\'"')
						);
					}
					else {
						$val = array(
							'column'=>$val
						);
					}
					
					$macros[$key] = $val;
				}
				else {
					$macros[$macro] = $macro;
				}
			}
			
		}
		else {
			$macros = array();
		}
		
		return array(
			'report'=>$report_name,
			'column'=>$col,
			'macros'=>$macros
		);
	}
}


================================================
FILE: classes/headers/deprecated/MongodatabaseHeader.php
================================================
<?php
class MongodatabaseHeader extends OptionsHeader {
	public static function init($params, &$report) {
		trigger_error("MONGODATABASE header is deprecated.  Use the OPTIONS header with the 'mongodatabase' parameter instead. (".$report->report.")",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'mongodatabase'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/NameHeader.php
================================================
<?php
class NameHeader extends InfoHeader {
	public static function init($params, &$report) {
		trigger_error("NAME header is deprecated.  Use the INFO header with the 'name' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {		
		return array(
			'name'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/NoteHeader.php
================================================
<?php
class NoteHeader extends InfoHeader {
	public static function init($params, &$report) {
		trigger_error("NOTE header is deprecated.  Use the INFO header with the 'note' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'note'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/OptionHeader.php
================================================
<?php
class OptionHeader extends OptionsHeader {
	public static function init($params, &$report) {
		trigger_error("OPTION header is deprecated.  Use the OPTIONS header instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
}


================================================
FILE: classes/headers/deprecated/PlotHeader.php
================================================
<?php
//This is for backwards compatibility
//The Chart header used to be called Plot
class PlotHeader extends ChartHeader {
	public static function init($params, &$report) {
		trigger_error("PLOT header is deprecated.  Use the CHART header instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
}


================================================
FILE: classes/headers/deprecated/StatusHeader.php
================================================
<?php
class StatusHeader extends InfoHeader {	
	public static function init($params, &$report) {
		trigger_error("STATUS header is deprecated.  Use the INFO header with the 'status' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'status'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/TotalHeader.php
================================================
<?php
class TotalHeader extends TotalsHeader {
	public static function init($params, &$report) {
		trigger_error("TOTAL header is deprecated.  Use the ROLLUP header instead.",E_USER_DEPRECATED);
	}
}


================================================
FILE: classes/headers/deprecated/TotalsHeader.php
================================================
<?php
class TotalsHeader extends HeaderBase {
	static $validation = array(
		'value'=>array(
			'required'=>true,
			'type'=>'string'
		)
	);
	
	public static function init($params, &$report) {
		trigger_error("TOTALS header is deprecated.  Use the ROLLUP header instead.",E_USER_DEPRECATED);
	}
	
	public static function parseShortcut($value) {
		return array(
			'value'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/TypeHeader.php
================================================
<?php
class TypeHeader extends InfoHeader {
	public static function init($params, &$report) {
		trigger_error("TYPE header is deprecated.  Use the INFO header with the 'type' parameter instead.",E_USER_DEPRECATED);
		
		return parent::init($params, $report);
	}
	
	public static function parseShortcut($value) {
		return array(
			'type'=>$value
		);
	}
}


================================================
FILE: classes/headers/deprecated/ValueHeader.php
================================================
<?php
class ValueHeader extends HeaderBase {
	static $validation = array(
		'name'=>array(
			'required'=>true,
			'type'=>'string'
		),
		'value'=>array(
			'required'=>true
		)
	);
	
	public static function init($params, &$report) {
		trigger_error("VALUE header is deprecated.  Use the VARIABLE header with a 'default' parameter instead.",E_USER_DEPRECATED);
		
		if(isset($report->options['Variables'][$params['name']])) {
			if($report->macros[$params['name']]) return;
			
			$report->options['Variables'][$params['name']]['default'] = $params['value'];
			$report->macros[$params['name']] = $params['value'];
			
			$report->exportHeader('Value',$params);
		}
		else {
			throw new Exception("Providing value for unknown variable $params[name]");
		}
	}
	
	public static function parseShortcut($value) {
		if(strpos($value,',') === false) {
			throw new Exception("Invalid value '$value'");
		}
		list($name,$value) = explode(',',$value);
		$var = trim($name);
		$default = trim($value);
		
		return array(
			'name'=>$var,
			'value'=>$default
		);
	}
}


================================================
FILE: classes/report_formats/ChartReportFormat.php
================================================
<?php
class ChartReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		if(!$report->options['has_charts']) return;
		
		//always use cache for chart reports
		//$report->use_cache = true;
		
		$result = $report->renderReportPage('html/chart_report');
		
		echo $result;
	}
}


================================================
FILE: classes/report_formats/CsvReportFormat.php
================================================
<?php
class CsvReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		//always use cache for CSV reports
		$report->use_cache = true;
		
		$file_name = preg_replace(array('/[\s]+/','/[^0-9a-zA-Z\-_\.]/'),array('_',''),$report->options['Name']);
		
		header("Content-type: application/csv");
		header("Content-Disposition: attachment; filename=".$file_name.".csv");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		$i=0;
		if(isset($_GET['dataset'])) $i = $_GET['dataset'];
		elseif(isset($report->options['default_dataset'])) $i = $report->options['default_dataset'];
		$i = intval($i);
		
		$data = $report->renderReportPage('csv/report',array(
			'dataset'=>$i
		));
		
		if(trim($data)) echo $data;
	}
}


================================================
FILE: classes/report_formats/DebugReportFormat.php
================================================
<?php
class DebugReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		header("Content-type: text/plain");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		$content = "****************** Raw Report File ******************\n\n".$report->getRaw()."\n\n\n";
		$content .= "****************** Macros ******************\n\n".print_r($report->macros,true)."\n\n\n";
		$content .= "****************** All Report Options ******************\n\n".print_r($report->options,true)."\n\n\n";
		
		if($report->is_ready) {
			$report->run();
		
			$content .= "****************** Generated Query ******************\n\n".print_r($report->options['Query'],true)."\n\n\n";
			
			$content .= "****************** Report Rows ******************\n\n".print_r($report->options['DataSets'],true)."\n\n\n";
		}
		
		echo $content;
	}
}


================================================
FILE: classes/report_formats/HtmlReportFormat.php
================================================
<?php
class HtmlReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		
		//determine if this is an asyncronous report or not		
		$report->async = !isset($request->query['content_only']);
		if(isset($request->query['no_async'])) $report->async = false;
		
		//if we're only getting the report content
		if(isset($request->query['content_only'])) {
			$template = 'html/content_only';
		}
		else {
			$template = 'html/report';
		}
		
		try {
			$additional_vars = array();
			if(isset($request->query['no_charts'])) $additional_vars['no_charts'] = true;
			
			$html = $report->renderReportPage($template,$additional_vars);
			echo $html;
		}
		catch(Exception $e) {			
			if(isset($request->query['content_only'])) {
				$template = 'html/blank_page';
			}
			
			$vars = array(
				'title'=>$report->report,
				'header'=>'<h2>There was an error running your report</h2>',
				'error'=>$e->getMessage(),
				'content'=>"<h2>Report Query</h2>".$report->options['Query_Formatted'],
			);
			
			echo PhpReports::render($template, $vars);
		}
	}
}


================================================
FILE: classes/report_formats/JsonReportFormat.php
================================================
<?php
class JsonReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {		
		header("Content-type: application/json");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		//run the report
		$report->run();
		
		if(!$report->options['DataSets']) return;

		$result = array();
		if(isset($_GET['datasets'])) {
			$datasets = $_GET['datasets'];
			// If all the datasets should be included
			if($datasets === 'all') {
				$datasets = array_keys($report->options['DataSets']);
			}
			// If just a single dataset was specified, make it an array
			else if(!is_array($datasets)) {
				$datasets = explode(',',$datasets);
			}
			
			foreach($datasets as $i) {
				$result[] = self::getDataSet($i, $report);
			}
		}
		else {
			$i=0;
			if(isset($_GET['dataset'])) $i = $_GET['dataset'];
			elseif(isset($report->options['default_dataset'])) $i = $report->options['default_dataset'];
			$i = intval($i);
			
			$dataset = self::getDataSet($i, $report);
			$result = $dataset['rows'];
		}

		if(defined('JSON_PRETTY_PRINT')) {
			echo json_encode($result,JSON_PRETTY_PRINT);
		}
		else {
			echo json_encode($result);
		}
	}
	
	public static function getDataSet($i, &$report) {
		$dataset = array();
		foreach($report->options['DataSets'][$i] as $k=>$v) {
			$dataset[$k] = $v;
		}

		$rows = array();
		foreach($dataset['rows'] as $i=>$row) {
			$tmp = array();
			foreach($row['values'] as $key=>$value){
				$tmp[$value->key] = $value->getValue();
			}
			$rows[] = $tmp;
		}
		$dataset['rows'] = $rows;

		return $dataset;
	}
}


================================================
FILE: classes/report_formats/RawReportFormat.php
================================================
<?php
class RawReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {		
		header("Content-type: text/plain");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		echo $report;
	}
	
	//no need to instantiate a report object, just return the source
	public static function prepareReport($report) {
		$contents = Report::getReportFileContents($report);
		
		return $contents;
	}
}


================================================
FILE: classes/report_formats/SqlReportFormat.php
================================================
<?php
class SqlReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		header("Content-type: text/plain");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		echo $report->renderReportPage('sql/report');
	}
}


================================================
FILE: classes/report_formats/TableReportFormat.php
================================================
<?php
class TableReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		
		$report->options['inline_email'] = true;
		$report->use_cache = true;
		
		try {
			$html = $report->renderReportPage('html/table');
			echo $html;
		}
		catch(Exception $e) {
			
		}
	}
}


================================================
FILE: classes/report_formats/TextReportFormat.php
================================================
<?php
class TextReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		header("Content-type: text/plain");
		header("Pragma: no-cache");
		header("Expires: 0");
		
		$report->use_cache = true;
		
		//run the report
		$report->run();
		
        if(!$report->options['DataSets']) return;
        
        foreach($report->options['DataSets'] as $i=>$dataset) {
			if(isset($dataset['title'])) echo $dataset['title']."\n";
			TextReportFormat::displayDataSet($dataset);
			
			// If this isn't the last dataset, add some spacing
			if($i < count($report->options['DataSets'])-1) {
				echo "\n\n";
			}
		}
    }
    
    protected static function displayDataSet($dataset) {
		/**
		 * This code taken from Stack Overflow answer by ehudokai
		 * http://stackoverflow.com/a/4597190
		 */

		//first get your sizes
		$sizes = array();
		$first_row = $dataset['rows'][0];
		foreach($first_row['values'] as $key=>$value){
			$key = $value->key;
			$value = $value->getValue();
			
			//initialize to the size of the column name
			$sizes[$key] = strlen($key);
		}
		foreach($dataset['rows'] as $row) {
			foreach($row['values'] as $key=>$value){
				$key = $value->key;
				$value = $value->getValue();
				
				$length = strlen($value);
				if($length > $sizes[$key]) $sizes[$key] = $length; // get largest result size
			}
		}

		//top of output
		foreach($sizes as $length){
			echo "+".str_pad("",$length+2,"-");
		}
		echo "+\n";

		// column names
		foreach($first_row['values'] as $key=>$value){
			$key = $value->key;
			$value = $value->getValue();
			
			echo "| ";
			echo str_pad($key,$sizes[$key]+1);
		}
		echo "|\n";

		//line under column names
		foreach($sizes as $length){
			echo "+".str_pad("",$length+2,"-");
		}
		echo "+\n";

		//output data
		foreach($dataset['rows'] as $row) {
			foreach($row['values'] as $key=>$value){
				$key = $value->key;
				$value = $value->getValue();
				
				echo "| ";
				echo str_pad($value,$sizes[$key]+1);
			}
			echo "|\n";
		}

		//bottom of output
		foreach($sizes as $length){
			echo "+".str_pad("",$length+2,"-");
		}
		echo "+\n";
	}
}


================================================
FILE: classes/report_formats/XlsReportBase.php
================================================
<?php
abstract class XlsReportBase extends ReportFormatBase {
	private static function columnLetter($c){
		$c = intval($c);
		if ($c <= 0) return '';
		$letter = '';
		
		while($c != 0){
			$p = ($c - 1) % 26;
			$c = intval(($c - $p) / 26);
			$letter = chr(65 + $p) . $letter;
		}

		return $letter;
	}
	
	public static function getExcelRepresantation(&$report) {
		// Create new PHPExcel object
		$objPHPExcel = new PHPExcel();

		// Set document properties
		$objPHPExcel->getProperties()->setCreator("PHP-Reports")
									 ->setLastModifiedBy("PHP-Reports")
									 ->setTitle("")
									 ->setSubject("")
									 ->setDescription("");

		$i = 0;
		foreach($report->options['DataSets'] as $dataset) {
			$objPHPExcel->createSheet($i);
			self::addSheet($objPHPExcel,$dataset,$i);
			$i++;
		}
		
		// Set the active sheet to the first one
		$objPHPExcel->setActiveSheetIndex(0);
		
		return $objPHPExcel;
	}
	public static function addSheet($objPHPExcel,$dataset, $i) {
		$rows = array();
		$row = array();
		$cols = 0;
		$first_row = $dataset['rows'][0];
		foreach($first_row['values'] as $key=>$value){
			array_push($row, $value->key);
			$cols++;
		}
		array_push($rows, $row);
		$row = array();

		foreach($dataset['rows'] as $r) {
			foreach($r['values'] as $key=>$value){
				array_push($row, $value->getValue());
			}
			array_push($rows, $row);
			$row = array();
		}

		$objPHPExcel->setActiveSheetIndex($i)->fromArray($rows, NULL, 'A1');
		$objPHPExcel->getActiveSheet()->setAutoFilter('A1:'.self::columnLetter($cols).count($rows));
		for ($a = 1; $a <= $cols; $a++) {
			$objPHPExcel->getActiveSheet()->getColumnDimension(self::columnLetter($a))->setAutoSize(true);
		}

		if(isset($dataset['title'])) {
			// Some characters are not allowed in Excel sheet titles
			$title = preg_replace('#[\\/*[\]:?]#','',$dataset['title']);

			// Max title length is 31 characters
			$title = substr($title, 0, 31);

			$objPHPExcel->getActiveSheet()->setTitle($title);
		}

		return $objPHPExcel;
	}
}


================================================
FILE: classes/report_formats/XlsReportFormat.php
================================================
<?php
class XlsReportFormat extends XlsReportBase {
	public static function display(&$report, &$request) {
		// First let set up some headers
		$file_name = preg_replace(array('/[\s]+/','/[^0-9a-zA-Z\-_\.]/'),array('_',''),$report->options['Name']);

		//always use cache for Excel reports
		$report->use_cache = true;

		//run the report
		$report->run();

		if(!$report->options['DataSets']) return;

		$objPHPExcel = parent::getExcelRepresantation($report);

		$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel5');
		
		header('Content-Type: application/vnd.ms-excel');
		header('Content-Disposition: attachment;filename="'.$file_name.'.xls"');
		header('Pragma: no-cache');
		header('Expires: 0');
		
		$objWriter->save('php://output');
	}
}


================================================
FILE: classes/report_formats/XlsxReportFormat.php
================================================
<?php
class XlsxReportFormat extends XlsReportBase {
	public static function display(&$report, &$request) {
		// First let set up some headers
		$file_name = preg_replace(array('/[\s]+/','/[^0-9a-zA-Z\-_\.]/'),array('_',''),$report->options['Name']);

		//always use cache for Excel reports
		$report->use_cache = true;

		//run the report
		$report->run();

		if(!$report->options['DataSets']) return;

		$objPHPExcel = parent::getExcelRepresantation($report);

		$objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
		
		header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
		header('Content-Disposition: attachment;filename="'.$file_name.'.xlsx"');
		header('Pragma: no-cache');
		header('Expires: 0');
		
		$objWriter->save('php://output');
	}
}


================================================
FILE: classes/report_formats/XmlReportFormat.php
================================================
<?php
class XmlReportFormat extends ReportFormatBase {
	public static function display(&$report, &$request) {
		header("Content-type: application/xml");
		header("Pragma: no-cache");
		header("Expires: 0");

		$datasets = array();
		$dataset_format = false;
		
		if(isset($_GET['datasets'])) {
			$dataset_format = true;
			$datasets = $_GET['datasets'];
			// If all the datasets should be included
			if($datasets === 'all') {
				$datasets = array_keys($report->options['DataSets']);
			}
			// If just a single dataset was specified, make it an array
			else if(!is_array($datasets)) {
				$datasets = explode(',',$datasets);
			}
		}
		else {
			$i=0;
			if(isset($_GET['dataset'])) $i = $_GET['dataset'];
			elseif(isset($report->options['default_dataset'])) $i = $report->options['default_dataset'];
			$i = intval($i);
			
			$datasets = array($i);
		}

		echo $report->renderReportPage('xml/report',array(
			'datasets'=>$datasets,
			'dataset_format'=>$dataset_format
		));
	}
}


================================================
FILE: classes/report_types/AdoPivotReportType.php
================================================
<?php
class AdoPivotReportType extends ReportTypeBase {
	public static function init(&$report) {
		$environments = PhpReports::$config['environments'];
		
		if(!isset($environments[$report->options['Environment']][$report->options['Database']])) {
			throw new Exception("No ".$report->options['Database']." database defined for environment '".$report->options['Environment']."'");
		}

		//make sure the syntax highlighting is using the proper class
		SqlFormatter::$pre_attributes = "class='prettyprint linenums lang-sql'";

        //set a formatted query here for debugging.  It will be overwritten below after macros are substituted.
        $report->options['Query_Formatted'] = "<pre class='prettyprint linenums lang-yaml'>".$report->raw_query."</pre>";

        $object = spyc_load($report->raw_query);

        $report->raw_query = array();
		//if there are any included reports, add the report sql to the top
		if(isset($report->options['Includes'])) {
			$included_sql = '';
			foreach($report->options['Includes'] as &$included_report) {
				$included_sql .= trim($included_report->raw_query)."\n";
			}
            if (strlen($included_sql) > 0) {
			    $report->raw_query[] = $included_sql;
            }
		}

        $report->raw_query[] = $object;
	}
	
	public static function openConnection(&$report) {
		if(isset($report->conn)) return;
		
		$environments = PhpReports::$config['environments'];
		$config = $environments[$report->options['Environment']][$report->options['Database']];
		
		if(!($report->conn = ADONewConnection($config['uri']))) {
			throw new Exception('Could not connect to the database');
		}
	}
	
	public static function closeConnection(&$report) {
		if (!isset($report->conn)) return;
		if ($report->conn->IsConnected()) {
			$report->conn->Close();
		}
		unset($report->conn);
	}
	
	public static function getVariableOptions($params, &$report) {
        $report->conn->SetFetchMode(ADODB_FETCH_NUM);
        $query = 'SELECT DISTINCT '.$params['column'].' FROM '.$params['table'];
		
		if(isset($params['where'])) {
			$query .= ' WHERE '.$params['where'];
		}

        $macros = $report->macros;
        foreach($macros as $key=>$value) {
            if(is_array($value)) {
                foreach($value as $key2=>$value2) {
                    $value[$key2] = mysql_real_escape_string(trim($value2));
                }
                $macros[$key] = $value;
            }
            else {
                $macros[$key] = mysql_real_escape_string($value);
            }

            if($value === 'ALL') $macros[$key.'_all'] = true;
        }

        //add the config and environment settings as macros
        $macros['config'] = PhpReports::$config;
        $macros['environment'] = PhpReports::$config['environments'][$report->options['Environment']];

		$result = $report->conn->Execute(PhpReports::renderString($query, $macros));
		
		if (!$result) {
			throw new Exception("Unable to get variable options: ".$report->conn->ErrorMsg());
		}

		$options = array();
		
		if(isset($params['all']) && $params['all']) {
            $options[] = 'ALL';
        }

        while ($row = $result->FetchRow()) {
            if ($result->FieldCount() > 1) {
                $options[] = array('display'=>$row[0], 'value'=>$row[1]);
            } else {
                $options[] = $row[0];
            }
        }

        return $options;
	}
	
	public static function run(&$report) {
        $report->conn->SetFetchMode(ADODB_FETCH_ASSOC);
        $rows = array();

		$macros = $report->macros;
		foreach($macros as $key=>$value) {
			if(is_array($value)) {
				$first = true;
				foreach($value as $key2=>$value2) {
					$value[$key2] = mysql_real_escape_string(trim($value2));
					$first = false;
				}
				$macros[$key] = $value;
			}
			else {
				$macros[$key] = mysql_real_escape_string($value);
			}
			
			if($value === 'ALL') $macros[$key.'_all'] = true;
		}
		
		//add the config and environment settings as macros
		$macros['config'] = PhpReports::$config;
		$macros['environment'] = PhpReports::$config['environments'][$report->options['Environment']];

        $raw_sql = "";
        foreach ($report->raw_query as $qry) {
            if (is_array($qry)) {
                foreach ($qry as $key=>$value) {
                    // TODO handle arrays better
                    if (!is_bool($value) && !is_array($value)) {
                        $qry[$key] = PhpReports::renderString($value, $macros);
                    }
                }
                //TODO This sux - need a class or something :-)
                $raw_sql .= PivotTableSQL($report->conn, $qry['tables'], $qry['rows'], $qry['columns'], $qry['where'], $qry['orderBy'], $qry['limit'], $qry['agg_field'], $qry['agg_label'], $qry['agg_fun'], $qry['include_agg_field'], $qry['show_count']);
            } else {
                $raw_sql .= $qry;
            }
        }

        //expand macros in query
        $sql = PhpReports::render($raw_sql, $macros);

        $report->options['Query'] = $sql;

        $report->options['Query_Formatted'] = SqlFormatter::format($sql);

        //split into individual queries and run each one, saving the last result
        $queries = SqlFormatter::splitQuery($sql);

        foreach($queries as $query) {
            if (!is_array($query)) {
                //skip empty queries
                $query = trim($query);
                if(!$query) continue;

                $result = $report->conn->Execute($query);
                if(!$result) {
                    throw new Exception("Query failed: ".$report->conn->ErrorMsg());
                }

                //if this query had an assert=empty flag and returned results, throw error
                if(preg_match('/^--[\s+]assert[\s]*=[\s]*empty[\s]*\n/',$query)) {
                    if($result->GetAssoc()) {
                        throw new Exception("Assert failed.  Query did not return empty results.");
                    }
                }
            }
        }

        return $result->GetArray();
	}
}


================================================
FILE: classes/report_types/AdoReportType.php
================================================
<?php
class AdoReportType extends ReportTypeBase {
	public static function init(&$report) {
		$environments = PhpReports::$config['environments'];
		
		if(!isset($environments[$report->options['Environment']][$report->options['Database']])) {
			throw new Exception("No ".$report->options['Database']." database defined for environment '".$report->options['Environment']."'");
		}

		//make sure the syntax highlighting is using the proper class
		SqlFormatter::$pre_attributes = "class='prettyprint linenums lang-sql'";
		
		//default host macro to mysql's host if it isn't defined elsewhere
		//if(!isset($report->macros['host'])) $report->macros['host'] = $mysql['host'];
		
		//replace legacy shorthand macro format
		foreach($report->macros as $key=>$value) {
			$params = array();
			if(isset($report->options['Variables'][$key])) {
				$params = $report->options['Variables'][$key];
			}

			//macros shortcuts for arrays
			if(isset($params['multiple']) && $params['multiple']) {
				//allow {macro} instead of {% for item in macro %}{% if not item.first %},{% endif %}{{ item.value }}{% endfor %}
				//this is shorthand for comma separated list
				$report->raw_query = preg_replace('/([^\{])\{'.$key.'\}([^\}])/','$1{% for item in '.$key.' %}{% if not loop.first %},{% endif %}\'{{ item }}\'{% endfor %}$2',$report->raw_query);
			
				//allow {(macro)} instead of {% for item in macro %}{% if not item.first %},{% endif %}{{ item.value }}{% endfor %}
				//this is shorthand for quoted, comma separated list
				$report->raw_query = preg_replace('/([^\{])\{\('.$key.'\)\}([^\}])/','$1{% for item in '.$key.' %}{% if not loop.first %},{% endif %}(\'{{ item }}\'){% endfor %}$2',$report->raw_query);
			}
			//macros sortcuts for non-arrays
			else {
				//allow {macro} instead of {{macro}} for legacy support
				$report->raw_query = preg_replace('/([^\{])(\{'.$key.'+\})([^\}])/','$1{$2}$3',$report->raw_query);
			}
		}
		
		//if there are any included reports, add the report sql to the top
		if(isset($report->options['Includes'])) {
			$included_sql = '';
			foreach($report->options['Includes'] as &$included_report) {
				$included_sql .= trim($included_report->raw_query)."\n";
			}
			
			$report->raw_query = $included_sql . $report->raw_query;
		}
		
		//set a formatted query here for debugging.  It will be overwritten below after macros are substituted.
		$report->options['Query_Formatted'] = SqlFormatter::format($report->raw_query);
	}
	
	public static function openConnection(&$report) {
		if(isset($report->conn)) return;
		
		$environments = PhpReports::$config['environments'];
		$config = $environments[$report->options['Environment']][$report->options['Database']];
		
		if(!($report->conn = ADONewConnection($config['uri']))) {
			throw new Exception('Could not connect to the database');
		}
	}
	
	public static function closeConnection(&$report) {
		if (!isset($report->conn)) return;
		if ($report->conn->IsConnected()) {
			$report->conn->Close();
		}
		unset($report->conn);
	}
	
	public static function getVariableOptions($params, &$report) {
        $report->conn->SetFetchMode(ADODB_FETCH_NUM);
        $query = 'SELECT DISTINCT '.$params['column'].' FROM '.$params['table'];
		
		if(isset($params['where'])) {
			$query .= ' WHERE '.$params['where'];
		}
		
		$result = $report->conn->Execute($query);
		
		if (!$result) {
			throw new Exception("Unable to get variable options: ".$report->conn->ErrorMsg());
		}

		$options = array();
		
		if(isset($params['all']) && $params['all']) {
            $options[] = 'ALL';
        }

        while ($row = $result->FetchRow()) {
            if ($result->FieldCount() > 1) {
                $options[] = array('display'=>$row[0], 'value'=>$row[1]);
            } else {
                $options[] = $row[0];
            }
        }

        return $options;
	}
	
	public static function run(&$report) {
        $report->conn->SetFetchMode(ADODB_FETCH_ASSOC);
        $rows = array();
		
		$macros = $report->macros;
		foreach($macros as $key=>$value) {
			if(is_array($value)) {
				$first = true;
				foreach($value as $key2=>$value2) {
					$value[$key2] = mysql_real_escape_string(trim($value2));
					$first = false;
				}
				$macros[$key] = $value;
			}
			else {
				$macros[$key] = mysql_real_escape_string($value);
			}
			
			if($value === 'ALL') $macros[$key.'_all'] = true;
		}
		
		//add the config and environment settings as macros
		$macros['config'] = PhpReports::$config;
		$macros['environment'] = PhpReports::$config['environments'][$report->options['Environment']];
		
		//expand macros in query
		$sql = PhpReports::render($report->raw_query,$macros);
		
		$report->options['Query'] = $sql;

		$report->options['Query_Formatted'] = SqlFormatter::format($sql);
		
		//split into individual queries and run each one, saving the last result
		$queries = SqlFormatter::splitQuery($sql);
		
		foreach($queries as $query) {
			//skip empty queries
			$query = trim($query);
			if(!$query) continue;
			
			$result = $report->conn->Execute($query);
			if(!$result) {
				throw new Exception("Query failed: ".$report->conn->ErrorMsg());
			}
			
			//if this query had an assert=empty flag and returned results, throw error
			if(preg_match('/^--[\s+]assert[\s]*=[\s]*empty[\s]*\n/',$query)) {
				if($result->GetAssoc()) {
					throw new Exception("Assert failed.  Query did not return empty results.");
				}
			}
		}
		
		return $result->GetArray();
	}
}

================================================
FILE: classes/report_types/MongoReportType.php
================================================
<?php
class MongoReportType extends ReportTypeBase {
	public static function init(&$report) {
		$environments = PhpReports::$config['environments'];
		
		if(!isset($environments[$report->options['Environment']][$report->options['Database']])) {
			throw new Exception("No ".$report->options['Database']." database defined for environment '".$report->options['Environment']."'");
		}
		
		$mongo = $environments[$report->options['Environment']][$report->options['Database']];
		
		//default host macro to mysql's host if it isn't defined elsewhere
		if(!isset($report->macros['host']) && isset($mongo['host'])) $report->macros['host'] = $mongo['host'];
		
		//if there are any included reports, add it to the top of the raw query
		if(isset($report->options['Includes'])) {
			$included_code = '';
			foreach($report->options['Includes'] as &$included_report) {
				$included_code .= trim($included_report->raw_query)."\n";
			}
			
			$report->raw_query = $included_code . $report->raw_query;
		}
	}
	
	public static function openConnection(&$report) {
		
	}
	
	public static function closeConnection(&$report) {
		
	}
	
	public static function run(&$report) {		
		$eval = '';
		foreach($report->macros as $key=>$value) {
			if(is_array($value)) {
				$value = json_encode($value);
			}
			else {
				$value = '"'.addslashes($value).'"';
			}
			
			$eval .= 'var '.$key.' = '.$value.';'."\n";
		}
		$eval .= $report->raw_query;

		$environments = PhpReports::$config['environments'];
		$config = $environments[$report->options['Environment']][$report->options['Database']];
		
		$mongo_database = isset($report->options['Mongodatabase'])? $report->options['Mongodatabase'] : '';

		// Get connection string
        $connectionString = '';
        if(isset($config['dsn'])) {
            // Replace any existing mongo database name with the one for the report
            $connectionString = preg_replace(
                '/(mongodb[a-zA-Z+]*:\/\/)?([^\/?]+)(\/[^\.,:?]*)?($|\?)/',
                '$1$2/'.$mongo_database.'$4',
                $config['dsn'],
                1
            );
        }
        elseif(isset($config['host'])) {
            $connectionString = $config['host'].':'.(isset($config['port'])?$config['port']:27017).'/'.$mongo_database;
        }

        //command without eval string
		$command = 'mongo '.escapeshellarg($connectionString).' --quiet --eval ';

        // Remove password from command for debug info
        $commandSanitized = preg_replace('/\/\/([^@:]+)(:[^:@]+)@/','//$1:*******@',$command);

		//easy to read formatted query
		$report->options['Query_Formatted'] = '<div>
			<pre style="background-color: black; color: white; padding: 10px 5px;">$ '.$commandSanitized.'"..."</pre>'.
			'Eval String:'.
			'<pre class="prettyprint linenums lang-js">'.htmlentities($eval).'</pre>
		</div>';

		//escape the eval string and add it to the command
		$command .= escapeshellarg($eval);
		$report->options['Query'] = '$ '.$commandSanitized.escapeshellarg($eval);

        $process = proc_open($command, [
            0 => ["pipe","r"],
            1 => ["pipe","w"],
            2 => ["pipe","w"]
        ], $pipes);

        $result = trim(stream_get_contents($pipes[1]));
        fclose($pipes[1]);

        $err = stream_get_contents($pipes[2]);
        fclose($pipes[2]);

        $exitCode = proc_close($process);

        // Annoying bug in MongoDB causes some debug messages to be output to stdout and also ignore "--quiet"
        // Filter out anything that matches the debug pattern and move to $err
        $debugInfoRegex = '/^[0-9]{4}-[0-9]{2}-[0-9]{2}T.*/m';
        if(preg_match_all($debugInfoRegex, $result, $matches)) {
            $result = trim(preg_replace($debugInfoRegex, '', $result));
            $err .= "\n".implode("\n",$matches[0]);
        }

        if($exitCode > 0) {
            throw new Exception('Exit code: '.$exitCode."\n\n".$result."\n".$err);
        }
		
		$json = json_decode($result, true);
		if($json === NULL) throw new Exception('Exit code: '.$exitCode."\n\n".$result."\n".$err);
		
		return $json;
	}
}


================================================
FILE: classes/report_types/MysqlReportType.php
================================================
<?php
class MysqlReportType extends PdoReportType {
	public static $default_driver = 'mysql';
}


================================================
FILE: classes/report_types/PdoReportType.php
================================================
<?php
class PdoReportType extends ReportTypeBase {
	public static $default_driver = null;

	public static function init(&$report) {
		$environments = PhpReports::$config['environments'];

		if(!isset($environments[$report->options['Environment']][$report->options['Database']])) {
			throw new Exception("No ".$report->options['Database']." info defined for environment '".$report->options['Environment']."'");
		}

		//make sure the syntax highlighting is using the proper class
		SqlFormatter::$pre_attributes = "class='prettyprint linenums lang-sql'";

		//replace legacy shorthand macro format
		foreach($report->macros as $key=>$value) {
			if(isset($report->options['Variables'][$key])) {
				$params = $report->options['Variables'][$key];
			}
			else {
				$params = array();
			}

			//macros shortcuts for arrays
			if(isset($params['multiple']) && $params['multiple']) {
				//allow {macro} instead of {% for item in macro %}{% if not item.first %},{% endif %}{{ item.value }}{% endfor %}
				//this is shorthand for comma separated list
				$report->raw_query = preg_replace('/([^\{])\{'.$key.'\}([^\}])/','$1{% for item in '.$key.' %}{% if not loop.first %},{% endif %}\'{{ item }}\'{% endfor %}$2',$report->raw_query);

				//allow {(macro)} instead of {% for item in macro %}{% if not item.first %},{% endif %}{{ item.value }}{% endfor %}
				//this is shorthand for quoted, comma separated list
				$report->raw_query = preg_replace('/([^\{])\{\('.$key.'\)\}([^\}])/','$1{% for item in '.$key.' %}{% if not loop.first %},{% endif %}(\'{{ item }}\'){% endfor %}$2',$report->raw_query);
			}
			//macros sortcuts for non-arrays
			else {
				//allow {macro} instead of {{macro}} for legacy support
				$report->raw_query = preg_replace('/([^\{])(\{'.$key.'+\})([^\}])/','$1{$2}$3',$report->raw_query);
			}
		}

		//if there are any included reports, add the report sql to the top
		if(isset($report->options['Includes'])) {
			$included_sql = '';
			foreach($report->options['Includes'] as &$included_report) {
				$included_sql .= trim($included_report->raw_query)."\n";
			}

			$report->raw_query = $included_sql . $report->raw_query;
		}

		//set a formatted query here for debugging.  It will be overwritten below after macros are substituted.
		$report->options['Query_Formatted'] = SqlFormatter::format($report->raw_query);
	}

	public static function openConnection(&$report) {
		if(isset($report->conn)) return;


		$environments = PhpReports::$config['environments'];
		$config = $environments[$report->options['Environment']][$report->options['Database']];

		if(isset($config['dsn'])) {
			$dsn = $config['dsn'];
		}
		else {
			$host = $config['host'];
			if(isset($report->options['access']) && $report->options['access']==='rw') {
				if(isset($config['host_rw'])) $host = $config['host_rw'];
			}

			$driver = isset($config['driver'])? $config['driver'] : static::$default_driver;

			if(!$driver) {
				throw new Exception("Must specify database `driver` (e.g. 'mysql')");
			}

			$dsn = $driver.':host='.$host;

			if(isset($config['database'])) {
				$dsn .= ';dbname='.$config['database'];
			}
		}

		//the default is to use a user with read only privileges
		$username = $config['user'];
		$password = $config['pass'];

		//if the report requires read/write privileges
		if(isset($report->options['access']) && $report->options['access']==='rw') {
			if(isset($config['user_rw'])) $username = $config['user_rw'];
			if(isset($config['pass_rw'])) $password = $config['pass_rw'];
		}

		$report->conn = new PDO($dsn,$username,$password);

		$report->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	}

	public static function closeConnection(&$report) {
		if(!isset($report->conn)) return;
		$report->conn = null;
		unset($report->conn);
	}

	public static function getVariableOptions($params, &$report) {
		$displayColumn = $params['column'];
		if(isset($params['display'])) $displayColumn = $params['display'];

		$query = 'SELECT DISTINCT `'.$params['column'].'` as val, `'.$displayColumn.'` as disp FROM '.$params['table'];

		if(isset($params['where'])) {
			$query .= ' WHERE '.$params['where'];
		}

		if(isset($params['order']) && in_array($params['order'], array('ASC', 'DESC')) ) {
			$query .= ' ORDER BY '.$params['column'].' '.$params['order'];
		}

		$result = $report->conn->query($query);

		$options = array();

		if(isset($params['all'])) $options[] = 'ALL';

		while($row = $result->fetch(PDO::FETCH_ASSOC)) {
			$options[] = array(
				'value'=>$row['val'],
				'display'=>$row['disp']
			);
		}

		return $options;
	}

	public static function run(&$report) {
		$macros = $report->macros;
		foreach($macros as $key=>$value) {
			if(is_array($value)) {
				$first = true;
				foreach($value as $key2=>$value2) {
					$value[$key2] = $report->conn->quote(trim($value2));
					$value[$key2] = preg_replace("/(^'|'$)/",'',$value[$key2]);
					$first = false;
				}
				$macros[$key] = $value;
			}
			else {
				$macros[$key] = $report->conn->quote($value);
				$macros[$key] = preg_replace("/(^'|'$)/",'',$macros[$key]);
			}

			if($value === 'ALL') $macros[$key.'_all'] = true;
		}

		//add the config and environment settings as macros
		$macros['config'] = PhpReports::$config;
		$macros['environment'] = PhpReports::$config['environments'][$report->options['Environment']];

		//expand macros in query
		$sql = PhpReports::render($report->raw_query,$macros);

		$report->options['Query'] = $sql;

		$report->options['Query_Formatted'] = SqlFormatter::format($sql);

		//split into individual queries and run each one, saving the last result
		$queries = SqlFormatter::splitQuery($sql);

		$datasets = array();

		$explicit_datasets = preg_match('/--\s+@dataset(\s*=\s*|\s+)true/',$sql);

		foreach($queries as $i=>$query) {
			$is_last = $i === count($queries)-1;

			//skip empty queries
			$query = trim($query);
			if(!$query) continue;

			$result = $report->conn->query($query);

			//if this query had an assert=empty flag and returned results, throw error
			if(preg_match('/^--[\s+]assert[\s]*=[\s]*empty[\s]*\n/',$query)) {
				if($result->fetch(PDO::FETCH_ASSOC))  throw new Exception("Assert failed.  Query did not return empty results.");
			}

			// If this query should be included as a dataset
			if((!$explicit_datasets && $is_last) || preg_match('/--\s+@dataset(\s*=\s*|\s+)true/',$query)) {
				$dataset = array('rows'=>array());

				while($row = $result->fetch(PDO::FETCH_ASSOC)) {
					$dataset['rows'][] = $row;
				}

				// Get dataset title if it has one
				if(preg_match('/--\s+@title(\s*=\s*|\s+)(.*)/',$query,$matches)) {
					$dataset['title'] = $matches[2];
				}

				$datasets[] = $dataset;
			}
		}

		return $datasets;
	}
}


================================================
FILE: classes/report_types/PhpReportType.php
================================================
<?php
class PhpReportType extends ReportTypeBase {
	public static function init(&$report) {
		$report->raw_query = "<?php\n//REPORT: ".$report->report."\n".trim($report->raw_query);
		
		//if there are any included reports, add it to the top of the raw query
		if(isset($report->options['Includes'])) {
			$included_code = '';
			foreach($report->options['Includes'] as &$included_report) {
				$included_code .= "\n<?php /*BEGIN INCLUDED REPORT*/ ?>".trim($included_report->raw_query)."<?php /*END INCLUDED REPORT*/ ?>";
			}
			
			if($included_code) $included_code.= "\n";
			
			$report->raw_query = $included_code . $report->raw_query;
			
			//make sure the raw query has a closing PHP tag at the end
			//this makes sure it will play nice as an included report
			if(!preg_match('/\?>\s*$/',$report->raw_query)) $report->raw_query .= "\n?>";
		}
	}
	
	public static function openConnection(&$report) {
		
	}
	
	public static function closeConnection(&$report) {
		
	}
	
	public static function run(&$report) {		
		$eval = "<?php /*BEGIN REPORT MACROS*/ ?><?php ";
		foreach($report->macros as $key=>$value) {
			$value = var_export($value,true);
			
			$eval .= "\n".'$'.$key.' = '.$value.';';
		}
		$eval .= "\n?><?php /*END REPORT MACROS*/ ?>".$report->raw_query;
		
		$config = PhpReports::$config;
		
		//store in both $database and $environment for backwards compatibility
		$database = PhpReports::$config['environments'][$report->options['Environment']];
		$environment = $database;
		
		$report->options['Query'] = $report->raw_query;
		
		$parts = preg_split('/<\?php \/\*(BEGIN|END) (INCLUDED REPORT|REPORT MACROS)\*\/ \?>/',$eval);
		$report->options['Query_Formatted'] = '';
		$code = htmlentities(trim(array_pop($parts)));
		$linenum = 1;
		foreach($parts as $part) {
			if(!trim($part)) continue;

			//get name of report
			$name = preg_match("|//REPORT: ([^\n]+)\n|",$part,$matches);

			if(!$matches) {
				$name = "Variables";
			}
			else {
				$name = $matches[1];
			}

			$report->options['Query_Formatted'] .= '<div class="included_report" data-name="'.htmlentities($name).'">';
			$report->options['Query_Formatted'] .= "<pre class='prettyprint lang-php linenums:".$linenum."'>".htmlentities(trim($part))."</pre>";
			$report->options['Query_Formatted'] .= "</div>";
			$linenum += count(explode("\n",trim($part)));
		}
		
		$report->options['Query_Formatted'] .= '<pre class="prettyprint lang-php linenums:'.$linenum.'">'.$code.'</pre>';

		ob_start();
		ini_set('display_errors','Off');
		eval('?>'.$eval);
		$result = ob_get_contents();
		ob_end_clean();
		ini_set('display_errors','On');

		$result = trim($result);
		
		$json = json_decode($result, true);
		if($json === NULL) throw new Exception($result);
		
		return $json;
	}
}


================================================
FILE: composer.json
================================================
{
    "name": "jdorn/php-reports",
    "description": "A PHP framework for displaying reports from any data source, including SQL and MongoDB",
    "homepage": "http://jdorn.github.com/php-reports/",
    "keywords": ["reports", "mongodb", "sql", "reporting", "csv", "database"],
    "license": "LGPL-3.0+",
    "authors": [
        {
            "name": "Jeremy Dorn",
            "email": "jeremy@jeremydorn.com"
        }
    ],
    "require": {
        "php": ">=5.3",
        "ext-pdo": "*",
        "twig/twig": "1.*",
        "swiftmailer/swiftmailer": "5.*",
        "mikecao/flight": "dev-master",
        "jdorn/sql-formatter": "dev-master",
        "jdorn/file-system-cache": "dev-master",
        "phpoffice/phpexcel": "dev-develop",
        "adodb/adodb-php": "dev-master",
        "lespoilus/spyc": "dev-master",
        "google/apiclient": "1.0.*@beta",
        "alcaeus/mongo-php-adapter": "^1.1@dev"
    },
    "autoload": {
        "files": [
            "lib/adodb/pivottable.inc.php"
        ],
        "classmap": [
            "vendor/jdorn/file-system-cache/"
        ]
    },
    "minimum-stability": "dev"
}


================================================
FILE: config/config.php.sample
================================================
<?php
return array(
	//the root directory of all your reports
	//reports can be organized in subdirectories
	'reportDir' => 'sample_reports',

	//the root directory of all dashboards
	'dashboardDir' => 'sample_dashboards',

	//the directory where things will be cached
	//this is relative to the project root by default, but can be set to an absolute path too
	//the cache has some relatively long lived data so don't use /tmp if you can avoid it
	//(for example historical report timing data is stored here)
	'cacheDir' => 'cache',

	//this maps file extensions to report types
	//to override this for a specific report, simply add a TYPE header
	//any file extension not in this array will be ignored when pulling the report list
	'default_file_extension_mapping' => array(
		'sql'=>'Pdo',
		'php'=>'Php',
		'js'=>'Mongo',
		'ado'=>'Ado',
		'pivot'=>'AdoPivot',
	),

	//this enables listing different types of download formats on the report page
	//to change that one can add or remove any format from the list below
	//in order to create a divider a list entry have to be added with any key name and
	//a value of 'divider'
	'report_formats' => array(
		'csv'=>'CSV',
		'xlsx'=>'Download Excel 2007',
		'xls'=>'Download Excel 97-2003',
		'text'=>'Text',
		'table'=>'Simple table',
		'raw data'=>'divider',
		'json'=>'JSON',
		'xml'=>'XML',
		'sql'=>'SQL INSERT command',
		'technical'=>'divider',
		'debug'=>'Debug information',
		'raw'=>'Raw report dump',
	),

	//this enebales one to change the default bootstrap theme
	'bootstrap_theme' => 'default',

	//this list all the available themes for a user to switch and use the one he or she likes
	//once removed the theme will not appear in the dropdown
	//if all to be removed - no dropdown will be visible for the user and the default (above) will be used
	'bootstrap_themelist' => array(
	    'default',
	    'amelia', 'cerulean', 'cosmo', 'cyborg', 'flatly', 'journal', 'readable', 'simplex', 'slate', 'spacelab', 'united'
	),

	//email settings
	'mail_settings' => array(
		//set 'enabled' to true to enable the 'email this report' functionality
		'enabled'=>false,

		'from'=>'reports@yourdomain.com',

		//php's mail function
		'method'=>'mail'

		//sendmail
		/*
		'method'=>'sendmail',
		'command'=>'/usr/sbin/sendmail -bs' //optional
		*/

		//smtp
		/*
		'method'=>'smtp',
		'server'=>'smtp.yourdomain.com',
		'port'=>'25', 						//optional (default 25)
		'username'=>'youremailusername', 	//optional
		'password'=>'yoursmtppassword', 	//optional
		'encryption'=>'ssl' 				//optional (either 'ssl' or 'tls')
		*/
	),

	// Google Analytics API Integration
	/*
	'ga_api'=>array(
		'applicationName'=>'PHP Reports',
		'clientId'=>'abcdef123456',
		'clientSecret'=>'1234abcd',
		'redirectUri'=>'http://example.com/'
	),
	*/

	//this defines the database environments
	//the keys are the environment names (e.g. "dev", "production")
	//the values are arrays that contain connection info
	'environments' => array(
		'main'=>array(
			// Supports AdoDB connections
			'ado'=>array(
				'uri'=>'mysql://username:password@localhost/database'
			),

			// Supports and PDO database
			'pdo'=>array(
				'dsn'=>'mysql:host=localhost;dbname=test',
				'user'=>'readonly',
				'pass'=>'password',
			),

			// Supports MongoDB
			'mongo'=>array(
				'host'=>'localhost',
				'port'=>'27017'
			),
		),
	),
	// This is called twice, once for each Twig_Environment that is used
	'twig_init_function' => function (Twig_Environment $twig) {

	}
);
?>


================================================
FILE: index.php
================================================
<?php
// for build-in php server serve the requested resource as-is.
if (php_sapi_name() == 'cli-server' && preg_match('/\.(?:png|jpg|jpeg|gif|css|js)$/', $_SERVER["REQUEST_URI"])) {
    return false;
}

session_start();

//set php ini so the page doesn't time out for long requests
ini_set('max_execution_time', 300);

//sets up autoloading of composer dependencies
include 'vendor/autoload.php';

//sets up autoload (looks in classes/local/, classes/, and lib/ in that order)
require 'lib/PhpReports/PhpReports.php';

header("Access-Control-Allow-Origin: *");

// Google Analytics API
if(isset(PhpReports::$config['ga_api'])) {
  $ga_client = new Google_Client();
  $ga_client->setApplicationName(PhpReports::$config['ga_api']['applicationName']);
  $ga_client->setClientId(PhpReports::$config['ga_api']['clientId']);
  $ga_client->setAccessType('offline');
  $ga_client->setClientSecret(PhpReports::$config['ga_api']['clientSecret']);
  $ga_client->setRedirectUri(PhpReports::$config['ga_api']['redirectUri']);
  $ga_service = new Google_Service_Analytics($ga_client);
  $ga_client->addScope(Google_Service_Analytics::ANALYTICS);
  if(isset($_GET['code'])) {
    $ga_client->authenticate($_GET['code']);
    $_SESSION['ga_token'] = $ga_client->getAccessToken();
    
    if(isset($_SESSION['ga_authenticate_redirect'])) {
      $url = $_SESSION['ga_authenticate_redirect'];
      unset($_SESSION['ga_authenticate_redirect']);
      header("Location: $url");
      exit;
    }
  }
  if(isset($_SESSION['ga_token'])) {    
    $ga_client->setAccessToken($_SESSION['ga_token']);
  }
  elseif(isset(PhpReports::$config['ga_api']['accessToken'])) {    
    $ga_client->setAccessToken(PhpReports::$config['ga_api']['accessToken']);
    $_SESSION['ga_token'] = $ga_client->getAccessToken();
  }
  
  Flight::route('/ga_authenticate',function() use($ga_client) {
    $authUrl = $ga_client->createAuthUrl();
    if(isset($_GET['redirect'])) {
      $_SESSION['ga_authenticate_redirect'] = $_GET['redirect'];
    }
    header("Location: $authUrl");
    exit;
  });
}

Flight::route('/',function() {
	PhpReports::listReports();
});

Flight::route('/dashboards',function() {
	PhpReports::listDashboards();
});

Flight::route('/dashboard/@name',function($name) {
	PhpReports::displayDashboard($name);
});

//JSON list of reports (used for typeahead search)
Flight::route('/report-list-json',function() {
	header("Content-Type: application/json");
	header("Cache-Control: max-age=3600");

	echo PhpReports::getReportListJSON();
});

//if no report format is specified, default to html
Flight::route('/report',function() {
	PhpReports::displayReport($_REQUEST['report'],'html');
});

//reports in a specific format (e.g. 'html','csv','json','xml', etc.)
Flight::route('/report/@format',function($format) {
	PhpReports::displayReport($_REQUEST['report'],$format);
});

Flight::route('/edit',function() {
	PhpReports::editReport($_REQUEST['report']);
});

Flight::route('/set-environment',function() {
    header("Content-Type: application/json");
	$_SESSION['environment'] = $_REQUEST['environment'];

    echo '{ "status": "OK" }';
});

//email report
Flight::route('/email',function() {
	PhpReports::emailReport();	
});

Flight::set('flight.handle_errors', false);
Flight::set('flight.log_errors', true);

Flight::start();


================================================
FILE: lib/PhpReports/FilterBase.php
================================================
<?php
abstract class FilterBase {	
	/**
	 * Filter a datapoint in the report
	 * @param String $value - The current value
	 * @param array $options - An array of filter options
	 * @param Report $report - The report object
	 * @param array $row - The report row the value is in
	 * @return String The filtered value or false if the column should not be shown
	 */
	abstract public static function filter($value, $options=array(), &$report, &$row);
}


================================================
FILE: lib/PhpReports/HeaderBase.php
================================================
<?php
class HeaderBase {
	static $validation = array();
	
	public static function parse($key, $value, &$report) {				
		$params = null;
		
		if(is_array($value)) {
			$params = $value;
		}
		//try to json_decode value
		//this is a wrapper json_decode function that supports non-strict JSON
		//for example, {key:"value",'key2':'value2',}
		elseif($value[0] === '{') {
			$params = PhpReports::json_decode($value, true);
		}
		
		//if it couldn't be parsed as json, try parsing it as a shortcut form
		if(!$params) $params = static::parseShortcut($value);
		
		if(!$params) throw new Exception("Could not parse header '$key'");
		
		//run defined validation rules and fill in default params
		try {
			$params = static::validate($params);
		}
		catch(Exception $e) {
			throw new Exception($key." Header: ".$e->getMessage());
		}
		
		static::init($params, $report);
	}
	
	public static function init($params, &$report) {
		
	}
	
	public static function parseShortcut($value) {
		return array();
	}
	
	public static function beforeRender(&$report) {
		
	}
	
	public static function afterParse(&$report) {
		
	}
	
	public static function beforeRun(&$report) {
		
	}
	
	protected static function validate($params) {
		if(!static::$validation) return $params;
		
		$errors = array();
		
		foreach(static::$validation as $key=>$rules) {
			//fill in default params
			if(isset($rules['default']) && !isset($params[$key])) {
				$params[$key] = $rules['default'];
				continue;
			}
			
			//if the param isn't required and it's defined, we can skip validation
			if((!isset($rules['required']) || !$rules['required']) && !isset($params[$key])) continue;
			
			//if the param must be a specific datatype
			if(isset($rules['type'])) {
				if($rules['type'] === 'number' && !is_numeric($params[$key])) $errors[] = "$key must be a number (".gettype($params[$key])." given)";
				elseif($rules['type'] === 'array' && !is_array($params[$key])) $errors[] = "$key must be an array (".gettype($params[$key])." given)";
				elseif($rules['type'] === 'boolean' && !is_bool($params[$key])) $errors[] = "$key must be true or false (".gettype($params[$key])." given)";
				elseif($rules['type'] === 'string' && !is_string($params[$key])) $errors[] = "$key must be a string (".gettype($params[$key])." given)";
				elseif($rules['type'] === 'enum' && !in_array($params[$key],$rules['values'])) $errors[] = "$key must be one of: [".implode(', ',$rules['values'])."]";
				elseif($rules['type'] === 'object' && !is_array($params[$key])) $errors[] = "$key must be an object (".gettype($params[$key])." given)";
			}
			
			//other validation rules
			if(isset($rules['min']) && $params[$key] < $rules['min']) $errors[] = "$key must be at least $rules[min]";
			if(isset($rules['max']) && $params[$key] > $rules['max']) $errors[] = "$key must be at most $rules[min]";
			
			if(isset($rules['pattern']) && !preg_match($rules['pattern'],$params[$key])) $errors[] = "$key does not match required pattern";
		}
		
		//every possible param must be defined in the validation rules
		foreach($params as $k=>$v) {
			if(!isset(static::$validation[$k])) $errors[] = "Unknown parameter '$k'";
		}
		
		if($errors) {
			throw new Exception(implode(". ",$errors));
		}
		else return $params;
	}
}


================================================
FILE: lib/PhpReports/PhpReports.php
================================================
<?php
class PhpReports {
	public static $config;
	public static $request;
	public static $twig;
	public static $twig_string;

	public static $vars;

	private static $loader_cache;

	public static function init($config = 'config/config.php') {
		//set up our autoloader
		spl_autoload_register(array('PhpReports','loader'),true,true);

		if(!file_exists($config)) {
			throw new Exception("Cannot find config file");
		}

		// The config.php.sample is used to populate default values should the config.php be incomplete.
		// As a result, we require it be there.
		if(!file_exists('config/config.php.sample')) {
			throw new Exception("Cannot find sample config. Please leave config/config.php.sample in place for default values.");
		}

		$default_config = include('config/config.php.sample');
		$config = include($config);

		self::$config = array_merge($default_config, $config);

		self::$request = Flight::request();

		$path = self::$request->base;

		if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) || isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
			$protocol = 'https://';
		} else {
			$protocol = 'http://';
		}
		self::$request->base = $protocol.rtrim($_SERVER['HTTP_HOST'].self::$request->base,'/');

		//the load order for templates is: "templates/local", "templates/default", "templates"
		//this means loading the template "html/report.twig" will load the local first and then the default
		//if you want to extend a default template from within a local template, you can do {% extends "default/html/report.twig" %} and it will fall back to the last loader
		$template_dirs = array('templates/default','templates');
		if(file_exists('templates/local')) array_unshift($template_dirs, 'templates/local');

		$loader = new Twig_Loader_Chain(array(
			new Twig_Loader_Filesystem($template_dirs),
			new Twig_Loader_String()
		));
		self::$twig = new Twig_Environment($loader);
		self::$twig->addFunction(new Twig_SimpleFunction('dbdate', 'PhpReports::dbdate'));
		self::$twig->addFunction(new Twig_SimpleFunction('sqlin', 'PhpReports::generateSqlIN'));

		if(isset($_COOKIE['reports-theme']) && $_COOKIE['reports-theme']) {
			$theme = $_COOKIE['reports-theme'];
		}
		else {
			$theme = self::$config['bootstrap_theme'];
		}
		self::$twig->addGlobal('theme', $theme);
		self::$twig->addGlobal('path', $path);

		self::$twig->addFilter('var_dump', new Twig_Filter_Function('var_dump'));

		self::$twig_string = new Twig_Environment(new Twig_Loader_String(), array('autoescape'=>false));
		self::$twig_string->addFunction(new Twig_SimpleFunction('sqlin', 'PhpReports::generateSqlIN'));

		FileSystemCache::$cacheDir = self::$config['cacheDir'];

		if(!isset($_SESSION['environment']) || !isset(self::$config['environments'][$_SESSION['environment']])) {
		    $tmp = array_keys(self::$config['environments']);
			$_SESSION['environment'] = array_shift($tmp);
		}

		// Extend twig.
		if (isset($config['twig_init_function']) && is_callable($config['twig_init_function'])) {
			$config['twig_init_function'](self::$twig);
			$config['twig_init_function'](self::$twig_string);
		}
	}

	public static function setVar($key,$value) {
		if(!self::$vars) self::$vars = array();

		self::$vars[$key] = $value;
	}
	public static function getVar($key, $default=null) {
		if(isset(self::$vars[$key])) return self::$vars[$key];
		else return $default;
	}

	public static function dbdate($time, $database=null, $format=null) {
		$report = self::getVar('Report',null);
		if(!$report) return strtotime('Y-m-d H:i:s',strtotime($time));

		//if a variable name was passed in
		$var = null;
		if(isset($report->options['Variables'][$time])) {
			$var = $report->options['Variables'][$time];
			$time = $report->macros[$time];
		}

		$time = strtotime($time);

		$environment = $report->getEnvironment();

		//determine time offset
		$offset = 0;

		if($database) {
			if(isset($environment[$database]['time_offset'])) $offset = $environment[$database]['time_offset'];
		}
		else {
			$database = $report->getDatabase();
			if(isset($database['time_offset'])) $offset = $database['time_offset'];
		}

		//if the time needs to be adjusted
		if($offset) {
			$time = strtotime((($offset > 0)? '+' : '-').abs($offset).' hours',$time);
		}

		//determine output format
		if($format) {
			$time = date($format,$time);
		}
		elseif($var && isset($var['format'])) {
			$time = date($var['format'],$time);
		}
		//default to Y-m-d H:i:s
		else {
			$time = date('Y-m-d H:i:s',$time);
		}

		return $time;
	}

	public static function generateSqlIN($column, $values, $or_null = false) {
		$sql = "$column IN (";
		foreach ($values as $value) {
			$sql .= is_numeric($value) ? $value : "'$value'";
			if ($value !== end($values)) {
				$sql .= ', ';
			}
		}
		$sql .= ")";
		if ($or_null) {
			$sql.= " OR $column IS NULL";
		}
		return $sql;
	}

	public static function render($template, $macros) {
		$default = array(
			'base'=>self::$request->base,
			'report_list_url'=>self::$request->base.'/',
			'request'=>self::$request,
			'querystring'=>$_SERVER['QUERY_STRING'],
			'config'=>self::$config,
			'environment'=>$_SESSION['environment'],
			'recent_reports'=>self::getRecentReports(),
			'session'=>$_SESSION
		);
		$macros = array_merge($default,$macros);

		//if a template path like 'html/report' is given, add the twig file extension
		if(preg_match('/^[a-zA-Z_\-0-9\/]+$/',$template)) $template .= '.twig';
		return self::$twig->render($template,$macros);
	}

	public static function renderString($template, $macros) {
			return self::$twig_string->render($template,$macros);
	}

	public static function displayReport($report,$type) {
		$classname = ucfirst(strtolower($type)).'ReportFormat';

		$error_header = 'An error occurred while running your report';
		$content = '';

		try {
			if(!class_exists($classname)) {
				$error_header = 'Unknown report format';
				throw new Exception("Unknown report format '$type'");
			}

			try {
				$report = $classname::prepareReport($report);
			}
			catch(Exception $e) {
				$error_header = 'An error occurred while preparing your report';
				throw $e;
			}

			$classname::display($report,self::$request);

			if(isset($report->options['Query_Formatted'])) {
				$content = $report->options['Query_Formatted'];
			}
		}
		catch(Exception $e) {
			echo self::render('html/page',array(
				'title'=>'Report',
				'header'=>'<h2>'.$error_header.'</h2>',
				'error'=>$e->getMessage(),
				'content'=>$content,
				'breadcrumb'=>array('Report List'=>'')
			));
		}
	}

	public static function editReport($report) {
		$template_vars = array();

		try {
			$report = ReportFormatBase::prepareReport($report);

			$template_vars = array(
				'report'=>$report->report,
				'options'=>$report->options,
				'contents'=>$report->getRaw(),
				'extension'=>array_pop(explode('.',$report->report))
			);
		}
		//if there is an error parsing the report
		catch(Exception $e) {
			$template_vars = array(
				'report'=>$report,
				'contents'=>Report::getReportFileContents($report),
				'options'=>array(),
				'extension'=>array_pop(explode('.',$report)),
				'error'=>$e
			);
		}

		if(isset($_POST['preview'])) {
			echo "<pre>".SimpleDiff::htmlDiffSummary($template_vars['contents'],$_POST['contents'])."</pre>";
		}
		elseif(isset($_POST['save'])) {
			Report::setReportFileContents($template_vars['report'],$_POST['contents']);
		}
		else {
			echo self::render('html/report_editor',$template_vars);
		}
	}

	public static function listReports() {
		$errors = array();

		$reports = self::getReports(self::$config['reportDir'].'/',$errors);

		$template_vars['reports'] = $reports;
		$template_vars['report_errors'] = $errors;

		$start = microtime(true);
		echo self::render('html/report_list',$template_vars);
	}

	public static function listDashboards() {
		$dashboards = self::getDashboards();

		uasort($dashboards,function($a,$b) {
			return strcmp($a['title'],$b['title']);
		});

		echo self::render('html/dashboard_list',array(
			'dashboards'=>$dashboards
		));
	}

	public static function displayDashboard($dashboard) {
		$content = self::getDashboard($dashboard);

		echo self::render('html/dashboard',array(
			'dashboard'=>$content
		));
	}

	public static function getDashboards() {
		$dashboards = glob(PhpReports::$config['dashboardDir'].'/*.json');

		$ret = array();
		foreach($dashboards as $key=>$value) {
			$name = basename($value,'.json');
			$ret[$name] = self::getDashboard($name);
		}

		return $ret;
	}

	public static function getDashboard($dashboard) {
		$file = PhpReports::$config['dashboardDir'].'/'.$dashboard.'.json';
		if(!file_exists($file)) {
			throw new Exception("Unknown dashboard - ".$dashboard);
		}

		return json_decode(file_get_contents($file),true);
	}

	public static function getRecentReports() {
		$recently_run = FileSystemCache::retrieve(FileSystemCache::generateCacheKey('recently_run'));
		$recent = array();
		if($recently_run !== false) {
			$i = 0;
			foreach($recently_run as $report) {
				if($i > 10) break;

				$headers = self::getReportHeaders($report);

				if(!$headers) continue;
				if(isset($recent[$headers['url']])) continue;

				$recent[$headers['url']] = $headers;
				$i++;
			}
		}

		return array_values($recent);
	}
	public static function getReportListJSON($reports=null) {
		if($reports === null) {
			$errors = array();
			$reports = self::getReports(self::$config['reportDir'].'/',$errors);
		}

		//weight by popular reports
		$recently_run = FileSystemCache::retrieve(FileSystemCache::generateCacheKey('recently_run'));
		$popular = array();
		if($recently_run !== false) {
			foreach($recently_run as $report) {
				if(!isset($popular[$report])) $popular[$report] = 1;
				else $popular[$report]++;
			}
		}
		$parts = array();

		foreach($reports as $report) {
			if($report['is_dir'] && $report['children']) {
				//skip if the directory doesn't have a title
				if(!isset($report['Title']) || !$report['Title']) continue;

				$part = trim(self::getReportListJSON($report['children']),'[],');
				if($part) $parts[] = $part;
			}
			else {
				//skip if report is marked as dangerous
				if((isset($report['stop'])&&$report['stop']) || isset($report['Caution']) || isset($report['warning'])) continue;
				if(!isset($report['url'])) continue;
				if(!isset($report['report'])) continue;

				//skip if report is marked as ignore
				if(isset($report['ignore']) && $report['ignore']) continue;

				if(isset($popular[$report['report']])) {
					$popularity = $popular[$report['report']];
				}
				else $popularity = 0;

				$parts[] = json_encode(array(
					'name'=>$report['Name'],
					'url'=>$report['url'],
					'popularity'=>$popularity
				));
			}
		}

		return '['.trim(implode(',',$parts),',').']';
	}

	protected static function getReportHeaders($report) {
		$cacheKey = FileSystemCache::generateCacheKey(array(self::$request->base, $report),'report_headers');

		//check if report data is cached and newer than when the report file was created
		//the url parameter ?nocache will bypass this and not use cache
		$data =false;

		$loc = Report::getFileLocation($report);
		if(!file_exists($loc)) {
			return false;
		}
		if(!isset($_REQUEST['nocache'])) {
			$data = FileSystemCache::retrieve($cacheKey, filemtime($loc));
		}

		//report data not cached, need to parse it
		if($data === false) {
			$temp = new Report($report);

			$data = $temp->options;

			$data['report'] = $report;
			$data['url'] = self::$request->base.'/report/html/?report='.$report;
			$data['is_dir'] = false;
			$data['Id'] = str_replace(array('_','-','/',' ','.'),array('','','_','-','_'),trim($report,'/'));
			if(!isset($data['Name'])) $data['Name'] = ucwords(str_replace(array('_','-'),' ',basename($report)));

			//store parsed report in cache
			FileSystemCache::store($cacheKey, $data);
		}

		return $data;
	}

	protected static function getReports($dir, &$errors = null) {
		$base = self::$config['reportDir'].'/';

		$reports = glob($dir.'*',GLOB_NOSORT);
		$return = array();
		foreach($reports as $key=>$report) {
			$title = $description = false;

			if(is_dir($report)) {
				if(file_exists($report.'/TITLE.txt')) $title = file_get_contents($report.'/TITLE.txt');
				if(file_exists($report.'/README.txt')) $description = file_get_contents($report.'/README.txt');

				$id = str_replace(array('_','-','/',' '),array('','','_','-'),trim(substr($report,strlen($base)),'/'));

				$children = self::getReports($report.'/', $errors);

				$count = 0;
				foreach($children as $child) {
					if(isset($child['count'])) $count += $child['count'];
					else $count++;
				}

				$return[] = array(
					'Name'=>ucwords(str_replace(array('_','-'),' ',basename($report))),
					'Title'=>$title,
					'Id'=> $id,
					'Description'=>$description,
					'is_dir'=>true,
					'children'=>$children,
					'count'=>$count
				);
			}
			else {
				//files to skip
				if(strpos(basename($report),'.') === false) continue;
				$ext = explode('.',$report);
				$ext = array_pop($ext);
				if(!isset(self::$config['default_file_extension_mapping'][$ext])) continue;

				$name = substr($report,strlen($base));

				try {
					$data = self::getReportHeaders($name,$base);
					$return[] = $data;
				}
				catch(Exception $e) {
					if(!$errors) $errors = array();
					$errors[] = array(
						'report'=>$name,
						'exception'=>$e
					);
				}
			}
		}

		usort($return,function(&$a,&$b) {
			if($a['is_dir'] && !$b['is_dir']) return 1;
			elseif($b['is_dir'] && !$a['is_dir']) return -1;

			if(empty($a['Title']) && empty($b['Title'])) return strcmp($a['Name'],$b['Name']);
			elseif(empty($a['Title'])) return 1;
			elseif(empty($b['Title'])) return -1;

			return strcmp($a['Title'], $b['Title']);
		});

		return $return;
	}

	/**
	 * Emails a report given a TO address, a subject, and a message
	 */
	public static function emailReport() {
		if(!isset($_REQUEST['email']) || !filter_var($_REQUEST['email'], FILTER_VALIDATE_EMAIL)) {
			echo json_encode(array('error'=>'Valid email address required'));
			return;
		}
		if(!isset($_REQUEST['url'])) {
			echo json_encode(array('error'=>'Report url required'));
			return;
		}
		if(!isset(PhpReports::$config['mail_settings']['enabled']) || !PhpReports::$config['mail_settings']['enabled']) {
			echo json_encode(array('error'=>'Email is disabled on this server'));
			return;
		}
		if(!isset(PhpReports::$config['mail_settings']['from'])) {
			echo json_encode(array('error'=>'Email settings have not been properly configured on this server'));
			return;
		}

		$from = PhpReports::$config['mail_settings']['from'];
		$subject = $_REQUEST['subject']? $_REQUEST['subject'] : 'Database Report';
		$body = $_REQUEST['message']? $_REQUEST['message'] : "You've been sent a database report!";
		$email = $_REQUEST['email'];
		$link = $_REQUEST['url'];
		$csv_link = str_replace('report/html/?','report/csv/?',$link);
		$table_link = str_replace('report/html/?','report/table/?',$link);
		$text_link = str_replace('report/html/?','report/text/?',$link);

		// Get the CSV file attachment and the inline HTML table
		$csv = self::urlDownload($csv_link);
		$table = self::urlDownload($table_link);
		$text = self::urlDownload($text_link);

		$email_text = $body."\n\n".$text."\n\nView the report online at $link";
		$email_html = "<p>$body</p>$table<p>View the report online at <a href=\"".htmlentities($link)."\">".htmlentities($link)."</a></p>";

		// Create the message
		$message = Swift_Message::newInstance()
		  ->setSubject($subject)
		  ->setFrom($from)
		  ->setTo($email)
		  //text body
		  ->setBody($email_text)
		  //html body
		  ->addPart($email_html, 'text/html')
		;

		$attachment = Swift_Attachment::newInstance()
			->setFilename('report.csv')
			->setContentType('text/csv')
			->setBody($csv)
		;

		$message->attach($attachment);

		// Create the Transport
		$transport = self::getMailTransport();
		$mailer = Swift_Mailer::newInstance($transport);

		try {
			// Send the message
			$result = $mailer->send($message);
		}
		catch(Exception $e) {
			echo json_encode(array(
				'error'=>$e->getMessage()
			));
			return;
		}

		if($result) {
			echo json_encode(array(
				'success'=>true
			));
		}
		else {
			echo json_encode(array(
				'error'=>'Failed to send email to requested recipient'
			));
		}
	}

	/**
	 * Determines the email transport to use based on the configuration settings
	 */
	protected static function getMailTransport() {
		if(!isset(PhpReports::$config['mail_settings'])) PhpReports::$config['mail_settings'] = array();
		if(!isset(PhpReports::$config['mail_settings']['method'])) PhpReports::$config['mail_settings']['method'] = 'mail';

		switch(PhpReports::$config['mail_settings']['method']) {
			case 'mail':
				return Swift_MailTransport::newInstance();
			case 'sendmail':
				return Swift_MailTransport::newInstance(
					isset(PhpReports::$config['mail_settings']['command'])? PhpReports::$config['mail_settings']['command'] : '/usr/sbin/sendmail -bs'
				);
			case 'smtp':
				if(!isset(PhpReports::$config['mail_settings']['server'])) throw new Exception("SMTP server must be configured");
				$transport = Swift_SmtpTransport::newInstance(
					PhpReports::$config['mail_settings']['server'],
					isset(PhpReports::$config['mail_settings']['port'])? PhpReports::$config['mail_settings']['port'] : 25
				);

				//if username/password
				if(isset(PhpReports::$config['mail_settings']['username'])) {
					$transport->setUsername(PhpReports::$config['mail_settings']['username']);
					$transport->setPassword(PhpReports::$config['mail_settings']['password']);
				}

				//if using encryption
				if(isset(PhpReports::$config['mail_settings']['encryption'])) {
					$transport->setEncryption(PhpReports::$config['mail_settings']['encryption']);
				}

				return $transport;
			default:
				throw new Exception("Mail method must be either 'mail', 'sendmail', or 'smtp'");
		}
	}

	/**
	 * Autoloader methods
	 */
	public static function loader($className) {
		if(!isset(self::$loader_cache)) {
			self::buildLoaderCache();
		}

		if(isset(self::$loader_cache[$className])) {
			require_once(self::$loader_cache[$className]);
			return true;
		}
		else {
			return false;
		}
	}
	public static function buildLoaderCache() {
		self::load('classes/local');
		self::load('classes',array('classes/local'));
		self::load('lib');
	}
	public static function load($dir, $skip=array()) {
		$files = glob($dir.'/*.php');
		$dirs = glob($dir.'/*',GLOB_ONLYDIR);


		foreach($files as $file) {
			//for file names same as class name
			$className = basename($file,'.php');
			if(!isset(self::$loader_cache[$className])) self::$loader_cache[$className] = $file;

			//for PEAR style: Path_To_Class.php
			$parts = explode('/',substr($file,0,-4));
			array_shift($parts);
			$className = implode('_',$parts);
			//if any of the directories in the path are lowercase, it isn't in PEAR format
			if(preg_match('/(^|_)[a-z]/',$className)) continue;
			if(!isset(self::$loader_cache[$className])) self::$loader_cache[$className] = $file;
		}

		foreach($dirs as $dir2) {
			//directories to skip
			if($dir2[0]==='.') continue;
			if(in_array($dir2,$skip)) continue;
			if(in_array(basename($dir2),array('tests','test','example','examples','bin'))) continue;

			self::load($dir2,$skip);
		}
	}

	/**
	 * A more lenient json_decode than the built-in PHP one.
	 * It supports strict JSON as well as javascript syntax (i.e. unquoted/single quoted keys, single quoted values, trailing commmas)
	 */
	public static function json_decode($json, $assoc=false) {
		//replace single quoted values
		$json = preg_replace_callback('/:\s*\'(([^\']|\\\\\')*)\'\s*([},])/', function($matches) { return ':'.json_encode(stripslashes($matches[1])).$matches[3]; }, $json);

		//replace single quoted keys
		$json = preg_replace_callback('/\'(([^\']|\\\\\')*)\'\s*:/', function($matches) {return json_encode(stripslashes($matches[1])).':';}, $json);

		//remove any line breaks in the code
		$json = str_replace(array("\n","\r"),"",$json);

		//replace non-quoted keys with double quoted keys
		$json = preg_replace('#(?<pre>\{|\[|,)\s*(?<key>(?:\w|_)+)\s*:#im', '$1"$2":', $json);

		//remove trailing comma
		$json = preg_replace('/,\s*\}/','}',$json);

		return json_decode($json, $assoc);
	}

	protected static function urlDownload($url) {
		$ch = curl_init();
		curl_setopt($ch, CURLOPT_URL, $url);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

		$output = curl_exec($ch);
		curl_close($ch);

		return $output;
	}
}
PhpReports::init();


================================================
FILE: lib/PhpReports/Report.php
================================================
<?php
class Report {
	public $report;
	public $macros = array();
	public $exported_headers = array();
	public $options = array();
	public $is_ready = false;
	public $async = false;
	public $headers = array();
	public $header_lines = array();
	public $raw_query;
	public $use_cache;
	
	protected $raw;
	protected $raw_headers;
	protected $filters = array();
	protected $filemtime;
	protected $has_run = false;
	
	public function __construct($report,$macros = array(), $environment = null, $use_cache = null) {			
		$this->report = $report;
		
		if(!file_exists(self::getFileLocation($report))) {
			throw new Exception('Report not found - '.$report);
		}
		
		$this->filemtime = filemtime(self::getFileLocation($report));
		
		$this->use_cache = $use_cache;
		
		//get the raw report file
		$this->raw = self::getReportFileContents($report);
		
		//if there are no headers in this report
		if(strpos($this->raw,"\n\n") === false) {
			throw new Exception('Report missing headers - '.$report);
		}
		
		//split the raw report into headers and code
		list($this->raw_headers, $this->raw_query) = explode("\n\n",$this->raw,2);
		
		$this->macros = array();
		foreach($macros as $key=>$value) {
			$this->addMacro($key,$value);
		}
		
		$this->parseHeaders();
		
		$this->options['Environment'] = $environment;
		
		$this->initDb();
		
		$this->getTimeEstimate();
	}
	
	public static function getFileLocation($report) {
		//make sure the report path doesn't go up a level for security reasons
		if(strpos($report,"..")!==false) {
			$reportdir = realpath(PhpReports::$config['reportDir']).'/';
			$reportpath = substr(realpath(PhpReports::$config['reportDir'].'/'.$report),0,strlen($reportdir));
			
			if($reportpath !== $reportdir) throw new Exception('Invalid report - '.$report);
		}
		
		$reportDir = PhpReports::$config['reportDir'];
		return $reportDir.'/'.$report;
	}
	
	public static function setReportFileContents($report, $new_contents) {
		echo "SAVING CONTENTS TO ".self::getFileLocation($report);
		
		if(!file_put_contents(self::getFileLocation($report),$new_contents)) {
			throw new Exception("Failed to set report contents");
		}
		
		echo "\n".$new_contents;
	}
	public static function getReportFileContents($report) {
		$contents = file_get_contents(self::getFileLocation($report));
		
		//convert EOL to unix format
		return str_replace(array("\r\n","\r"),"\n",$contents);
	}
	
	public function getDatabase() {
		if(isset($this->options['Database']) && $this->options['Database']) {
			$environment = $this->getEnvironment();
			
			if(isset($environment[$this->options['Database']])) {
				return $environment[$this->options['Database']];
			}
		}
		
		return array();
	}
	public function getEnvironment() {
		return PhpReports::$config['environments'][$this->options['Environment']];
	}
	
	public function addMacro($name, $value) {
		$this->macros[$name] = $value;
	}
	public function exportHeader($name,$params) {
		$this->exported_headers[] = array('name'=>$name,'params'=>$params);
	}
	
	public function getCacheKey() {
		return FileSystemCache::generateCacheKey(array(
			'report'=>$this->report,
			'macros'=>$this->macros,
			'database'=>$this->options['Environment']
		),'report_results');
	}
	public function getReportTimesCacheKey() {
		return FileSystemCache::generateCacheKey($this->report,'report_times');
	}

	protected function retrieveFromCache() {
		if(!$this->use_cache || isset($_GET['nocache'])) {
			return false;
		}
		
		return FileSystemCache::retrieve($this->getCacheKey(),$this->filemtime);
	}
	
	protected function storeInCache() {
		if(isset($this->options['Cache']) && is_numeric($this->options['Cache'])) {
			$ttl = intval($this->options['Cache']);
		}
		else {
			//default to caching things for 10 minutes
			$ttl = 600;
		}
		
		FileSystemCache::store($this->getCacheKey(), $this->options, $ttl);
	}
	
	protected function parseHeaders() {
		//default the report to being ready
		//if undefined variables are found in the headers, set to false
		$this->is_ready = true;
		
		$this->options = array(
			'Filters'=>array(),
			'Variables'=>array(),
			'Includes'=>array(),
		);
		$this->headers = array();
		
		$lines = explode("\n",$this->raw_headers);
		
		//remove empty headers and remove comment characters
		$fixed_lines = array();
		foreach($lines as $line) {
			if(empty($line)) continue;
			
			//if the line doesn't start with a comment character, skip
			if(!in_array(substr($line,0,2),array('--','/*','//',' *')) && $line[0] !== '#') continue;
			
			//remove comment from start of line and skip if empty
			$line = trim(ltrim($line,"-*/# \t"));
			if(!$line) continue;
			
			$fixed_lines[] = $line;
		}
		$lines = $fixed_lines;
		
		$name = null;
		$value = '';
		foreach($lines as $line) {
			$has_name_value = preg_match('/^\s*[A-Z0-9_\-]+\s*\:/',$line);
		
			//if this is the first header and not in the format name:value, assume it is the report name
			if(!$has_name_value && $name === null && (!isset($this->options['Name']) || !$this->options['Name'])) {
				$this->parseHeader('Info',array('name'=>$line));
			}
			else {
				//if this is a continuation of another header
				if(!$has_name_value) {
					$value .= "\n".trim($line);
				}
				//if this is a new header
				else {
					//if the previous header didn't have a name, assume it is the description
					if($value && $name === null) {
						$this->parseHeader('Info',array('description'=>$value));
					}
					//otherwise, parse the previous header
					elseif($value) {
						$this->parseHeader($name,$value);
					}
					
					list($name,$value) = explode(':',$line,2);
					$name = trim($name);
					$value = trim($value);
				
					if(strtoupper($name) === $name) $name = ucfirst(strtolower($name));
				}
			}
		}
		//parse the last header
		if($value && $name) {
			$this->parseHeader($name,$value);
		}
		
		//try to infer report type from file extension
		if(!isset($this->options['Type'])) {
			$tmp = explode('.',$this->report);
			$file_type = array_pop($tmp);
			
			if(!isset(PhpReports::$config['default_file_extension_mapping'][$file_type])) {
				throw new Exception("Unknown report type - ".$this->report);
			}
			else {
				$this->options['Type'] = PhpReports::$config['default_file_extension_mapping'][$file_type];
			}
		}
		
		if(!isset($this->options['Database'])) $this->options['Database'] = strtolower($this->options['Type']);
		
		if(!isset($this->options['Name'])) $this->options['Name'] = $this->report;
	}
	
	public function parseHeader($name,$value,$dataset=null) {
		$classname = $name.'Header';
		if(class_exists($classname)) {
			if($dataset !== null && isset($classname::$validation) && isset($classname::$validation['dataset'])) $value['dataset'] = $dataset;
			$classname::parse($name,$value,$this);
			if(!in_array($name,$this->headers)) $this->headers[] = $name;
		}
		else {
			throw new Exception("Unknown header '$name' - ".$this->report);
		}
	}
	
	public function addFilter($dataset, $column, $type, $options) {
		// If adding for multiple datasets
		if(is_array($dataset)) {
			foreach($dataset as $d) {
				$this->addFilter($d,$column,$type,$options);
			}
		}
		// If adding for all datasets
		else if($dataset === true) {
			$this->addFilter('all',$column,$type,$options);
		}
		// If adding for a single dataset
		else {
			if(!isset($this->filters[$dataset])) $this->filters[$dataset] = array();
			if(!isset($this->filters[$dataset][$column])) $this->filters[$dataset][$column] = array();
			
			$this->filters[$dataset][$column][$type] = $options;
		}
		
	}
	protected function applyFilters($dataset, $column, $value, $row) {
		// First, apply filters for all datasets
		if(isset($this->filters['all']) && isset($this->filters['all'][$column])) {
			foreach($this->filters['all'][$column] as $type=>$options) {
				$classname = $type.'Filter';
				$value = $classname::filter($value, $options, $this, $row);
				
				//if the column should not be displayed
				if($value === false) return false;
			}
		}
		
		// Then apply filters for this specific dataset
		if(isset($this->filters[$dataset]) && isset($this->filters[$dataset][$column])) { 
			foreach($this->filters[$dataset][$column] as $type=>$options) {
				$classname = $type.'Filter';
				$value = $classname::filter($value, $options, $this, $row);
				
				//if the column should not be displayed
				if($value === false) return false;
			}
		}
		
		return $value;
	}
	
	protected function initDb() {
		//if the database isn't set, use the first defined one from config
		$environments = PhpReports::$config['environments'];
		if(!$this->options['Environment']) {
			$this->options['Environment'] = current(array_keys($environments));
		}
		
		//set database options
		$environment_options = array();
		foreach($environments as $key=>$params) {
			$environment_options[] = array(
				'name'=>$key,
				'selected'=>$key===$this->options['Environment']
			);
		}
		$this->options['Environments'] = $environment_options;
		
		//add a host macro
		if(isset($environments[$this->options['Environment']]['host'])) {
			$this->macros['host'] = $environments[$this->options['Environment']]['host'];
		}
		
		$classname = $this->options['Type'].'ReportType';
		
		if(!class_exists($classname)) {
			throw new exception("Unknown report type '".$this->options['Type']."'");
		}
		
		$classname::init($this);
	}
	
	public function getRaw() {
		return $this->raw;
	}
	public function getUrl() {
		return 'report/html/?report='.urlencode($this->report);
	}
	
	public function prepareVariableForm() {
		$vars = array();
		
		if($this->options['Variables']) {
			foreach($this->options['Variables'] as $var => $params) {
				if(!isset($params['name'])) $params['name'] = ucwords(str_replace(array('_','-'),' ',$var));
				if(!isset($params['type'])) $params['type'] = 'string';
				if(!isset($params['options'])) $params['options'] = false;
				$params['value'] = $this->macros[$var];
				$params['key'] = $var;
				
				if($params['type'] === 'select') {
					$params['is_select'] = true;
					
					foreach($params['options'] as $key=>$option) {
						if(!is_array($option)) {
							$params['options'][$key] = array(
								'display'=>$option,
								'value'=>$option
							);
						}
						if($params['options'][$key]['value'] == $params['value']) $params['options'][$key]['selected'] = true;
						elseif(is_array($params['value']) && in_array($params['options'][$key]['value'],$params['value'])) $params['options'][$key]['selected'] = true;
						else $params['options'][$key]['selected'] = false;
						
						if($params['multiple']) {
							$params['is_multiselect'] = true;
							$params['choices'] = count($params['options']);
						}
					}
				}
				else {
					if($params['multiple']) {
						$params['is_textarea'] = true;
					}
				}
				
				if(isset($params['modifier_options'])) {
					$modifier_value = isset($this->macros[$var.'_modifier'])? $this->macros[$var.'_modifier'] : null;
					
					foreach($params['modifier_options'] as $key=>$option) {
						if(!is_array($option)) {
							$params['modifier_options'][$key] = array(
								'display'=>$option,
								'value'=>$option
							);
						}
						
						if($params['modifier_options'][$key]['value'] == $modifier_value) $params['modifier_options'][$key]['selected'] = true;
						else $params['modifier_options'][$key]['selected'] = false;
					}
					
				}
				
				$vars[] = $params;
			}	
		}
		
		return $vars;
	}
	
	protected function _runReport() {
		if(!$this->is_ready) {
			throw new Exception("Report is not ready.  Missing variables");
		}
		
		PhpReports::setVar('Report',$this);
		
		//release the write lock on the session file
		//so the session isn't locked while the report is running
		session_write_close();
		
		$classname = $this->options['Type'].'ReportType';
		
		if(!class_exists($classname)) {
			throw new exception("Unknown report type '".$this->options['Type']."'");
		}
		
		foreach($this->headers as $header) {
			$headerclass = $header.'Header';
			$headerclass::beforeRun($this);
		}
		
		$classname::openConnection($this);
		$datasets = $classname::run($this);		
		$classname::closeConnection($this);
		
		// Convert old single dataset format to multi-dataset format
		if(!isset($datasets[0]['rows']) || !is_array($datasets[0]['rows'])) {
			$datasets = array(
				array(
					'rows'=>$datasets
				)
			);
		}

		// Only include a subset of datasets
		$include = array_keys($datasets);
		if(isset($_GET['dataset'])) {
			$include = array($_GET['dataset']);
		}
		elseif(isset($_GET['datasets'])) {
			// If just a single dataset was specified, make it an array
			if(!is_array($_GET['datasets'])) {
				$include = explode(',',$_GET['datasets']);
			}
			else {
				$include = $_GET['datasets'];
			}
		}

		$this->options['DataSets'] = array();
		foreach($include as $i) {
			if(!isset($datasets[$i])) continue;
			$this->options['DataSets'][$i] = $datasets[$i];
		}

		$this->parseDynamicHeaders();
	}

	protected function parseDynamicHeaders() {
		foreach($this->options['DataSets'] as $i=>&$dataset) {
			if(isset($dataset['headers'])) {
				foreach($dataset['headers'] as $j=>$header) {
					if(isset($header['header']) && isset($header['value'])) {
						$this->parseHeader($header['header'],$header['value'],$i);
					}
				}
			}
		}
	}
	
	protected function getTimeEstimate() {
		$report_times = FileSystemCache::retrieve($this->getReportTimesCacheKey());
		if(!$report_times) return;
		
		sort($report_times);
		
		$sum = array_sum($report_times);
		$count = count($report_times);
		$average = $sum/$count;
		$quartile1 = $report_times[round(($count-1)/4)];
		$median = $report_times[round(($count-1)/2)];
		$quartile3 = $report_times[round(($count-1)*3/4)];
		$min = min($report_times);
		$max = max($report_times);
		$iqr = $quartile3-$quartile1;
		$range = (1.5)*$iqr;
		
		$sample_square = 0;
		for($i = 0; $i < $count; $i++) {
			$sample_square += pow($report_times[$i], 2);
		}
		$standard_deviation = sqrt($sample_square / $count - pow(($average), 2));
		
		$this->options['time_estimate'] = array(
			'times'=>$report_times,
			'count'=>$count,
			'min'=>round($min,2),
			'max'=>round($max,2),
			'median'=>round($median,2),
			'average'=>round($average,2),
			'q1'=>round($quartile1,2),
			'q3'=>round($quartile3,2),
			'iqr'=>round($range,2),
			'sum'=>round($sum,2),
			'stdev'=>round($standard_deviation,2)
		);
	}
	protected function prepareDataSets() {
		foreach($this->options['DataSets'] as $i=>$dataset) {
			$this->prepareRows($i);
		}
		if(isset($this->options['DataSets'][0])) {
			$this->options['Rows'] = $this->options['DataSets'][0]['rows'];
			$this->options['Count'] = $this->options['DataSets'][0]['count'];
		}
	}
	protected function prepareRows($dataset) {
		$rows = array();
		
		//generate list of all values for each numeric column
		//this is used to calculate percentiles/averages/etc.
		$vals = array();
		foreach($this->options['DataSets'][$dataset]['rows'] as $row) {
			foreach($row as $key=>$value) {
				if(!isset($vals[$key])) $vals[$key] = array();
				
				if(is_numeric($value)) $vals[$key][] = $value;
			}
		}
		$this->options['DataSets'][$dataset]['values'] = $vals;
		
		foreach($this->options['DataSets'][$dataset]['rows'] as $row) {
			$rowval = array();
			
			$i=1;
			foreach($row as $key=>$value) {
				$val = new ReportValue($i, $key, $value);
				
				//apply filters for the column key
				$val = $this->applyFilters($dataset,$key,$val,$row);
				//apply filters for the column position
				if($val) $val = $this->applyFilters($dataset,$i,$val,$row);
				
				if($val) {
					$rowval[] = $val;
				}
				
				$i++;
			}
			
			$first = !$rows;
			
			$rows[] = array(
				'values'=>$rowval,
				'first'=>$first
			);
		}
		
		$this->options['DataSets'][$dataset]['rows'] = $rows;
		$this->options['DataSets'][$dataset]['count'] = count($rows);
	}
	
	public function run() {
		if($this->has_run) return true;
		
		//at this point, all the headers are parsed and we haven't run the report yet
		foreach($this->headers as $header) {
			$classname = $header.'Header';
			$classname::afterParse($this);
		}
		
		//record how long it takes to run the report
		$start = microtime(true);
		
		if($this->is_ready && !$this->async) {
			//if the report is cached
			if($options = $this->retrieveFromCache()) {				
				$this->options = $options;
				$this->options['FromCache'] = true;
			}
			else {
				$this->_runReport();
				$this->prepareDataSets();
				$this->storeInCache();
			}

			//add this to the list of recently run reports
			$recently_run_key = FileSystemCache::generateCacheKey('recently_run');
			$recently_run = FileSystemCache::retrieve($recently_run_key);
			if($recently_run === false) {
				$recently_run = array();
			}
			array_unshift($recently_run,$this->report);
			if(count($recently_run) > 200) $recently_run = array_slice($recently_run,0,200);
			FileSystemCache::store($recently_run_key,$recently_run);
		}
		
		//call the beforeRender callback for each header
		foreach($this->headers as $header) {
			$classname = $header.'Header';
			$classname::beforeRender($this);
		}
		
		$this->options['Time'] = round(microtime(true) - $start,5);
		
		if($this->is_ready && !$this->async && !isset($this->options['FromCache'])) {
			//get current report times for this report
			$report_times = FileSystemCache::retrieve($this->getReportTimesCacheKey());
			if(!$report_times) $report_times = array();
			//only keep the last 10 times for each report
			//this keeps the timing data up to date and relevant
			if(count($report_times) > 10) array_shift($report_times);
			
			//store report times
			$report_times[] = $this->options['Time'];
			FileSystemCache::store($this->getReportTimesCacheKey(), $report_times);
		}
		
		$this->has_run = true;
	}
	
	public function renderReportPage($template='html/report', $additional_vars = array()) {
		$this->run();
		
		$template_vars = array(
			'is_ready'=>$this->is_ready,
			'async'=>$this->async,
			'report_url'=>PhpReports::$request->base.'/report/?'.$_SERVER['QUERY_STRING'],
			'report_querystring'=>$_SERVER['QUERY_STRING'],
			'base'=>PhpReports::$request->base,
			'report'=>$this->report,
			'vars'=>$this->prepareVariableForm(),
			'macros'=>$this->macros,
		);
		
		$template_vars = array_merge($template_vars,$additional_vars);
		
		$template_vars = array_merge($template_vars,$this->options);
		
		return PhpReports::render($template, $template_vars);
	}
}
?>


================================================
FILE: lib/PhpReports/ReportFormatBase.php
================================================
<?php
abstract class ReportFormatBase {
	abstract public static function display(&$report, &$request);
	
	public static function prepareReport($report) {
		$environment = $_SESSION['environment'];

		$macros = array();
		if(isset($_GET['macros'])) $macros = $_GET['macros'];

		$report = new Report($report,$macros,$environment);

		return $report;
	}
}


================================================
FILE: lib/PhpReports/ReportTypeBase.php
================================================
<?php
abstract class ReportTypeBase {
	public static function init(&$report) {
		
	}
	
	public static function openConnection(&$report) {
		
	}
	
	public static function closeConnection(&$report) {
		
	}
	
	public static function getVariableOptions($params, &$report) {
		return array();
	}
	
	abstract public static function run(&$report);
}


================================================
FILE: lib/PhpReports/ReportValue.php
================================================
<?php
class ReportValue {
	public $key;
	public $i;
	
	public $original_value;
	public $filtered_value;
	public $html_value;
	public $chart_value;
	
	public $is_html;
	public $type;
	
	public $class;
	
	public function __construct($i, $key, $value) {
		$this->i = $i;
		$this->key = $key;
		$this->original_value = $value;
		$this->filtered_value = is_string($value)? strip_tags($value) : $value;
		$this->html_value = $value;
		$this->chart_value = $value;
		
		$this->is_html = false;
		$this->class = '';
		
		$this->type = $this->_getType();
	}
	
	public function addClass($class) {
		$this->class = trim($this->class . ' ' .$class);
	}
	
	public function setValue($value, $html = false) {		
		if(is_string($value)) $value = trim($value);
		
		if($html) {
			$this->is_html = true;
			$this->html_value = $value;
		}
		else {
			$this->is_html = false;
			$this->filtered_value = is_string($value)? htmlentities($value) : $value;
			$this->html_value = $value;
		}
		
		$this->type = $this->_getType();
	}
	
	protected function _getType($value=null) {
		if(is_null($value)) return null;
		elseif(trim($value) === '') return null;
		elseif(preg_match('/^([$%(\-+\s])*([0-9,]+(\.[0-9]+)?|\.[0-9]+)([$%(\-+\s])*$/',$value)) return 'number';
		elseif(strtotime($value)) return 'date';
		else return 'string';
	}
	protected function _getDisplayValue($value, $html=false, $date=false) {
		$type = $this->_getType($value);
		
		if($type === null) {
			if($html && $this->is_html) return '&nbsp;';
			else return null;
		}
		elseif($type === 'number') {
			return $value;
		}
		elseif($type === 'date') {
			if($date) return date($date,strtotime($value));
			else return $value;
		}
		elseif($type === 'string') {
			return utf8_encode($value);
		}
	}
	
	public function getValue($html = false, $date = false) {
		if($html) {
			$return = $this->_getDisplayValue($this->html_value, true, $date);

			if($this->is_html) {
				return $return;
			}
			else {
				return htmlentities($return);
			}
		}
		else {
			return $this->_getDisplayValue($this->filtered_value, false, $date);
		}
	}
	
	public function getKeyCollapsed() {
		return trim(preg_replace(array('/\s+/','/[^a-zA-Z0-9_]*/'),array('_',''),$this->key),'_');
	}
}


================================================
FILE: lib/adodb/pivottable.inc.php
================================================
<?php
/** 
 * @version V4.93 10 Oct 2006 (c) 2000-2012 John Lim (jlim#natsoft.com). All rights reserved.
 * Released under both BSD license and Lesser GPL library license. 
 * Whenever there is any discrepancy between the two licenses, 
 * the BSD license will take precedence. 
 *
 * Set tabs to 4 for best viewing.
 * 
*/

/*
 * Concept from daniel.lucazeau@ajornet.com. 
 *
 * @param db		Adodb database connection
 * @param tables	List of tables to join
 * @rowfields		List of fields to display on each row
 * @colfield		Pivot field to slice and display in columns, if we want to calculate ranges, we pass in an array
 * @where			Where clause. Optional.
 * @aggfield		This is the field to sum. Optional. 
 * @sumlabel		Prefix to display in sum columns. Optional.
 * @aggfn			Aggregate function to use (could be AVG, SUM, COUNT)
 * @showcount		Show count of records
 *
 * @returns			Sql generated
 */

function PivotTableSQL(&$db, $tables, $rowfields, $colfield, $where = false, $orderBy = false, $limit = false,
                        $aggfield = false, $sumlabel = "Sum {}", $aggfn = "SUM", $includeaggfield = true, $showcount = true) {
	if ($aggfield) {
        $hidecnt = true;
    } else {
        $hidecnt = false;
    }

    $sumlabel = is_null($sumlabel) ? "Sum {}" : $sumlabel;
    $aggfn = is_null($aggfn) ? "SUM" : $aggfn;
	
	$iif = strpos($db->databaseType,'access') !== false; 
	// note - vfp 6 still doesn' work even with IIF enabled || $db->databaseType == 'vfp';
	
 	if ($where) $where = "\nWHERE $where";
	if (!is_array($colfield)) $colarr = $db->GetCol("select distinct $colfield from $tables $where order by 1");
	$hidecnt = $aggfield ? true : false;
	
	$sel = "$rowfields, ";
	if (is_array($colfield)) {
		foreach ($colfield as $k => $v) {
			$k = trim($k);
			if (!$hidecnt) {
				$sel .= $iif ? 
					"\n\t$aggfn(IIF($v,1,0)) AS \"$k\", "
					:
					"\n\t$aggfn(CASE WHEN $v THEN 1 ELSE 0 END) AS \"$k\", ";
			}
			if ($aggfield) {
				$sel .= $iif ?
					"\n\t$aggfn(IIF($v,$aggfield,0)) AS \"$sumlabel$k\", "
					:
					"\n\t$aggfn(CASE WHEN $v THEN $aggfield ELSE 0 END) AS \"$sumlabel$k\", ";
			}
		} 
	} else {
		foreach ($colarr as $v) {
			if (!is_numeric($v)) $vq = $db->qstr($v);
			else $vq = $v;
			$v = trim($v);
			if (strlen($v) == 0	) $v = 'null';
			if (!$hidecnt) {
				$sel .= $iif ?
					"\n\t$aggfn(IIF($colfield=$vq,1,0)) AS \"$v\", "
					:
					"\n\t$aggfn(CASE WHEN $colfield=$vq THEN 1 ELSE 0 END) AS \"$v\", ";
			}
			if ($aggfield) {
				if ($hidecnt) $label = $v;
				else $label = "{$v}_$aggfield";
				$sel .= $iif ?
					"\n\t$aggfn(IIF($colfield=$vq,$aggfield,0)) AS \"$label\", "
					:
					"\n\t$aggfn(CASE WHEN $colfield=$vq THEN $aggfield ELSE 0 END) AS \"$label\", ";
			}
		}
	}
	if ($includeaggfield && ($aggfield && $aggfield != '1')) {
		$agg = "$aggfn($aggfield)";
        if (strstr($sumlabel, '{}')) {
            $sumlabel = trim($sumlabel, ' \t\n\r\0\x0B{}').' '.trim($aggfield);
        }
        $sel .= "\n\t$agg AS \"$sumlabel\", ";
	}
	
	if ($showcount) {
		$sel .= "\n\tSUM(1) as Total";
    } else {
		$sel = substr($sel,0,strlen($sel)-2);
    }

    if ($orderBy) {
        $orderSql = "\nORDER BY $orderBy";
    }
    if ($limit) {
        if (is_numeric($limit)) {
            $limitSql = "\nLIMIT $limit";
        } else {
            $limitSql = "\n-- LIMIT was declared as non-numeric value\n";
        }
    }

	// Strip aliases
	$rowfields = preg_replace('/\s+AS\s+[\'\"]?[\w\s]+[\'\"]?/i', '', $rowfields);
	
	$sql = "SELECT $sel \nFROM $tables $where \nGROUP BY $rowfields $orderSql $limitSql";
	
	return $sql;
}


================================================
FILE: lib/simplediff/SimpleDiff.php
================================================
<?php

/*
	Paul's Simple Diff Algorithm v 0.1
	(C) Paul Butler 2007 <http://www.paulbutler.org/>
	May be used and distributed under the zlib/libpng license.
	
	This code is intended for learning purposes; it was written with short
	code taking priority over performance. It could be used in a practical
	application, but there are a few ways it could be optimized.
	
	Given two arrays, the function diff will return an array of the changes.
	I won't describe the format of the array, but it will be obvious
	if you use print_r() on the result of a diff on some test data.
	
	htmlDiff is a wrapper for the diff command, it takes two strings and
	returns the differences in HTML. The tags used are <ins> and <del>,
	which can easily be styled with CSS.  
*/

class SimpleDiff {
	function diff($old, $new){
		$maxlen = 0;
		foreach($old as $oindex => $ovalue){
			$nkeys = array_keys($new, $ovalue);
			foreach($nkeys as $nindex){
				$matrix[$oindex][$nindex] = isset($matrix[$oindex - 1][$nindex - 1]) ?
					$matrix[$oindex - 1][$nindex - 1] + 1 : 1;
				if($matrix[$oindex][$nindex] > $maxlen){
					$maxlen = $matrix[$oindex][$nindex];
					$omax = $oindex + 1 - $maxlen;
					$nmax = $nindex + 1 - $maxlen;
				}
			}	
		}
		if($maxlen == 0) return array(array('d'=>$old, 'i'=>$new));
		return array_merge(
			self::diff(array_slice($old, 0, $omax), array_slice($new, 0, $nmax)),
			array_slice($new, $nmax, $maxlen),
			self::diff(array_slice($old, $omax + $maxlen), array_slice($new, $nmax + $maxlen))
		);
	}

	function htmlDiff($old, $new){
		$ret = '';
		$diff = self::diff(explode(" ", $old), explode(" ", $new));
		foreach($diff as $k){
			if(is_array($k))
				$ret .= (!empty($k['d'])?"<del>".implode(" ",array_map('htmlentities',$k['d']))."</del> ":'').
					(!empty($k['i'])?"<ins>".implode(" ",array_map('htmlentities',$k['i']))."</ins> ":'');
			else $ret .= htmlentities($k) . " ";
		}
		return $ret;
	}
	
	protected function hasChange($diff, $i, $before=0, $after=0) {
		if($before) if(self::hasChange($diff, $i-1, $before -1, 0)) return true;
		if($after) if(self::hasChange($diff, $i+1, 0, $after -1)) return true;
		
		if(!isset($diff[$i])) return false;
		if(!is_array($diff[$i])) return false;
		if($diff[$i]['i'] || $diff[$i]['d']) return true;
		else return false;
	}
	
	function htmlDiffSummary($old, $new){
		$ret = '';
		$diff = self::diff(explode("\n", $old), explode("\n", $new));
		
		$diff_section = false;
		
		foreach($diff as $i=>$k){
			//if we are within 1 lines of a change
			if(self::hasChange($diff,$i,1,1)) {
				//if we aren't already in a diff section, start it
				if(!$diff_section) {
					$diff_section = true;
					$ret .= "<div class='section'><div class='line_number'>Line $i</div>";
				}
			}
			else {
				//close the diff section
				$diff_section = false;
				$ret .= "</div>";
			}
			
			if(is_array($k))
				$ret .= (!empty($k['d'])?"<del>".implode("\n",array_map('htmlentities',$k['d']))."</del>\n":'').
					(!empty($k['i'])?"<ins>".implode("\n",array_map('htmlentities',$k['i']))."</ins>\n":'');
			elseif($diff_section) {
				$ret .= htmlentities($k) . "\n";
			}
		}
		
		if($diff_section) $ret .= "</div>";
		
		return $ret;
	}
}
?>


================================================
FILE: public/css/bootstrap-multiselect.css
================================================
.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container input[type="text"]{width:70%}.multiselect-container .input-prepend{padding:3px}.multiselect-container>li{padding:0}.multiselect-container>li>label{margin:0;padding:3px 20px 3px 20px;height:100%;cursor:pointer}.multiselect-container>li>label.multiselect-header{margin:0;padding:3px 20px 3px 20px;height:100%}.multiselect-container>li>label>input[type="checkbox"]{margin-bottom:5px}

================================================
FILE: public/css/datepicker.css
================================================
 /*
	Datepicker for Bootstrap
	Copyright 2012 Stefan Petre
	Licensed under the Apache License v2.0
	http://www.apache.org/licenses/LICENSE-2.0
*/
 .datepicker { top: 0; left: 0; padding: 4px; margin-top: 1px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; /*.dow { border-top: 1px solid #ddd !important; }*/ } .datepicker:before { content: ''; display: inline-block; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 7px solid #ccc; border-bottom-color: rgba(0, 0, 0, 0.2); position: absolute; top: -7px; left: 6px; } .datepicker:after { content: ''; display: inline-block; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 6px solid #ffffff; position: absolute; top: -6px; left: 7px; } .datepicker > div { display: none; } .datepicker table { width: 100%; margin: 0; } .datepicker td, .datepicker th { text-align: center; width: 20px; height: 20px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .datepicker td.day:hover { background: #eeeeee; cursor: pointer; } .datepicker td.old, .datepicker td.new { color: #999999; } .datepicker td.active, .datepicker td.active:hover { background-color: #006dcc; background-image: -moz-linear-gradient(top, #0088cc, #0044cc); background-image: -ms-linear-gradient(top, #0088cc, #0044cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); background-image: -o-linear-gradient(top, #0088cc, #0044cc); background-image: linear-gradient(top, #0088cc, #0044cc); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); border-color: #0044cc #0044cc #002a80; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .datepicker td.active:hover, .datepicker td.active:hover:hover, .datepicker td.active:active, .datepicker td.active:hover:active, .datepicker td.active.active, .datepicker td.active:hover.active, .datepicker td.active.disabled, .datepicker td.active:hover.disabled, .datepicker td.active[disabled], .datepicker td.active:hover[disabled] { background-color: #0044cc; } .datepicker td.active:active, .datepicker td.active:hover:active, .datepicker td.active.active, .datepicker td.active:hover.active { background-color: #003399 \9; } .datepicker td span { display: block; width: 47px; height: 54px; line-height: 54px; float: left; margin: 2px; cursor: pointer; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; } .datepicker td span:hover { background: #eeeeee; } .datepicker td span.active { background-color: #006dcc; background-image: -moz-linear-gradient(top, #0088cc, #0044cc); background-image: -ms-linear-gradient(top, #0088cc, #0044cc); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); background-image: -o-linear-gradient(top, #0088cc, #0044cc); background-image: linear-gradient(top, #0088cc, #0044cc); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); border-color: #0044cc #0044cc #002a80; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); } .datepicker td span.active:hover, .datepicker td span.active:active, .datepicker td span.active.active, .datepicker td span.active.disabled, .datepicker td span.active[disabled] { background-color: #0044cc; } .datepicker td span.active:active, .datepicker td span.active.active { background-color: #003399 \9; } .datepicker td span.old { color: #999999; } .datepicker th.switch { width: 145px; } .datepicker th.next, .datepicker th.prev { font-size: 19.5px; } .datepicker thead tr:first-child th { cursor: pointer; } .datepicker thead tr:first-child th:hover { background: #eeeeee; } .input-append.date .add-on i, .input-prepend.date .add-on i { display: block; cursor: pointer; width: 16px; height: 16px; }

================================================
FILE: public/css/daterangepicker-bs3.css
================================================
/*!
 * Stylesheet for the Date Range Picker, for use with Bootstrap 3.x
 *
 * Copyright 2013 Dan Grossman ( http://www.dangrossman.info )
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Built for http://www.improvely.com
 */

.daterangepicker.dropdown-menu {
    max-width: none;
    z-index: 3000;
}

.daterangepicker.opensleft .ranges, .daterangepicker.opensleft .calendar {
    float: left;
    margin: 4px;
}

.daterangepicker.opensright .ranges, .daterangepicker.opensright .calendar {
    float: right;
    margin: 4px;
}

.daterangepicker .ranges {
    width: 160px;
    text-align: left;
}

.daterangepicker .ranges .range_inputs>div {
    float: left;
}

.daterangepicker .ranges .range_inputs>div:nth-child(2) {
    padding-left: 11px;
}

.daterangepicker .calendar {
    display: none;
    max-width: 270px;
}

.daterangepicker .calendar.single .calendar-date {
    border: none;
}

.daterangepicker .calendar th, .daterangepicker .calendar td {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
    white-space: nowrap;
    text-align: center;
    min-width: 32px;
}

.daterangepicker .ranges label {
    color: #333;
    display: block;
    font-size: 11px;
    font-weight: normal;
    height: 20px;
    line-height: 20px;
    margin-bottom: 2px;
    text-shadow: #fff 1px 1px 0px;
    text-transform: uppercase;
    width: 74px;
}

.daterangepicker .ranges input {
    font-size: 11px;
}

.daterangepicker .ranges .input-mini {
    background-color: #eee;
    border: 1px solid #ccc;
    border-radius: 4px;
    color: #555;
    display: block;
    font-size: 11px;
    height: 30px;
    line-height: 30px;
    vertical-align: middle;
    margin: 0 0 10px 0;
    padding: 0 6px;
    width: 74px;
}

.daterangepicker .ranges ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.daterangepicker .ranges li {
    font-size: 13px;
    background: #f5f5f5;
    border: 1px solid #f5f5f5;
    color: #08c;
    padding: 3px 12px;
    margin-bottom: 8px;
    -webkit-border-radius: 5px;
    -moz-border-radius: 5px;
    border-radius: 5px;
    cursor: pointer;
}

.daterangepicker .ranges li.active, .daterangepicker .ranges li:hover {
    background: #08c;
    border: 1px solid #08c;
    color: #fff;
}

.daterangepicker .calendar-date {
    border: 1px solid #ddd;
    padding: 4px;
    border-radius: 4px;
    background: #fff;
}

.daterangepicker .calendar-time {
    text-align: center;
    margin: 8px auto 0 auto;
    line-height: 30px;
}

.daterangepicker {
    position: absolute;
    background: #fff;
    top: 100px;
    left: 20px;
    padding: 4px;
    margin-top: 1px;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
}

.daterangepicker.opensleft:before {
    position: absolute;
    top: -7px;
    right: 9px;
    display: inline-block;
    border-right: 7px solid transparent;
    border-bottom: 7px solid #ccc;
    border-left: 7px solid transparent;
    border-bottom-color: rgba(0, 0, 0, 0.2);
    content: '';
}

.daterangepicker.opensleft:after {
    position: absolute;
    top: -6px;
    right: 10px;
    display: inline-block;
    border-right: 6px solid transparent;
    border-bottom: 6px solid #fff;
    border-left: 6px solid transparent;
    content: '';
}

.daterangepicker.opensright:before {
    position: absolute;
    top: -7px;
    left: 9px;
    display: inline-block;
    border-right: 7px solid transparent;
    border-bottom: 7px solid #ccc;
    border-left: 7px solid transparent;
    border-bottom-color: rgba(0, 0, 0, 0.2);
    content: '';
}

.daterangepicker.opensright:after {
    position: absolute;
    top: -6px;
    left: 10px;
    display: inline-block;
    border-right: 6px solid transparent;
    border-bottom: 6px solid #fff;
    border-left: 6px solid transparent;
    content: '';
}

.daterangepicker table {
    width: 100%;
    margin: 0;
}

.daterangepicker td, .daterangepicker th {
    text-align: center;
    width: 20px;
    height: 20px;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    cursor: pointer;
    white-space: nowrap;
}

.daterangepicker td.off {
    color: #999;
}

.daterangepicker td.disabled {
    color: #999;
}

.daterangepicker td.available:hover, .daterangepicker th.available:hover {
    background: #eee;
}

.daterangepicker td.in-range {
    background: #ebf4f8;
    -webkit-border-radius: 0;
    -moz-border-radius: 0;
    border-radius: 0;
}

.daterangepicker td.active, .daterangepicker td.active:hover {
    background-color: #357ebd;
    border-color: #3071a9;
    color: #fff;
}

.daterangepicker td.week, .daterangepicker th.week {
    font-size: 80%;
    color: #ccc;
}

.daterangepicker select.monthselect, .daterangepicker select.yearselect {
    font-size: 12px;
    padding: 1px;
    height: auto;
    margin: 0;
    cursor: default;
}

.daterangepicker select.monthselect {
    margin-right: 2%;
    width: 56%;
}

.daterangepicker select.yearselect {
    width: 40%;
}

.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.ampmselect {
    width: 50px;
    margin-bottom: 0;
}

.daterangepicker_start_input {
    float: left;
}

.daterangepicker_end_input {
    float: left;
    padding-left: 11px
}

.daterangepicker th.month {
    width: auto;
}


================================================
FILE: public/css/jquery.dataTables.css
================================================
/*
 * Table styles
 */
table.dataTable {
  width: auto;
  clear: both;
  border-collapse: separate;
  border-spacing: 0;
  /*
   * Header and footer styles
   */
  /*
   * Body styles
   */
}

table.dataTable thead {
	background-color: white;
	box-shadow: 0 3px 15px rgba(0,0,0,.3);
}

table.dataTable thead th,
table.dataTable tfoot th {
  font-weight: bold;
}
table.dataTable thead th,
table.dataTable thead td {
  padding: 10px 18px;
  border-bottom: 1px solid #111111;
}
table.dataTable thead th:active,
table.dataTable thead td:active {
  outline: none;
}
table.dataTable tfoot th,
table.dataTable tfoot td {
  padding: 10px 18px 6px 18px;
  border-top: 1px solid #111111;
}
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting {
  cursor: pointer;
  *cursor: hand;
}
table.dataTable thead .sorting {
  background: url("../images/sort_both.png") no-repeat center right;
}
table.dataTable thead .sorting_asc {
  background: url("../images/sort_asc.png") no-repeat center right;
}
table.dataTable thead .sorting_desc {
  background: url("../images/sort_desc.png") no-repeat center right;
}
table.dataTable thead .sorting_asc_disabled {
  background: url("../images/sort_asc_disabled.png") no-repeat center right;
}
table.dataTable thead .sorting_desc_disabled {
  background: url("../images/sort_desc_disabled.png") no-repeat center right;
}
table.dataTable tbody tr {
  background-color: white;
}
table.dataTable tbody tr.selected {
  background-color: #b0bed9;
}
table.dataTable tbody th,
table.dataTable tbody td {
  padding: 8px 10px;
}
table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
  border-top: 1px solid #dddddd;
}
table.dataTable.row-border tbody tr:first-child th,
table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
table.dataTable.display tbody tr:first-child td {
  border-top: none;
}
table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
  border-top: 1px solid #dddddd;
  border-right: 1px solid #dddddd;
}
table.dataTable.cell-border tbody tr th:first-child,
table.dataTable.cell-border tbody tr td:first-child {
  border-left: 1px solid #dddddd;
}
table.dataTable.cell-border tbody tr:first-child th,
table.dataTable.cell-border tbody tr:first-child td {
  border-top: none;
}
table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
  background-color: #f9f9f9;
}
table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
  background-color: #abb9d3;
}
table.dataTable.hover tbody tr:hover,
table.dataTable.hover tbody tr.odd:hover,
table.dataTable.hover tbody tr.even:hover, table.dataTable.display tbody tr:hover,
table.dataTable.display tbody tr.odd:hover,
table.dataTable.display tbody tr.even:hover {
  background-color: whitesmoke;
}
table.dataTable.hover tbody tr:hover.selected,
table.dataTable.hover tbody tr.odd:hover.selected,
table.dataTable.hover tbody tr.even:hover.selected, table.dataTable.display tbody tr:hover.selected,
table.dataTable.display tbody tr.odd:hover.selected,
table.dataTable.display tbody tr.even:hover.selected {
  background-color: #a9b7d1;
}
table.dataTable.order-column tbody tr > .sorting_1,
table.dataTable.order-column tbody tr > .sorting_2,
table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
table.dataTable.display tbody tr > .sorting_2,
table.dataTable.display tbody tr > .sorting_3 {
  background-color: #f9f9f9;
}
table.dataTable.order-column tbody tr.selected > .sorting_1,
table.dataTable.order-column tbody tr.selected > .sorting_2,
table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
table.dataTable.display tbody tr.selected > .sorting_2,
table.dataTable.display tbody tr.selected > .sorting_3 {
  background-color: #acbad4;
}
table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
  background-color: #f1f1f1;
}
table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
  background-color: #f3f3f3;
}
table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
  background-color: whitesmoke;
}
table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
  background-color: #a6b3cd;
}
table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
  background-color: #a7b5ce;
}
table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
  background-color: #a9b6d0;
}
table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
  background-color: #f9f9f9;
}
table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
  background-color: #fbfbfb;
}
table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
  background-color: #fdfdfd;
}
table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
  background-color: #acbad4;
}
table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
  background-color: #adbbd6;
}
table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
  background-color: #afbdd8;
}
table.dataTable.display tbody tr:hover > .sorting_1,
table.dataTable.display tbody tr.odd:hover > .sorting_1,
table.dataTable.display tbody tr.even:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1,
table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_1,
table.dataTable.order-column.hover tbody tr.even:hover > .sorting_1 {
  background-color: #eaeaea;
}
table.dataTable.display tbody tr:hover > .sorting_2,
table.dataTable.display tbody tr.odd:hover > .sorting_2,
table.dataTable.display tbody tr.even:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2,
table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_2,
table.dataTable.order-column.hover tbody tr.even:hover > .sorting_2 {
  background-color: #ebebeb;
}
table.dataTable.display tbody tr:hover > .sorting_3,
table.dataTable.display tbody tr.odd:hover > .sorting_3,
table.dataTable.display tbody tr.even:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3,
table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_3,
table.dataTable.order-column.hover tbody tr.even:hover > .sorting_3 {
  background-color: #eeeeee;
}
table.dataTable.display tbody tr:hover.selected > .sorting_1,
table.dataTable.display tbody tr.odd:hover.selected > .sorting_1,
table.dataTable.display tbody tr.even:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1,
table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_1,
table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_1 {
  background-color: #a1aec7;
}
table.dataTable.display tbody tr:hover.selected > .sorting_2,
table.dataTable.display tbody tr.odd:hover.selected > .sorting_2,
table.dataTable.display tbody tr.even:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2,
table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_2,
table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_2 {
  background-color: #a2afc8;
}
table.dataTable.display tbody tr:hover.selected > .sorting_3,
table.dataTable.display tbody tr.odd:hover.selected > .sorting_3,
table.dataTable.display tbody tr.even:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3,
table.dataTable.order-column.hover tbody tr.odd:hover.selected > .sorting_3,
table.dataTable.order-column.hover tbody tr.even:hover.selected > .sorting_3 {
  background-color: #a4b2cb;
}
table.dataTable.no-footer {
  border-bottom: 1px solid #111111;
}
table.dataTable.nowrap th, table.dataTable.nowrap td {
  white-space: nowrap;
}
table.dataTable.compact thead th,
table.dataTable.compact thead td {
  padding: 5px 9px;
}
table.dataTable.compact tfoot th,
table.dataTable.compact tfoot td {
  padding: 5px 9px 3px 9px;
}
table.dataTable.compact tbody th,
table.dataTable.compact tbody td {
  padding: 4px 5px;
}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
  text-align: left;
}
table.dataTable th.dt-center,
table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
  text-align: center;
}
table.dataTable th.dt-right,
table.dataTable td.dt-right {
  text-align: right;
}
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
  text-align: justify;
}
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
  white-space: nowrap;
}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
  text-align: left;
}
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center {
  text-align: center;
}
table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
  text-align: right;
}
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
  text-align: justify;
}
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap {
  white-space: nowrap;
}
table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left {
  text-align: left;
}
table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center {
  text-align: center;
}
table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right {
  text-align: right;
}
table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify {
  text-align: justify;
}
table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
  white-space: nowrap;
}

table.dataTable,
table.dataTable th,
table.dataTable td {
  -webkit-box-sizing: content-box;
  -moz-box-sizing: content-box;
  box-sizing: content-box;
}

/*
 * Control feature layout
 */
.dataTables_wrapper {
  position: relative;
  clear: both;
  *zoom: 1;
  zoom: 1;
}
.dataTables_wrapper .dataTables_length {
  float: left;
}
.dataTables_wrapper .dataTables_filter {
	display: inline-block;

}
.dataTables_wrapper .dataTables_filter input {
  margin-left: 0.5em;
}
.dataTables_wrapper .dataTables_info {
  clear: both;
  padding-top: 0.755em;
  display:inline-block;
  margin-left: 20px;
}
.dataTables_wrapper .dataTables_paginate {
  padding-top: 0.25em;
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
  box-sizing: border-box;
  display: inline-block;
  min-width: 1.5em;
  padding: 0.5em 1em;
  margin-left: 2px;
  text-align: center;
  text-decoration: none !important;
  cursor: pointer;
  *cursor: hand;
  color: #333333 !important;
  border: 1px solid transparent;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
  color: #333333 !important;
  border: 1px solid #cacaca;
  background-color: white;
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, gainsboro));
  /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(top, white 0%, gainsboro 100%);
  /* Chrome10+,Safari5.1+ */
  background: -moz-linear-gradient(top, white 0%, gainsboro 100%);
  /* FF3.6+ */
  background: -ms-linear-gradient(top, white 0%, gainsboro 100%);
  /* IE10+ */
  background: -o-linear-gradient(top, white 0%, gainsboro 100%);
  /* Opera 11.10+ */
  background: linear-gradient(to bottom, white 0%, gainsboro 100%);
  /* W3C */
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
  cursor: default;
  color: #666 !important;
  border: 1px solid transparent;
  background: transparent;
  box-shadow: none;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
  color: white !important;
  border: 1px solid #111111;
  background-color: #585858;
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111111));
  /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(top, #585858 0%, #111111 100%);
  /* Chrome10+,Safari5.1+ */
  background: -moz-linear-gradient(top, #585858 0%, #111111 100%);
  /* FF3.6+ */
  background: -ms-linear-gradient(top, #585858 0%, #111111 100%);
  /* IE10+ */
  background: -o-linear-gradient(top, #585858 0%, #111111 100%);
  /* Opera 11.10+ */
  background: linear-gradient(to bottom, #585858 0%, #111111 100%);
  /* W3C */
}
.dataTables_wrapper .dataTables_paginate .paginate_button:active {
  outline: none;
  background-color: #2b2b2b;
  background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
  /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
  /* Chrome10+,Safari5.1+ */
  background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
  /* FF3.6+ */
  background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
  /* IE10+ */
  background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
  /* Opera 11.10+ */
  background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
  /* W3C */
  box-shadow: inset 0 0 3px #111;
}
.dataTables_wrapper .dataTables_processing {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 100%;
  height: 40px;
  margin-left: -50%;
  margin-top: -25px;
  padding-top: 20px;
  text-align: center;
  font-size: 1.2em;
  background-color: white;
  background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
  /* Chrome,Safari4+ */
  background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
  /* Chrome10+,Safari5.1+ */
  background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
  /* FF3.6+ */
  background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
  /* IE10+ */
  background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
  /* Opera 11.10+ */
  background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
  /* W3C */
}
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate {
  color: #333333;
}
.dataTables_wrapper .dataTables_scroll {
  clear: both;
}
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
  *margin-top: -1px;
  -webkit-overflow-scrolling: touch;
}
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody td > div.dataTables_sizing {
  height: 0;
  overflow: hidden;
  margin: 0 !important;
  padding: 0 !important;
}
.dataTables_wrapper.no-footer .dataTables_scrollBody {
  border-bottom: 1px solid #111111;
}
.dataTables_wrapper.no-footer div.dataTables_scrollHead table,
.dataTables_wrapper.no-footer div.dataTables_scrollBody table {
  border-bottom: none;
}
.dataTables_wrapper:after {
  visibility: hidden;
  display: block;
  content: "";
  clear: both;
  height: 0;
}

@media screen and (max-width: 767px) {
  .dataTables_wrapper .dataTables_info,
  .dataTables_wrapper .dataTables_paginate {
    float: none;
    text-align: center;
  }
  .dataTables_wrapper .dataTables_paginate {
    margin-top: 0.5em;
  }
}
@media screen and (max-width: 640px) {
  .dataTables_wrapper .dataTables_length,
  .dataTables_wrapper .dataTables_filter {
    float: none;
    text-align: center;
  }
  .dataTables_wrapper .dataTables_filter {
    margin-top: 0.5em;
  }
}





================================================
FILE: public/css/prettify.css
================================================
.com { color: #93a1a1; }
.lit { color: #195f91; }
.pun, .opn, .clo { color: #93a1a1; }
.fun { color: #dc322f; }
.str, .atv { color: #D14; }
.kwd, .prettyprint .tag { color: #1e347b; }
.typ, .atn, .dec, .var { color: teal; }
.pln { color: #48484c; }

.prettyprint {
  padding: 8px;
  background-color: #f7f7f9;
  border: 1px solid #e1e1e8;
}
.prettyprint.linenums {
  -webkit-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
     -moz-box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
          box-shadow: inset 40px 0 0 #fbfbfc, inset 41px 0 0 #ececf0;
}

/* Specify class=linenums on a pre to get line numbering */
ol.linenums {
  margin: 0 0 0 33px; /* IE indents via margin-left */
}
ol.linenums li {
  padding-left: 12px;
  color: #bebec5;
  line-height: 20px;
  text-shadow: 0 1px 0 #fff;
}

================================================
FILE: public/css/report.css
================================================
body {
	padding-bottom: 200px;
}
.database_message {
	color: #999;
}
.result_table td .bar{
	height: 18px; 
	display:inline-block; 
	margin-right: 5px; 
	vertical-align:middle;
	background: #6bba70; /* Old browsers */
	background: -moz-linear-gradient(top, #6bba70 0%, #255e2b 100%); /* FF3.6+ */
	background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6bba70), color-stop(100%,#255e2b)); /* Chrome,Safari4+ */
	background: -webkit-linear-gradient(top, #6bba70 0%,#255e2b 100%); /* Chrome10+,Safari5.1+ */
	background: -o-linear-gradient(top, #6bba70 0%,#255e2b 100%); /* Opera 11.10+ */
	background: -ms-linear-gradient(top, #6bba70 0%,#255e2b 100%); /* IE10+ */
	background: linear-gradient(top, #6bba70 0%,#255e2b 100%); /* W3C */
	filter: progid:DXImageTr
Download .txt
gitextract_kc8to4kf/

├── .gitignore
├── .htaccess
├── LICENSE
├── README.md
├── classes/
│   ├── filters/
│   │   ├── barFilter.php
│   │   ├── classFilter.php
│   │   ├── dateFilter.php
│   │   ├── drilldownFilter.php
│   │   ├── geoipFilter.php
│   │   ├── hideFilter.php
│   │   ├── htmlFilter.php
│   │   ├── imgsizeFilter.php
│   │   ├── linkFilter.php
│   │   ├── numberFilter.php
│   │   ├── paddingFilter.php
│   │   ├── preFilter.php
│   │   └── twigFilter.php
│   ├── headers/
│   │   ├── ChartHeader.php
│   │   ├── ColumnsHeader.php
│   │   ├── FilterHeader.php
│   │   ├── FormattingHeader.php
│   │   ├── IncludeHeader.php
│   │   ├── InfoHeader.php
│   │   ├── OptionsHeader.php
│   │   ├── RollupHeader.php
│   │   ├── VariableHeader.php
│   │   └── deprecated/
│   │       ├── CacheHeader.php
│   │       ├── CautionHeader.php
│   │       ├── ColumnHeader.php
│   │       ├── CreatedHeader.php
│   │       ├── DatabaseHeader.php
│   │       ├── DescriptionHeader.php
│   │       ├── DetailHeader.php
│   │       ├── MongodatabaseHeader.php
│   │       ├── NameHeader.php
│   │       ├── NoteHeader.php
│   │       ├── OptionHeader.php
│   │       ├── PlotHeader.php
│   │       ├── StatusHeader.php
│   │       ├── TotalHeader.php
│   │       ├── TotalsHeader.php
│   │       ├── TypeHeader.php
│   │       └── ValueHeader.php
│   ├── report_formats/
│   │   ├── ChartReportFormat.php
│   │   ├── CsvReportFormat.php
│   │   ├── DebugReportFormat.php
│   │   ├── HtmlReportFormat.php
│   │   ├── JsonReportFormat.php
│   │   ├── RawReportFormat.php
│   │   ├── SqlReportFormat.php
│   │   ├── TableReportFormat.php
│   │   ├── TextReportFormat.php
│   │   ├── XlsReportBase.php
│   │   ├── XlsReportFormat.php
│   │   ├── XlsxReportFormat.php
│   │   └── XmlReportFormat.php
│   └── report_types/
│       ├── AdoPivotReportType.php
│       ├── AdoReportType.php
│       ├── MongoReportType.php
│       ├── MysqlReportType.php
│       ├── PdoReportType.php
│       └── PhpReportType.php
├── composer.json
├── config/
│   └── config.php.sample
├── index.php
├── lib/
│   ├── PhpReports/
│   │   ├── FilterBase.php
│   │   ├── HeaderBase.php
│   │   ├── PhpReports.php
│   │   ├── Report.php
│   │   ├── ReportFormatBase.php
│   │   ├── ReportTypeBase.php
│   │   └── ReportValue.php
│   ├── adodb/
│   │   └── pivottable.inc.php
│   └── simplediff/
│       └── SimpleDiff.php
├── public/
│   ├── css/
│   │   ├── bootstrap-multiselect.css
│   │   ├── datepicker.css
│   │   ├── daterangepicker-bs3.css
│   │   ├── jquery.dataTables.css
│   │   ├── prettify.css
│   │   ├── report.css
│   │   ├── report_list.css
│   │   ├── timeline.css
│   │   └── typeahead.js-bootstrap.css
│   └── js/
│       ├── ace/
│       │   ├── ace.js
│       │   ├── ext-searchbox.js
│       │   ├── ext-spellcheck.js
│       │   ├── ext-static_highlight.js
│       │   ├── ext-textarea.js
│       │   ├── mode-diff.js
│       │   ├── mode-html.js
│       │   ├── mode-javascript.js
│       │   ├── mode-json.js
│       │   ├── mode-php.js
│       │   ├── mode-sql.js
│       │   ├── mode-text.js
│       │   ├── mode-xml.js
│       │   ├── mode-yaml.js
│       │   ├── theme-eclipse.js
│       │   ├── worker-javascript.js
│       │   ├── worker-json.js
│       │   └── worker-php.js
│       ├── bootstrap-datepicker.js
│       ├── bootstrap-multiselect.js
│       ├── daterangepicker-1.3.2.js
│       ├── html5shiv.js
│       ├── jquery.browser.js
│       ├── jquery.cookie.js
│       ├── jquery.iframe-auto-height.plugin.1.9.3.js
│       ├── jquery.stickytableheaders.js
│       ├── lang-sql.js
│       ├── prettify.js
│       ├── scripts.js
│       └── timeline.js
├── sample_dashboards/
│   └── timezones.json
├── sample_reports/
│   ├── ado/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   ├── names.ado
│   │   └── names.pivot
│   ├── mongodb/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   └── log-events.js
│   ├── mysql/
│   │   ├── README.txt
│   │   ├── TITLE.txt
│   │   ├── all-orders.sql
│   │   └── drilldown/
│   │       └── customer-orders.sql
│   └── php/
│       ├── README.txt
│       ├── TITLE.txt
│       ├── functions.php
│       ├── timezones.php
│       └── timezones_multiple.php
└── templates/
    └── default/
        ├── csv/
        │   └── report.twig
        ├── html/
        │   ├── blank_page.twig
        │   ├── chart_page.twig
        │   ├── chart_report.twig
        │   ├── content_only.twig
        │   ├── dashboard.twig
        │   ├── dashboard_list.twig
        │   ├── page.twig
        │   ├── report.twig
        │   ├── report_ajax_loading.twig
        │   ├── report_content.twig
        │   ├── report_editor.twig
        │   ├── report_list.twig
        │   ├── report_list_item.twig
        │   ├── report_list_table_of_contents_item.twig
        │   ├── table.twig
        │   └── variable_form.twig
        ├── sql/
        │   └── report.twig
        └── xml/
            ├── page.twig
            └── report.twig
Download .txt
SYMBOL INDEX (423 symbols across 83 files)

FILE: classes/filters/barFilter.php
  class barFilter (line 2) | class barFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/classFilter.php
  class classFilter (line 2) | class classFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/dateFilter.php
  class dateFilter (line 2) | class dateFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/drilldownFilter.php
  class drilldownFilter (line 2) | class drilldownFilter extends linkFilter {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/geoipFilter.php
  class geoipFilter (line 2) | class geoipFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/hideFilter.php
  class hideFilter (line 2) | class hideFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/htmlFilter.php
  class htmlFilter (line 2) | class htmlFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/imgsizeFilter.php
  class imgsizeFilter (line 2) | class imgsizeFilter extends FilterBase {
    method filter (line 5) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/linkFilter.php
  class linkFilter (line 2) | class linkFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/numberFilter.php
  class numberFilter (line 9) | class numberFilter extends FilterBase {
    method filter (line 10) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/paddingFilter.php
  class paddingFilter (line 2) | class paddingFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/preFilter.php
  class preFilter (line 2) | class preFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/filters/twigFilter.php
  class twigFilter (line 2) | class twigFilter extends FilterBase {
    method filter (line 3) | public static function filter($value, $options = array(), &$report, &$...

FILE: classes/headers/ChartHeader.php
  class ChartHeader (line 2) | class ChartHeader extends HeaderBase {
    method init (line 114) | public static function init($params, &$report) {
    method fixDimension (line 139) | protected static function fixDimension($dim) {
    method parseShortcut (line 144) | public static function parseShortcut($value) {
    method getRowInfo (line 179) | protected static function getRowInfo(&$rows, $params, $num, &$report) {
    method generateHistogramRows (line 287) | protected static function generateHistogramRows($rows, $column, $num_b...
    method determineDataType (line 344) | protected static function determineDataType($value) {
    method beforeRender (line 356) | public static function beforeRender(&$report) {
    method _processChart (line 391) | protected static function _processChart($num, &$params, $dataset, &$re...

FILE: classes/headers/ColumnsHeader.php
  class ColumnsHeader (line 2) | class ColumnsHeader extends HeaderBase {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 12) | public static function parseShortcut($value) {

FILE: classes/headers/FilterHeader.php
  class FilterHeader (line 2) | class FilterHeader extends HeaderBase {
    method init (line 21) | public static function init($params, &$report) {
    method parseShortcut (line 31) | public static function parseShortcut($value) {

FILE: classes/headers/FormattingHeader.php
  class FormattingHeader (line 2) | class FormattingHeader extends HeaderBase {
    method init (line 40) | public static function init($params, &$report) {
    method parseShortcut (line 45) | public static function parseShortcut($value) {
    method beforeRender (line 68) | public static function beforeRender(&$report) {

FILE: classes/headers/IncludeHeader.php
  class IncludeHeader (line 2) | class IncludeHeader extends HeaderBase {
    method init (line 10) | public static function init($params, &$report) {
    method parseShortcut (line 42) | public static function parseShortcut($value) {

FILE: classes/headers/InfoHeader.php
  class InfoHeader (line 2) | class InfoHeader extends HeaderBase {
    method init (line 25) | public static function init($params, &$report) {
    method parseShortcut (line 33) | public static function parseShortcut($value) {

FILE: classes/headers/OptionsHeader.php
  class OptionsHeader (line 2) | class OptionsHeader extends HeaderBase {
    method init (line 78) | public static function init($params, &$report) {
    method parseShortcut (line 120) | public static function parseShortcut($value) {

FILE: classes/headers/RollupHeader.php
  class RollupHeader (line 2) | class RollupHeader extends HeaderBase {
    method init (line 15) | public static function init($params, &$report) {
    method beforeRender (line 35) | public static function beforeRender(&$report) {

FILE: classes/headers/VariableHeader.php
  class VariableHeader (line 2) | class VariableHeader extends HeaderBase {
    method init (line 49) | public static function init($params, &$report) {
    method parseShortcut (line 82) | public static function parseShortcut($value) {
    method afterParse (line 180) | public static function afterParse(&$report) {
    method beforeRun (line 207) | public static function beforeRun(&$report) {

FILE: classes/headers/deprecated/CacheHeader.php
  class CacheHeader (line 2) | class CacheHeader extends OptionsHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/CautionHeader.php
  class CautionHeader (line 2) | class CautionHeader extends HeaderBase {
    method init (line 10) | public static function init($params, &$report) {
    method parseShortcut (line 18) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/ColumnHeader.php
  class ColumnHeader (line 2) | class ColumnHeader extends ColumnsHeader {
    method init (line 3) | public static function init($params, &$report) {

FILE: classes/headers/deprecated/CreatedHeader.php
  class CreatedHeader (line 2) | class CreatedHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/DatabaseHeader.php
  class DatabaseHeader (line 2) | class DatabaseHeader extends OptionsHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/DescriptionHeader.php
  class DescriptionHeader (line 2) | class DescriptionHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/DetailHeader.php
  class DetailHeader (line 2) | class DetailHeader extends HeaderBase {
    method init (line 17) | public static function init($params, &$report) {
    method parseShortcut (line 23) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/MongodatabaseHeader.php
  class MongodatabaseHeader (line 2) | class MongodatabaseHeader extends OptionsHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/NameHeader.php
  class NameHeader (line 2) | class NameHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/NoteHeader.php
  class NoteHeader (line 2) | class NoteHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/OptionHeader.php
  class OptionHeader (line 2) | class OptionHeader extends OptionsHeader {
    method init (line 3) | public static function init($params, &$report) {

FILE: classes/headers/deprecated/PlotHeader.php
  class PlotHeader (line 4) | class PlotHeader extends ChartHeader {
    method init (line 5) | public static function init($params, &$report) {

FILE: classes/headers/deprecated/StatusHeader.php
  class StatusHeader (line 2) | class StatusHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/TotalHeader.php
  class TotalHeader (line 2) | class TotalHeader extends TotalsHeader {
    method init (line 3) | public static function init($params, &$report) {

FILE: classes/headers/deprecated/TotalsHeader.php
  class TotalsHeader (line 2) | class TotalsHeader extends HeaderBase {
    method init (line 10) | public static function init($params, &$report) {
    method parseShortcut (line 14) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/TypeHeader.php
  class TypeHeader (line 2) | class TypeHeader extends InfoHeader {
    method init (line 3) | public static function init($params, &$report) {
    method parseShortcut (line 9) | public static function parseShortcut($value) {

FILE: classes/headers/deprecated/ValueHeader.php
  class ValueHeader (line 2) | class ValueHeader extends HeaderBase {
    method init (line 13) | public static function init($params, &$report) {
    method parseShortcut (line 29) | public static function parseShortcut($value) {

FILE: classes/report_formats/ChartReportFormat.php
  class ChartReportFormat (line 2) | class ChartReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/CsvReportFormat.php
  class CsvReportFormat (line 2) | class CsvReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/DebugReportFormat.php
  class DebugReportFormat (line 2) | class DebugReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/HtmlReportFormat.php
  class HtmlReportFormat (line 2) | class HtmlReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/JsonReportFormat.php
  class JsonReportFormat (line 2) | class JsonReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {
    method getDataSet (line 47) | public static function getDataSet($i, &$report) {

FILE: classes/report_formats/RawReportFormat.php
  class RawReportFormat (line 2) | class RawReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {
    method prepareReport (line 12) | public static function prepareReport($report) {

FILE: classes/report_formats/SqlReportFormat.php
  class SqlReportFormat (line 2) | class SqlReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/TableReportFormat.php
  class TableReportFormat (line 2) | class TableReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/TextReportFormat.php
  class TextReportFormat (line 2) | class TextReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {
    method displayDataSet (line 26) | protected static function displayDataSet($dataset) {

FILE: classes/report_formats/XlsReportBase.php
  class XlsReportBase (line 2) | abstract class XlsReportBase extends ReportFormatBase {
    method columnLetter (line 3) | private static function columnLetter($c){
    method getExcelRepresantation (line 17) | public static function getExcelRepresantation(&$report) {
    method addSheet (line 40) | public static function addSheet($objPHPExcel,$dataset, $i) {

FILE: classes/report_formats/XlsReportFormat.php
  class XlsReportFormat (line 2) | class XlsReportFormat extends XlsReportBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/XlsxReportFormat.php
  class XlsxReportFormat (line 2) | class XlsxReportFormat extends XlsReportBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_formats/XmlReportFormat.php
  class XmlReportFormat (line 2) | class XmlReportFormat extends ReportFormatBase {
    method display (line 3) | public static function display(&$report, &$request) {

FILE: classes/report_types/AdoPivotReportType.php
  class AdoPivotReportType (line 2) | class AdoPivotReportType extends ReportTypeBase {
    method init (line 3) | public static function init(&$report) {
    method openConnection (line 33) | public static function openConnection(&$report) {
    method closeConnection (line 44) | public static function closeConnection(&$report) {
    method getVariableOptions (line 52) | public static function getVariableOptions($params, &$report) {
    method run (line 102) | public static function run(&$report) {

FILE: classes/report_types/AdoReportType.php
  class AdoReportType (line 2) | class AdoReportType extends ReportTypeBase {
    method init (line 3) | public static function init(&$report) {
    method openConnection (line 54) | public static function openConnection(&$report) {
    method closeConnection (line 65) | public static function closeConnection(&$report) {
    method getVariableOptions (line 73) | public static function getVariableOptions($params, &$report) {
    method run (line 104) | public static function run(&$report) {

FILE: classes/report_types/MongoReportType.php
  class MongoReportType (line 2) | class MongoReportType extends ReportTypeBase {
    method init (line 3) | public static function init(&$report) {
    method openConnection (line 26) | public static function openConnection(&$report) {
    method closeConnection (line 30) | public static function closeConnection(&$report) {
    method run (line 34) | public static function run(&$report) {

FILE: classes/report_types/MysqlReportType.php
  class MysqlReportType (line 2) | class MysqlReportType extends PdoReportType {

FILE: classes/report_types/PdoReportType.php
  class PdoReportType (line 2) | class PdoReportType extends ReportTypeBase {
    method init (line 5) | public static function init(&$report) {
    method openConnection (line 55) | public static function openConnection(&$report) {
    method closeConnection (line 99) | public static function closeConnection(&$report) {
    method getVariableOptions (line 105) | public static function getVariableOptions($params, &$report) {
    method run (line 135) | public static function run(&$report) {

FILE: classes/report_types/PhpReportType.php
  class PhpReportType (line 2) | class PhpReportType extends ReportTypeBase {
    method init (line 3) | public static function init(&$report) {
    method openConnection (line 23) | public static function openConnection(&$report) {
    method closeConnection (line 27) | public static function closeConnection(&$report) {
    method run (line 31) | public static function run(&$report) {

FILE: lib/PhpReports/FilterBase.php
  class FilterBase (line 2) | abstract class FilterBase {
    method filter (line 11) | abstract public static function filter($value, $options=array(), &$rep...

FILE: lib/PhpReports/HeaderBase.php
  class HeaderBase (line 2) | class HeaderBase {
    method parse (line 5) | public static function parse($key, $value, &$report) {
    method init (line 34) | public static function init($params, &$report) {
    method parseShortcut (line 38) | public static function parseShortcut($value) {
    method beforeRender (line 42) | public static function beforeRender(&$report) {
    method afterParse (line 46) | public static function afterParse(&$report) {
    method beforeRun (line 50) | public static function beforeRun(&$report) {
    method validate (line 54) | protected static function validate($params) {

FILE: lib/PhpReports/PhpReports.php
  class PhpReports (line 2) | class PhpReports {
    method init (line 12) | public static function init($config = 'config/config.php') {
    method setVar (line 84) | public static function setVar($key,$value) {
    method getVar (line 89) | public static function getVar($key, $default=null) {
    method dbdate (line 94) | public static function dbdate($time, $database=null, $format=null) {
    method generateSqlIN (line 140) | public static function generateSqlIN($column, $values, $or_null = fals...
    method render (line 155) | public static function render($template, $macros) {
    method renderString (line 173) | public static function renderString($template, $macros) {
    method displayReport (line 177) | public static function displayReport($report,$type) {
    method editReport (line 214) | public static function editReport($report) {
    method listReports (line 249) | public static function listReports() {
    method listDashboards (line 261) | public static function listDashboards() {
    method displayDashboard (line 273) | public static function displayDashboard($dashboard) {
    method getDashboards (line 281) | public static function getDashboards() {
    method getDashboard (line 293) | public static function getDashboard($dashboard) {
    method getRecentReports (line 302) | public static function getRecentReports() {
    method getReportListJSON (line 322) | public static function getReportListJSON($reports=null) {
    method getReportHeaders (line 372) | protected static function getReportHeaders($report) {
    method getReports (line 406) | protected static function getReports($dir, &$errors = null) {
    method emailReport (line 478) | public static function emailReport() {
    method getMailTransport (line 562) | protected static function getMailTransport() {
    method loader (line 600) | public static function loader($className) {
    method buildLoaderCache (line 613) | public static function buildLoaderCache() {
    method load (line 618) | public static function load($dir, $skip=array()) {
    method json_decode (line 651) | public static function json_decode($json, $assoc=false) {
    method urlDownload (line 670) | protected static function urlDownload($url) {

FILE: lib/PhpReports/Report.php
  class Report (line 2) | class Report {
    method __construct (line 20) | public function __construct($report,$macros = array(), $environment = ...
    method getFileLocation (line 56) | public static function getFileLocation($report) {
    method setReportFileContents (line 69) | public static function setReportFileContents($report, $new_contents) {
    method getReportFileContents (line 78) | public static function getReportFileContents($report) {
    method getDatabase (line 85) | public function getDatabase() {
    method getEnvironment (line 96) | public function getEnvironment() {
    method addMacro (line 100) | public function addMacro($name, $value) {
    method exportHeader (line 103) | public function exportHeader($name,$params) {
    method getCacheKey (line 107) | public function getCacheKey() {
    method getReportTimesCacheKey (line 114) | public function getReportTimesCacheKey() {
    method retrieveFromCache (line 118) | protected function retrieveFromCache() {
    method storeInCache (line 126) | protected function storeInCache() {
    method parseHeaders (line 138) | protected function parseHeaders() {
    method parseHeader (line 224) | public function parseHeader($name,$value,$dataset=null) {
    method addFilter (line 236) | public function addFilter($dataset, $column, $type, $options) {
    method applyFilters (line 256) | protected function applyFilters($dataset, $column, $value, $row) {
    method initDb (line 282) | protected function initDb() {
    method getRaw (line 313) | public function getRaw() {
    method getUrl (line 316) | public function getUrl() {
    method prepareVariableForm (line 320) | public function prepareVariableForm() {
    method _runReport (line 381) | protected function _runReport() {
    method parseDynamicHeaders (line 440) | protected function parseDynamicHeaders() {
    method getTimeEstimate (line 452) | protected function getTimeEstimate() {
    method prepareDataSets (line 489) | protected function prepareDataSets() {
    method prepareRows (line 498) | protected function prepareRows($dataset) {
    method run (line 544) | public function run() {
    method renderReportPage (line 603) | public function renderReportPage($template='html/report', $additional_...

FILE: lib/PhpReports/ReportFormatBase.php
  class ReportFormatBase (line 2) | abstract class ReportFormatBase {
    method display (line 3) | abstract public static function display(&$report, &$request);
    method prepareReport (line 5) | public static function prepareReport($report) {

FILE: lib/PhpReports/ReportTypeBase.php
  class ReportTypeBase (line 2) | abstract class ReportTypeBase {
    method init (line 3) | public static function init(&$report) {
    method openConnection (line 7) | public static function openConnection(&$report) {
    method closeConnection (line 11) | public static function closeConnection(&$report) {
    method getVariableOptions (line 15) | public static function getVariableOptions($params, &$report) {
    method run (line 19) | abstract public static function run(&$report);

FILE: lib/PhpReports/ReportValue.php
  class ReportValue (line 2) | class ReportValue {
    method __construct (line 16) | public function __construct($i, $key, $value) {
    method addClass (line 30) | public function addClass($class) {
    method setValue (line 34) | public function setValue($value, $html = false) {
    method _getType (line 50) | protected function _getType($value=null) {
    method _getDisplayValue (line 57) | protected function _getDisplayValue($value, $html=false, $date=false) {
    method getValue (line 76) | public function getValue($html = false, $date = false) {
    method getKeyCollapsed (line 92) | public function getKeyCollapsed() {

FILE: lib/adodb/pivottable.inc.php
  function PivotTableSQL (line 28) | function PivotTableSQL(&$db, $tables, $rowfields, $colfield, $where = fa...

FILE: lib/simplediff/SimpleDiff.php
  class SimpleDiff (line 21) | class SimpleDiff {
    method diff (line 22) | function diff($old, $new){
    method htmlDiff (line 44) | function htmlDiff($old, $new){
    method hasChange (line 56) | protected function hasChange($diff, $i, $before=0, $after=0) {
    method htmlDiffSummary (line 66) | function htmlDiffSummary($old, $new){

FILE: public/js/ace/ace.js
  function o (line 1) | function o(e){var i=function(e,t){return r("",e,t)},s=t;e&&(t[e]||(t[e]=...
  function o (line 1) | function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline...
  function u (line 1) | function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(v...
  function m (line 1) | function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentine...
  function o (line 1) | function o(e,t,n){var s=0;!i.isOpera||"KeyboardEvent"in window||!i.isMac...
  function s (line 1) | function s(o){n(o),i||(i=!0,r(o)),t.removeListener(e,"mousemove",n),t.re...
  function r (line 1) | function r(e){t&&t(e),n&&n(e),document.removeEventListener("mousemove",t...
  function b (line 1) | function b(e){if(h)return;var t=e?2:1,r=2;try{n.setSelectionRange(t,r)}c...
  function w (line 1) | function w(){if(h)return;n.value=a,i.isWebKit&&y.schedule()}
  function B (line 1) | function B(){setTimeout(function(){p&&(n.style.cssText=p,p=""),t.rendere...
  function o (line 1) | function o(e){e.$clickSelection=null;var t=e.editor;t.setDefaultHandler(...
  function u (line 1) | function u(e,t,n,r){return Math.sqrt(Math.pow(n-e,2)+Math.pow(r-t,2))}
  function a (line 1) | function a(e,t){if(e.start.row==e.end.row)var n=2*t.column-e.start.colum...
  function s (line 1) | function s(e){function f(){u=r.createElement("div"),u.className="ace_gut...
  function r (line 1) | function r(e){e.on("click",function(t){var n=t.getDocumentPosition(),r=e...
  function g (line 1) | function g(e){return e<4352?!1:e>=4352&&e<=4447||e>=4515&&e<=4519||e>=46...
  function r (line 1) | function r(e){var n=e.action=="insertText"||e.action=="insertLines";retu...
  function u (line 1) | function u(t){var r=e.slice(i,t),o=r.length;r.join("").replace(/12/g,fun...
  function f (line 1) | function f(e){return e.replace(/-(.)/g,function(e,t){return t.toUpperCas...
  function r (line 1) | function r(e){var n=/\w{4}/g;for(var r in e)t.packages[r]=e[r].replace(n...
  function u (line 1) | function u(){this.getFoldAt=function(e,t,n){var r=this.getFoldLine(e);if...
  function i (line 1) | function i(e,t){this.foldData=e,Array.isArray(t)?this.folds=t:t=this.fol...
  function s (line 1) | function s(){this.findMatchingBracket=function(e,t){if(e.column==0)retur...
  function i (line 1) | function i(e,t){this.platform=t,this.commands={},this.commmandKeyBinding...
  function s (line 1) | function s(e,t){return{win:e,mac:t}}
  function r (line 1) | function r(e){i.importCssString(e.cssText,e.cssClass,t.container.ownerDo...
  function i (line 1) | function i(e,t,n){var i=0,s=0;while(s+e[i].value.length<t){s+=e[i].value...
  function h (line 1) | function h(e,t,n){return c.$options.wrap=!0,c.$options.needle=t,c.$optio...
  function v (line 1) | function v(e,t){return e.row==t.row&&e.column==t.column}
  function m (line 1) | function m(e){e.$onAddRange=e.$onAddRange.bind(e),e.$onRemoveRange=e.$on...
  function g (line 1) | function g(e){function i(){n&&(r.style.cursor="",n=!1)}var t=e.textInput...
  function o (line 1) | function o(e){return a.stringRepeat(" ",e)}
  function u (line 1) | function u(e){return e[2]?o(r)+e[2]+o(i-e[2].length+s)+e[4].replace(/^([...
  function f (line 1) | function f(e){return e[2]?o(r+i-e[2].length)+e[2]+o(s," ")+e[4].replace(...
  function l (line 1) | function l(e){return e[2]?o(r)+e[2]+o(s)+e[4].replace(/^([=:])\s+/,"$1 "...
  function i (line 1) | function i(e,t){return e.row==t.row&&e.column==t.column}
  function s (line 1) | function s(e){var t=e.domEvent,n=t.altKey,s=t.shiftKey,o=e.getAccelKey()...

FILE: public/js/ace/ext-textarea.js
  function a (line 1) | function a(e,t){for(var n in t)e.style[n]=t[n]}
  function f (line 1) | function f(e,t){if(e.type!="textarea")throw"Textarea required!";var n=e....
  function l (line 1) | function l(t,n,r){s.loadScript(t,function(){e([n],r)})}
  function c (line 1) | function c(n,r,i,s,o,u){function c(e){return e=="true"}var a=n.getSessio...
  function h (line 1) | function h(e,t,n,i){function f(e,t,n,r){e.push("<select title='"+t+"'>")...

FILE: public/js/ace/mode-html.js
  function r (line 1) | function r(e){return[{token:"string",regex:'"',next:e+"_qqstring"},{toke...
  function i (line 1) | function i(e,t){return[{token:"string",regex:e,next:t},{token:"constant....
  function a (line 1) | function a(e,t){var n=!0,r=e.type.split("."),i=t.split(".");return i.for...
  function u (line 1) | function u(e,t){var n=!0,r=e.type.split("."),i=t.split(".");return i.for...

FILE: public/js/ace/mode-php.js
  function r (line 1) | function r(e){return[{token:"string",regex:'"',next:e+"_qqstring"},{toke...
  function i (line 1) | function i(e,t){return[{token:"string",regex:e,next:t},{token:"constant....

FILE: public/js/ace/mode-xml.js
  function r (line 1) | function r(e){return[{token:"string",regex:'"',next:e+"_qqstring"},{toke...
  function i (line 1) | function i(e,t){return[{token:"string",regex:e,next:t},{token:"constant....
  function u (line 1) | function u(e,t){var n=!0,r=e.type.split("."),i=t.split(".");return i.for...

FILE: public/js/ace/worker-javascript.js
  function initBaseUrls (line 1) | function initBaseUrls(e){require.tlns=e}
  function initSender (line 1) | function initSender(){var e=require(null,"ace/lib/event_emitter").EventE...
  function o (line 1) | function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline...
  function u (line 1) | function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(v...
  function m (line 1) | function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentine...
  function startRegex (line 1) | function startRegex(e){return RegExp("^("+e.join("|")+")")}
  function ot (line 1) | function ot(){}
  function ut (line 1) | function ut(e,t){return Object.prototype.hasOwnProperty.call(e,t)}
  function at (line 1) | function at(e,t){i[e]===undefined&&n[e]===undefined&&bt("Bad option: '"+...
  function ft (line 1) | function ft(e){return Object.prototype.toString.call(e)==="[object Strin...
  function lt (line 1) | function lt(e){return e>="a"&&e<="z￿"||e>="A"&&e<="Z￿"}
  function ct (line 1) | function ct(e){return e>="0"&&e<="9"}
  function ht (line 1) | function ht(e,t){return e?!e.identifier||e.value!==t?!1:!0:!1}
  function pt (line 1) | function pt(e,t){return e.replace(/\{([^{}]*)\}/g,function(e,n){var r=t[...
  function dt (line 1) | function dt(e,t){var n;for(n in t)ut(t,n)&&!ut(r.blacklist,n)&&(e[n]=t[n])}
  function vt (line 1) | function vt(){Object.keys(r.blacklist).forEach(function(e){delete O[e]})}
  function mt (line 1) | function mt(){A.couch&&dt(O,a),A.rhino&&dt(O,H),A.prototypejs&&dt(O,D),A...
  function gt (line 1) | function gt(e,t,n){var r=Math.floor(t/E.length*100);throw{name:"JSHintEr...
  function yt (line 1) | function yt(e,t,n,i){return r.undefs.push([e,t,n,i])}
  function bt (line 1) | function bt(e,t,n,i,s,o){var u,a,f;return t=t||C,t.id==="(end)"&&(t=z),a...
  function wt (line 1) | function wt(e,t,n,r,i,s,o){return bt(e,{line:t,from:n},r,i,s,o)}
  function Et (line 1) | function Et(e,t,n,r,i,s){bt(e,t,n,r,i,s)}
  function St (line 1) | function St(e,t,n,r,i,s,o){return Et(e,{line:t,from:n},r,i,s,o)}
  function xt (line 1) | function xt(e,t){var n;return n={id:"(internal)",elem:e,value:t},r.inter...
  function Nt (line 1) | function Nt(e,t,n){e==="hasOwnProperty"&&bt("'hasOwnProperty' is a reall...
  function Ct (line 1) | function Ct(){var e=C,t=e.value,i=A.quotmark,u={},a,l,c,p,d,v,m;switch(t...
  function kt (line 1) | function kt(e){var t=e||0,n=0,r;while(n<=t)r=S[n],r||(r=S[n]=Tt.token())...
  function Lt (line 1) | function Lt(t,n){switch(z.id){case"(number)":C.id==="."&&bt("A dot follo...
  function At (line 1) | function At(t,n){var r,i=!1,s=!1;C.id==="(end)"&&Et("Unexpected early en...
  function Ot (line 1) | function Ot(e,t){e=e||z,t=t||C,A.white&&e.character!==t.from&&e.line===t...
  function Mt (line 1) | function Mt(e,t){e=e||z,t=t||C,A.white&&(e.character!==t.from||e.line!==...
  function _t (line 1) | function _t(e,t){e=e||z,t=t||C,A.white&&!e.comment&&e.line===t.line&&Ot(...
  function Dt (line 1) | function Dt(e,t){if(A.white){e=e||z,t=t||C;if(e.value===";"&&t.value==="...
  function Pt (line 1) | function Pt(e,t){e=e||z,t=t||C,!A.laxbreak&&e.line!==t.line?bt("Bad line...
  function Ht (line 1) | function Ht(e){var t;A.white&&C.id!=="(end)"&&(t=y+(e||0),C.from!==t&&bt...
  function Bt (line 1) | function Bt(e){e=e||z,e.line!==C.line&&bt("Line breaking error '{a}'.",e...
  function jt (line 1) | function jt(){z.line!==C.line?A.laxcomma||(jt.first&&(bt("Comma warnings...
  function Ft (line 1) | function Ft(e,t){var n=R[e];if(!n||typeof n!="object")R[e]=n={id:e,lbp:t...
  function It (line 1) | function It(e){return Ft(e,0)}
  function qt (line 1) | function qt(e,t){var n=It(e);return n.identifier=n.reserved=!0,n.fud=t,n}
  function Rt (line 1) | function Rt(e,t){var n=qt(e,t);return n.block=!0,n}
  function Ut (line 1) | function Ut(e){var t=e.id.charAt(0);if(t>="a"&&t<="z"||t>="A"&&t<="Z")e....
  function zt (line 1) | function zt(e,t){var n=Ft(e,150);return Ut(n),n.nud=typeof t=="function"...
  function Wt (line 1) | function Wt(e,t){var n=It(e);return n.type=e,n.nud=t,n}
  function Xt (line 1) | function Xt(e,t){var n=Wt(e,t);return n.identifier=n.reserved=!0,n}
  function Vt (line 1) | function Vt(e,t){return Xt(e,function(){return typeof t=="function"&&t(t...
  function $t (line 1) | function $t(e,t,n,r){var i=Ft(e,n);return Ut(i),i.led=function(i){return...
  function Jt (line 1) | function Jt(e,t){var n=Ft(e,100);return n.led=function(e){Pt(_,z),Dt(z,C...
  function Kt (line 1) | function Kt(e){return e&&(e.type==="(number)"&&+e.value===0||e.type==="(...
  function Qt (line 1) | function Qt(e){return Ft(e,20).exps=!0,$t(e,function(e,t){t.left=e,O[e.v...
  function Gt (line 1) | function Gt(e,t,n){var r=Ft(e,n);return Ut(r),r.led=typeof t=="function"...
  function Yt (line 1) | function Yt(e){return Ft(e,20).exps=!0,$t(e,function(e,t){A.bitwise&&bt(...
  function Zt (line 1) | function Zt(e){var t=Ft(e,150);return t.led=function(e){return A.plusplu...
  function en (line 1) | function en(e){if(C.identifier)return Lt(),z.reserved&&!A.es5&&(!e||z.va...
  function tn (line 1) | function tn(e){var t=en(e);if(t)return t;z.id==="function"&&C.id==="("?b...
  function nn (line 1) | function nn(e){var t=0,n;if(C.id!==";"||L)return;for(;;){n=kt(t);if(n.re...
  function rn (line 1) | function rn(e){var t=y,n,r=B,i=C;if(i.id===";"){Lt(";");return}i.identif...
  function sn (line 1) | function sn(e){var t=[],n;while(!C.reach&&C.id!=="(end)")C.id===";"?(n=k...
  function on (line 1) | function on(){var e,t,n;for(;;){if(C.id==="(string)"){t=kt(0);if(t.id===...
  function un (line 1) | function un(e,t,n){var r,i=g,s=y,o,u=B,a,f,l;g=e;if(!e||!A.funcscope)B=O...
  function an (line 1) | function an(e){T&&typeof T[e]!="boolean"&&bt("Unexpected /*member '{a}'....
  function fn (line 1) | function fn(e){var t=e.value,n=e.line,r=m[t];typeof r=="function"&&(r=!1...
  function ln (line 1) | function ln(){var e=en(!0);return e||(C.id==="(string)"?(e=C.value,Lt())...
  function cn (line 1) | function cn(){var e=C,t=[],n;Lt("("),_t();if(C.id===")"){Lt(")");return}...
  function hn (line 1) | function hn(t,n){var r,i=A,s=B;return A=Object.create(A),B=Object.create...
  function pn (line 1) | function pn(e){return{statementCount:0,nestedBlockDepth:-1,ComplexityCou...
  function dn (line 1) | function dn(){h["(metrics)"].ComplexityCount+=1}
  function mn (line 1) | function mn(){function e(){var e={},t=C;Lt("{");if(C.id!=="}")for(;;){if...
  function s (line 1) | function s(){var e,n,s;return r>=E.length?!1:(t=1,i=E[r],r+=1,A.smarttab...
  function o (line 1) | function o(e,i){function u(e){if(!A.proto&&e==="__proto__"){wt("The '{a}...
  function E (line 1) | function E(e){var r=e.exec(i),s;if(r)return p=r[0].length,s=r[1],u=s.cha...
  function S (line 1) | function S(e){function c(e){var n=parseInt(i.substr(a+1,e),16);a+=e,n>=3...
  function o (line 1) | function o(e,t){s[e]&&ut(s,e)?bt("Duplicate member '{a}'.",C,n):s[e]={},...
  function u (line 1) | function u(e,t){s[e]&&ut(s,e)?(s[e].basic||s[e].setter)&&bt("Duplicate m...
  function a (line 1) | function a(e){s[e]&&ut(s,e)?(s[e].basic||s[e].getter)&&bt("Duplicate mem...
  function t (line 1) | function t(){var e=B,t;Lt("catch"),Dt(z,C),Lt("("),B=Object.create(e),t=...

FILE: public/js/ace/worker-json.js
  function initBaseUrls (line 1) | function initBaseUrls(e){require.tlns=e}
  function initSender (line 1) | function initSender(){var e=require(null,"ace/lib/event_emitter").EventE...
  function o (line 1) | function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline...
  function u (line 1) | function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(v...
  function m (line 1) | function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentine...

FILE: public/js/ace/worker-php.js
  function initBaseUrls (line 1) | function initBaseUrls(e){require.tlns=e}
  function initSender (line 1) | function initSender(){var e=require(null,"ace/lib/event_emitter").EventE...
  function o (line 1) | function o(e){return(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline...
  function u (line 1) | function u(e,t,n){if(Array.prototype.indexOf)return e.indexOf(t,n);for(v...
  function m (line 1) | function m(e){try{return Object.defineProperty(e,"sentinel",{}),"sentine...

FILE: public/js/bootstrap-multiselect.js
  function Multiselect (line 30) | function Multiselect(select, options) {

FILE: public/js/html5shiv.js
  function m (line 4) | function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}
  function i (line 4) | function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}
  function p (line 4) | function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=...
  function t (line 4) | function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.cr...
  function q (line 5) | function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a...

FILE: public/js/jquery.cookie.js
  function decode (line 20) | function decode(s) {
  function decodeAndParse (line 30) | function decodeAndParse(s) {

FILE: public/js/jquery.iframe-auto-height.plugin.1.9.3.js
  function debug (line 54) | function debug(message) {
  function showDiagnostics (line 61) | function showDiagnostics(iframe, calledFrom) {
  function findStrategy (line 101) | function findStrategy(browser) {
  function resizeHeight (line 123) | function resizeHeight(iframe) {

FILE: public/js/jquery.stickytableheaders.js
  function Plugin (line 15) | function Plugin (el, options) {

FILE: public/js/prettify.js
  function L (line 2) | function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var...
  function M (line 6) | function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.classN...
  function B (line 7) | function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}
  function x (line 7) | function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(...
  function u (line 9) | function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''...
  function D (line 12) | function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.clas...
  function k (line 15) | function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(...
  function C (line 15) | function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-m...
  function E (line 15) | function E(a){var m=
  function m (line 25) | function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Inf...

FILE: public/js/timeline.js
  function filter (line 5102) | function filter (arr) {
  function isLoaded (line 6014) | function isLoaded (url) {
  function isLoading (line 6034) | function isLoading (url) {
  function load (line 6044) | function load (url, callback, sendCallbackWhenAlreadyLoaded) {
  function loadAll (line 6090) | function loadAll (urls, callback, sendCallbackWhenAlreadyLoaded) {
  function filterImageUrls (line 6126) | function filterImageUrls (elem, urls) {

FILE: sample_reports/php/functions.php
  function array_stats (line 13) | function array_stats($array, $key) {
Condensed preview — 150 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,436K chars).
[
  {
    "path": ".gitignore",
    "chars": 85,
    "preview": ".idea/\nconfig/config.php\nreports/\ncache/\nclasses/local/*.php\ntemplates/local\nvendor/\n"
  },
  {
    "path": ".htaccess",
    "chars": 152,
    "preview": "RewriteEngine On\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule ^(.*)$ index.php [Q"
  },
  {
    "path": "LICENSE",
    "chars": 7651,
    "preview": "                   GNU LESSER GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007"
  },
  {
    "path": "README.md",
    "chars": 3756,
    "preview": "Php Reports\n===========\n\nA reporting framework for managing and displaying nice looking, exportable reports from any dat"
  },
  {
    "path": "classes/filters/barFilter.php",
    "chars": 501,
    "preview": "<?php\nclass barFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row) "
  },
  {
    "path": "classes/filters/classFilter.php",
    "chars": 188,
    "preview": "<?php\nclass classFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row"
  },
  {
    "path": "classes/filters/dateFilter.php",
    "chars": 965,
    "preview": "<?php\nclass dateFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row)"
  },
  {
    "path": "classes/filters/drilldownFilter.php",
    "chars": 2051,
    "preview": "<?php\nclass drilldownFilter extends linkFilter {\t\n\tpublic static function filter($value, $options = array(), &$report, &"
  },
  {
    "path": "classes/filters/geoipFilter.php",
    "chars": 684,
    "preview": "<?php\nclass geoipFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row"
  },
  {
    "path": "classes/filters/hideFilter.php",
    "chars": 144,
    "preview": "<?php\nclass hideFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row)"
  },
  {
    "path": "classes/filters/htmlFilter.php",
    "chars": 171,
    "preview": "<?php\nclass htmlFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row)"
  },
  {
    "path": "classes/filters/imgsizeFilter.php",
    "chars": 549,
    "preview": "<?php\nclass imgsizeFilter extends FilterBase {\t\n\tstatic $default_format = '{{ geometry.width }}x{{ geometry.height }} {{"
  },
  {
    "path": "classes/filters/linkFilter.php",
    "chars": 525,
    "preview": "<?php\nclass linkFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row)"
  },
  {
    "path": "classes/filters/numberFilter.php",
    "chars": 703,
    "preview": "<?php\n/**\n * Created by JetBrains PhpStorm.\n * User: lrzanek\n * Date: 30.08.13\n * Time: 15:37\n * To change this template"
  },
  {
    "path": "classes/filters/paddingFilter.php",
    "chars": 301,
    "preview": "<?php\nclass paddingFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$r"
  },
  {
    "path": "classes/filters/preFilter.php",
    "chars": 213,
    "preview": "<?php\nclass preFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row) "
  },
  {
    "path": "classes/filters/twigFilter.php",
    "chars": 468,
    "preview": "<?php\nclass twigFilter extends FilterBase {\t\n\tpublic static function filter($value, $options = array(), &$report, &$row)"
  },
  {
    "path": "classes/headers/ChartHeader.php",
    "chars": 10867,
    "preview": "<?php\nclass ChartHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'columns'=>array(\n\t\t\t'type'=>'array',\n\t\t\t'de"
  },
  {
    "path": "classes/headers/ColumnsHeader.php",
    "chars": 2085,
    "preview": "<?php\nclass ColumnsHeader extends HeaderBase {\n\tpublic static function init($params, &$report) {\n\t\tforeach($params['colu"
  },
  {
    "path": "classes/headers/FilterHeader.php",
    "chars": 1052,
    "preview": "<?php\nclass FilterHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'column'=>array(\n\t\t\t'required'=>true,\n\t\t\t't"
  },
  {
    "path": "classes/headers/FormattingHeader.php",
    "chars": 4434,
    "preview": "<?php\nclass FormattingHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'limit'=>array(\n\t\t\t'type'=>'number',\n\t\t"
  },
  {
    "path": "classes/headers/IncludeHeader.php",
    "chars": 1224,
    "preview": "<?php\nclass IncludeHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'report'=>array(\n\t\t\t'required'=>true,\n\t\t\t'"
  },
  {
    "path": "classes/headers/InfoHeader.php",
    "chars": 952,
    "preview": "<?php\nclass InfoHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'name'=>array(\n\t\t\t'type'=>'string'\n\t\t),\n\t\t'de"
  },
  {
    "path": "classes/headers/OptionsHeader.php",
    "chars": 3034,
    "preview": "<?php\nclass OptionsHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'limit'=>array(\n\t\t\t'type'=>'number',\n\t\t\t'd"
  },
  {
    "path": "classes/headers/RollupHeader.php",
    "chars": 4784,
    "preview": "<?php\nclass RollupHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'columns'=>array(\n\t\t\t'required'=>true,\n\t\t\t'"
  },
  {
    "path": "classes/headers/VariableHeader.php",
    "chars": 6123,
    "preview": "<?php\nclass VariableHeader extends HeaderBase {\n\t\n\tstatic $validation = array(\n\t\t'name'=>array(\n\t\t\t'required'=>true,\n\t\t\t"
  },
  {
    "path": "classes/headers/deprecated/CacheHeader.php",
    "chars": 525,
    "preview": "<?php\nclass CacheHeader extends OptionsHeader {\t\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"CACH"
  },
  {
    "path": "classes/headers/deprecated/CautionHeader.php",
    "chars": 462,
    "preview": "<?php\nclass CautionHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'value'=>array(\n\t\t\t'required'=>true,\n\t\t\t't"
  },
  {
    "path": "classes/headers/deprecated/ColumnHeader.php",
    "chars": 248,
    "preview": "<?php\nclass ColumnHeader extends ColumnsHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"COLU"
  },
  {
    "path": "classes/headers/deprecated/CreatedHeader.php",
    "chars": 368,
    "preview": "<?php\nclass CreatedHeader extends InfoHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"CREATE"
  },
  {
    "path": "classes/headers/deprecated/DatabaseHeader.php",
    "chars": 406,
    "preview": "<?php\nclass DatabaseHeader extends OptionsHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"DA"
  },
  {
    "path": "classes/headers/deprecated/DescriptionHeader.php",
    "chars": 384,
    "preview": "<?php\nclass DescriptionHeader extends InfoHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"DE"
  },
  {
    "path": "classes/headers/deprecated/DetailHeader.php",
    "chars": 1471,
    "preview": "<?php\nclass DetailHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'report'=>array(\n\t\t\t'required'=>true,\n\t\t\t't"
  },
  {
    "path": "classes/headers/deprecated/MongodatabaseHeader.php",
    "chars": 420,
    "preview": "<?php\nclass MongodatabaseHeader extends OptionsHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_erro"
  },
  {
    "path": "classes/headers/deprecated/NameHeader.php",
    "chars": 358,
    "preview": "<?php\nclass NameHeader extends InfoHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"NAME head"
  },
  {
    "path": "classes/headers/deprecated/NoteHeader.php",
    "chars": 356,
    "preview": "<?php\nclass NoteHeader extends InfoHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"NOTE head"
  },
  {
    "path": "classes/headers/deprecated/OptionHeader.php",
    "chars": 248,
    "preview": "<?php\nclass OptionHeader extends OptionsHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"OPTI"
  },
  {
    "path": "classes/headers/deprecated/PlotHeader.php",
    "chars": 320,
    "preview": "<?php\n//This is for backwards compatibility\n//The Chart header used to be called Plot\nclass PlotHeader extends ChartHead"
  },
  {
    "path": "classes/headers/deprecated/StatusHeader.php",
    "chars": 365,
    "preview": "<?php\nclass StatusHeader extends InfoHeader {\t\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"STATUS"
  },
  {
    "path": "classes/headers/deprecated/TotalHeader.php",
    "chars": 200,
    "preview": "<?php\nclass TotalHeader extends TotalsHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"TOTAL "
  },
  {
    "path": "classes/headers/deprecated/TotalsHeader.php",
    "chars": 391,
    "preview": "<?php\nclass TotalsHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'value'=>array(\n\t\t\t'required'=>true,\n\t\t\t'ty"
  },
  {
    "path": "classes/headers/deprecated/TypeHeader.php",
    "chars": 356,
    "preview": "<?php\nclass TypeHeader extends InfoHeader {\n\tpublic static function init($params, &$report) {\n\t\ttrigger_error(\"TYPE head"
  },
  {
    "path": "classes/headers/deprecated/ValueHeader.php",
    "chars": 1062,
    "preview": "<?php\nclass ValueHeader extends HeaderBase {\n\tstatic $validation = array(\n\t\t'name'=>array(\n\t\t\t'required'=>true,\n\t\t\t'type"
  },
  {
    "path": "classes/report_formats/ChartReportFormat.php",
    "chars": 318,
    "preview": "<?php\nclass ChartReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\tif(!$r"
  },
  {
    "path": "classes/report_formats/CsvReportFormat.php",
    "chars": 759,
    "preview": "<?php\nclass CsvReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\t//always"
  },
  {
    "path": "classes/report_formats/DebugReportFormat.php",
    "chars": 864,
    "preview": "<?php\nclass DebugReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\theader"
  },
  {
    "path": "classes/report_formats/HtmlReportFormat.php",
    "chars": 1089,
    "preview": "<?php\nclass HtmlReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\t\n\t\t//de"
  },
  {
    "path": "classes/report_formats/JsonReportFormat.php",
    "chars": 1572,
    "preview": "<?php\nclass JsonReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\t\t\n\t\theade"
  },
  {
    "path": "classes/report_formats/RawReportFormat.php",
    "chars": 425,
    "preview": "<?php\nclass RawReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\t\t\n\t\theader"
  },
  {
    "path": "classes/report_formats/SqlReportFormat.php",
    "chars": 258,
    "preview": "<?php\nclass SqlReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\theader(\""
  },
  {
    "path": "classes/report_formats/TableReportFormat.php",
    "chars": 306,
    "preview": "<?php\nclass TableReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\t\n\t\t$re"
  },
  {
    "path": "classes/report_formats/TextReportFormat.php",
    "chars": 2138,
    "preview": "<?php\nclass TextReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\theader("
  },
  {
    "path": "classes/report_formats/XlsReportBase.php",
    "chars": 2094,
    "preview": "<?php\r\nabstract class XlsReportBase extends ReportFormatBase {\r\n\tprivate static function columnLetter($c){\r\n\t\t$c = intva"
  },
  {
    "path": "classes/report_formats/XlsReportFormat.php",
    "chars": 763,
    "preview": "<?php\nclass XlsReportFormat extends XlsReportBase {\n\tpublic static function display(&$report, &$request) {\n\t\t// First le"
  },
  {
    "path": "classes/report_formats/XlsxReportFormat.php",
    "chars": 809,
    "preview": "<?php\nclass XlsxReportFormat extends XlsReportBase {\n\tpublic static function display(&$report, &$request) {\n\t\t// First l"
  },
  {
    "path": "classes/report_formats/XmlReportFormat.php",
    "chars": 989,
    "preview": "<?php\nclass XmlReportFormat extends ReportFormatBase {\n\tpublic static function display(&$report, &$request) {\n\t\theader(\""
  },
  {
    "path": "classes/report_types/AdoPivotReportType.php",
    "chars": 6045,
    "preview": "<?php\nclass AdoPivotReportType extends ReportTypeBase {\n\tpublic static function init(&$report) {\n\t\t$environments = PhpRe"
  },
  {
    "path": "classes/report_types/AdoReportType.php",
    "chars": 5461,
    "preview": "<?php\nclass AdoReportType extends ReportTypeBase {\n\tpublic static function init(&$report) {\n\t\t$environments = PhpReports"
  },
  {
    "path": "classes/report_types/MongoReportType.php",
    "chars": 4093,
    "preview": "<?php\nclass MongoReportType extends ReportTypeBase {\n\tpublic static function init(&$report) {\n\t\t$environments = PhpRepor"
  },
  {
    "path": "classes/report_types/MysqlReportType.php",
    "chars": 96,
    "preview": "<?php\nclass MysqlReportType extends PdoReportType {\n\tpublic static $default_driver = 'mysql';\n}\n"
  },
  {
    "path": "classes/report_types/PdoReportType.php",
    "chars": 6733,
    "preview": "<?php\nclass PdoReportType extends ReportTypeBase {\n\tpublic static $default_driver = null;\n\n\tpublic static function init("
  },
  {
    "path": "classes/report_types/PhpReportType.php",
    "chars": 2767,
    "preview": "<?php\nclass PhpReportType extends ReportTypeBase {\n\tpublic static function init(&$report) {\n\t\t$report->raw_query = \"<?ph"
  },
  {
    "path": "composer.json",
    "chars": 1132,
    "preview": "{\n    \"name\": \"jdorn/php-reports\",\n    \"description\": \"A PHP framework for displaying reports from any data source, incl"
  },
  {
    "path": "config/config.php.sample",
    "chars": 3498,
    "preview": "<?php\nreturn array(\n\t//the root directory of all your reports\n\t//reports can be organized in subdirectories\n\t'reportDir'"
  },
  {
    "path": "index.php",
    "chars": 3312,
    "preview": "<?php\n// for build-in php server serve the requested resource as-is.\nif (php_sapi_name() == 'cli-server' && preg_match('"
  },
  {
    "path": "lib/PhpReports/FilterBase.php",
    "chars": 450,
    "preview": "<?php\nabstract class FilterBase {\t\n\t/**\n\t * Filter a datapoint in the report\n\t * @param String $value - The current valu"
  },
  {
    "path": "lib/PhpReports/HeaderBase.php",
    "chars": 3265,
    "preview": "<?php\nclass HeaderBase {\n\tstatic $validation = array();\n\t\n\tpublic static function parse($key, $value, &$report) {\t\t\t\t\n\t\t"
  },
  {
    "path": "lib/PhpReports/PhpReports.php",
    "chars": 20663,
    "preview": "<?php\nclass PhpReports {\n\tpublic static $config;\n\tpublic static $request;\n\tpublic static $twig;\n\tpublic static $twig_str"
  },
  {
    "path": "lib/PhpReports/Report.php",
    "chars": 18447,
    "preview": "<?php\nclass Report {\n\tpublic $report;\n\tpublic $macros = array();\n\tpublic $exported_headers = array();\n\tpublic $options ="
  },
  {
    "path": "lib/PhpReports/ReportFormatBase.php",
    "chars": 354,
    "preview": "<?php\nabstract class ReportFormatBase {\n\tabstract public static function display(&$report, &$request);\n\t\n\tpublic static "
  },
  {
    "path": "lib/PhpReports/ReportTypeBase.php",
    "chars": 343,
    "preview": "<?php\nabstract class ReportTypeBase {\n\tpublic static function init(&$report) {\n\t\t\n\t}\n\t\n\tpublic static function openConne"
  },
  {
    "path": "lib/PhpReports/ReportValue.php",
    "chars": 2220,
    "preview": "<?php\nclass ReportValue {\n\tpublic $key;\n\tpublic $i;\n\t\n\tpublic $original_value;\n\tpublic $filtered_value;\n\tpublic $html_va"
  },
  {
    "path": "lib/adodb/pivottable.inc.php",
    "chars": 3607,
    "preview": "<?php\n/** \n * @version V4.93 10 Oct 2006 (c) 2000-2012 John Lim (jlim#natsoft.com). All rights reserved.\n * Released und"
  },
  {
    "path": "lib/simplediff/SimpleDiff.php",
    "chars": 3196,
    "preview": "<?php\n\n/*\n\tPaul's Simple Diff Algorithm v 0.1\n\t(C) Paul Butler 2007 <http://www.paulbutler.org/>\n\tMay be used and distri"
  },
  {
    "path": "public/css/bootstrap-multiselect.css",
    "chars": 484,
    "preview": ".multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container input[type=\"text"
  },
  {
    "path": "public/css/datepicker.css",
    "chars": 4340,
    "preview": " /*\n\tDatepicker for Bootstrap\n\tCopyright 2012 Stefan Petre\n\tLicensed under the Apache License v2.0\n\thttp://www.apache.or"
  },
  {
    "path": "public/css/daterangepicker-bs3.css",
    "chars": 5348,
    "preview": "/*!\n * Stylesheet for the Date Range Picker, for use with Bootstrap 3.x\n *\n * Copyright 2013 Dan Grossman ( http://www.d"
  },
  {
    "path": "public/css/jquery.dataTables.css",
    "chars": 17263,
    "preview": "/*\n * Table styles\n */\ntable.dataTable {\n  width: auto;\n  clear: both;\n  border-collapse: separate;\n  border-spacing: 0;"
  },
  {
    "path": "public/css/prettify.css",
    "chars": 817,
    "preview": ".com { color: #93a1a1; }\n.lit { color: #195f91; }\n.pun, .opn, .clo { color: #93a1a1; }\n.fun { color: #dc322f; }\n.str, .a"
  },
  {
    "path": "public/css/report.css",
    "chars": 1906,
    "preview": "body {\n\tpadding-bottom: 200px;\n}\n.database_message {\n\tcolor: #999;\n}\n.result_table td .bar{\n\theight: 18px; \n\tdisplay:inl"
  },
  {
    "path": "public/css/report_list.css",
    "chars": 1810,
    "preview": "#report_list a[name] {\n\tpadding: 0;\n\tmargin: 0;\n\theight: 5px;\n    visibility: hidden;\n}\n#report_list h2.no_title {\n\tfont"
  },
  {
    "path": "public/css/timeline.css",
    "chars": 4840,
    "preview": "body div.chart_color_10 {\r\n\tbackground-color: red;\r\n\tcolor: #1A1A1A;\r\n\tborder-color: darkred;\r\n}\r\nbody div.chart_color_2"
  },
  {
    "path": "public/css/typeahead.js-bootstrap.css",
    "chars": 2158,
    "preview": ".navbar-form[role=\"search\"] > .form-group {\n\twidth: 240px;\n}\n\n.twitter-typeahead {\n    width: 100%;\n    position: relati"
  },
  {
    "path": "public/js/ace/ace.js",
    "chars": 280658,
    "preview": "(function(){function o(e){var i=function(e,t){return r(\"\",e,t)},s=t;e&&(t[e]||(t[e]={}),s=t[e]);if(!s.define||!s.define."
  },
  {
    "path": "public/js/ace/ext-searchbox.js",
    "chars": 7086,
    "preview": "define(\"ace/ext/searchbox\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\",\"ace/lib/lang\",\"ace/lib/event\",\"ace/keyboard/hash"
  },
  {
    "path": "public/js/ace/ext-spellcheck.js",
    "chars": 746,
    "preview": "define(\"ace/ext/spellcheck\",[\"require\",\"exports\",\"module\"],function(e,t,n){text.spellcheck=!0,host.on(\"nativecontextmenu"
  },
  {
    "path": "public/js/ace/ext-static_highlight.js",
    "chars": 1231,
    "preview": "define(\"ace/ext/static_highlight\",[\"require\",\"exports\",\"module\",\"ace/edit_session\",\"ace/layer/text\"],function(e,t,n){var"
  },
  {
    "path": "public/js/ace/ext-textarea.js",
    "chars": 9116,
    "preview": "define(\"ace/ext/textarea\",[\"require\",\"exports\",\"module\",\"ace/lib/event\",\"ace/lib/useragent\",\"ace/lib/net\",\"ace/ace\",\"ace"
  },
  {
    "path": "public/js/ace/mode-diff.js",
    "chars": 2354,
    "preview": "define(\"ace/mode/diff\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/diff_highli"
  },
  {
    "path": "public/js/ace/mode-html.js",
    "chars": 40600,
    "preview": "define(\"ace/mode/html\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/mode/javascript\",\"ace/mode/css\","
  },
  {
    "path": "public/js/ace/mode-javascript.js",
    "chars": 19711,
    "preview": "define(\"ace/mode/javascript\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/javas"
  },
  {
    "path": "public/js/ace/mode-json.js",
    "chars": 9125,
    "preview": "define(\"ace/mode/json\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/json_highli"
  },
  {
    "path": "public/js/ace/mode-php.js",
    "chars": 141776,
    "preview": "define(\"ace/mode/php\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/php_highligh"
  },
  {
    "path": "public/js/ace/mode-sql.js",
    "chars": 1728,
    "preview": "define(\"ace/mode/sql\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/sql_highligh"
  },
  {
    "path": "public/js/ace/mode-text.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "public/js/ace/mode-xml.js",
    "chars": 12447,
    "preview": "define(\"ace/mode/xml\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/xml_highligh"
  },
  {
    "path": "public/js/ace/mode-yaml.js",
    "chars": 4007,
    "preview": "define(\"ace/mode/yaml\",[\"require\",\"exports\",\"module\",\"ace/lib/oop\",\"ace/mode/text\",\"ace/tokenizer\",\"ace/mode/yaml_highli"
  },
  {
    "path": "public/js/ace/theme-eclipse.js",
    "chars": 2048,
    "preview": "define(\"ace/theme/eclipse\",[\"require\",\"exports\",\"module\",\"ace/lib/dom\"],function(e,t,n){t.isDark=!1,t.cssText='.ace-ecli"
  },
  {
    "path": "public/js/ace/worker-javascript.js",
    "chars": 80683,
    "preview": "\"no use strict\";function initBaseUrls(e){require.tlns=e}function initSender(){var e=require(null,\"ace/lib/event_emitter\""
  },
  {
    "path": "public/js/ace/worker-json.js",
    "chars": 29924,
    "preview": "\"no use strict\";function initBaseUrls(e){require.tlns=e}function initSender(){var e=require(null,\"ace/lib/event_emitter\""
  },
  {
    "path": "public/js/ace/worker-php.js",
    "chars": 131363,
    "preview": "\"no use strict\";function initBaseUrls(e){require.tlns=e}function initSender(){var e=require(null,\"ace/lib/event_emitter\""
  },
  {
    "path": "public/js/bootstrap-datepicker.js",
    "chars": 13178,
    "preview": "/* =========================================================\n * bootstrap-datepicker.js \n * http://www.eyecon.ro/bootstr"
  },
  {
    "path": "public/js/bootstrap-multiselect.js",
    "chars": 22519,
    "preview": "/**\n * bootstrap-multiselect.js 1.0.0\n * https://github.com/davidstutz/bootstrap-multiselect\n *\n * Copyright 2012, 2013 "
  },
  {
    "path": "public/js/daterangepicker-1.3.2.js",
    "chars": 38728,
    "preview": "/**\n * @version: 1.3.2\n * @author: Dan Grossman http://www.dangrossman.info/\n * @date: 2014-01-22\n * @copyright: Copyrig"
  },
  {
    "path": "public/js/html5shiv.js",
    "chars": 2429,
    "preview": "/*\n HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed\n*/\n(function(l,f){function m(){var a=e.elem"
  },
  {
    "path": "public/js/jquery.browser.js",
    "chars": 984,
    "preview": "(function( jQuery ) {\n\tvar matched,\n\t\tuserAgent = navigator.userAgent || \"\";\n\n\t// Use of jQuery.browser is frowned upon."
  },
  {
    "path": "public/js/jquery.cookie.js",
    "chars": 2740,
    "preview": "/*!\n * jQuery Cookie Plugin v1.3.1\n * https://github.com/carhartl/jquery-cookie\n *\n * Copyright 2013 Klaus Hartl\n * Rele"
  },
  {
    "path": "public/js/jquery.iframe-auto-height.plugin.1.9.3.js",
    "chars": 7613,
    "preview": "/*jslint white: true, indent: 2, onevar: false, browser: true, undef: true, nomen: false, eqeqeq: true, plusplus: false,"
  },
  {
    "path": "public/js/jquery.stickytableheaders.js",
    "chars": 8673,
    "preview": "/*! Copyright (c) 2011 by Jonas Mosbech - https://github.com/jmosbech/StickyTableHeaders\n\tMIT license info: https://gith"
  },
  {
    "path": "public/js/lang-sql.js",
    "chars": 3486,
    "preview": "// Copyright (C) 2008 Google Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not u"
  },
  {
    "path": "public/js/prettify.js",
    "chars": 13632,
    "preview": "var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;\n(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92"
  },
  {
    "path": "public/js/scripts.js",
    "chars": 1479,
    "preview": "$.queryStringToJSON = function(string) {\n  var qArr = string.split('&');\n  var stack = [];\n  for (var i in qArr) {\n    v"
  },
  {
    "path": "public/js/timeline.js",
    "chars": 215244,
    "preview": "/**\n * @file timeline.js\n *\n * @brief\n * The Timeline is an interactive visualization chart to visualize events in\n * ti"
  },
  {
    "path": "sample_dashboards/timezones.json",
    "chars": 610,
    "preview": "{\n  \"title\": \"Timezone Dashboard\",\n  \"description\": \"Shows off the dashboard feature of Php Reports\",\n  \"reports\": [\n   "
  },
  {
    "path": "sample_reports/ado/README.txt",
    "chars": 196,
    "preview": "Reports written in SQL. \nAdoDB library is responsible for making and maintaining the DB connectivity. Some additional ex"
  },
  {
    "path": "sample_reports/ado/TITLE.txt",
    "chars": 20,
    "preview": "AdoDB based Reports\n"
  },
  {
    "path": "sample_reports/ado/names.ado",
    "chars": 627,
    "preview": "-- Registration times\n--\n-- VARIABLE: { \n--      name: \"range\", \n--      display: \"Report Range\",\n--      type: \"dateran"
  },
  {
    "path": "sample_reports/ado/names.pivot",
    "chars": 541,
    "preview": "-- Registration types per day\n--\n-- INFO: {\n--      created: \"2013-08-14\"\n-- }\n-- OPTIONS: {\n--      database: \"ado\"\n-- "
  },
  {
    "path": "sample_reports/mongodb/README.txt",
    "chars": 109,
    "preview": "Reports run in the MongoDB Shell.  mongo-client is required for these reports, but the PHP extension is not.\n"
  },
  {
    "path": "sample_reports/mongodb/TITLE.txt",
    "chars": 16,
    "preview": "MongoDB Reports\n"
  },
  {
    "path": "sample_reports/mongodb/log-events.js",
    "chars": 317,
    "preview": "// Last 50 Log Events\n// OPTIONS: { mongodatabase: \"Logs\" }\n\nvar result = db.logs.find({}).limit(50).sort({date: -1});\n\n"
  },
  {
    "path": "sample_reports/mysql/README.txt",
    "chars": 90,
    "preview": "Reports written in SQL.  The mysql or mysqli PHP extension is required for these reports.\n"
  },
  {
    "path": "sample_reports/mysql/TITLE.txt",
    "chars": 14,
    "preview": "MySQL Reports\n"
  },
  {
    "path": "sample_reports/mysql/all-orders.sql",
    "chars": 874,
    "preview": "-- All Orders\n-- This report lets you view all orders within a specified time period.\n-- You can click on a customer nam"
  },
  {
    "path": "sample_reports/mysql/drilldown/customer-orders.sql",
    "chars": 336,
    "preview": "-- All Orders For a Customer\n-- This is a drilldown report that lists all orders for a given customer\n-- VARIABLE: { nam"
  },
  {
    "path": "sample_reports/php/README.txt",
    "chars": 176,
    "preview": "Reports written in PHP.  \nThis is useful for merging data between different sources, \npulling in data from APIs, \nand do"
  },
  {
    "path": "sample_reports/php/TITLE.txt",
    "chars": 12,
    "preview": "PHP Reports\n"
  },
  {
    "path": "sample_reports/php/functions.php",
    "chars": 1093,
    "preview": "<?php\n// Helper PHP Functions\n// This report is never run on it's own.\n// It is only ever used when it's included from w"
  },
  {
    "path": "sample_reports/php/timezones.php",
    "chars": 1097,
    "preview": "<?php\n// Get Timezones Contained In a Region\n// INCLUDE: {report: \"functions.php\"}\n// VARIABLE: {\n//      name: \"region\""
  },
  {
    "path": "sample_reports/php/timezones_multiple.php",
    "chars": 901,
    "preview": "<?php\n// Get All Timezones By Region\n// VARIABLE: {\n//      name: \"regions\",\n//      multiple: true,\n//      display: \"R"
  },
  {
    "path": "templates/default/csv/report.twig",
    "chars": 294,
    "preview": "{% for value in DataSets[dataset].rows[0].values %}{% if not loop.first %},{% endif %}\"{{value.key}}\"{% endfor %}\n\n{% fo"
  },
  {
    "path": "templates/default/html/blank_page.twig",
    "chars": 914,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>{% block title %}{{ title }}{% endblock "
  },
  {
    "path": "templates/default/html/chart_page.twig",
    "chars": 755,
    "preview": "{% extends \"html/blank_page.twig\" %}\n{% block javascripts %}\n\t<script type=\"text/javascript\" src=\"https://www.gstatic.co"
  },
  {
    "path": "templates/default/html/chart_report.twig",
    "chars": 3713,
    "preview": "{% extends \"html/chart_page.twig\" %}\n\n{% block content %}\n\t{% for chart in Charts %}\n\t<div id='chart_div_{{chart.num}}' "
  },
  {
    "path": "templates/default/html/content_only.twig",
    "chars": 324,
    "preview": "{% block notice_area %}\n\t{% if error %}\n\t\t<div class='alert alert-danger'>\n\t\t\t{{error}}\n\t\t</div>\n\t{% endif %}\n\n\t{% if no"
  },
  {
    "path": "templates/default/html/dashboard.twig",
    "chars": 5296,
    "preview": "{% extends \"html/page.twig\" %}\n\n{% block title %}{{dashboard.title | default('Dashboard')}}{% endblock %}\n\n{% block head"
  },
  {
    "path": "templates/default/html/dashboard_list.twig",
    "chars": 382,
    "preview": "{% extends \"html/page.twig\" %}\n\n{% block title %}Dashboard List{% endblock %}\n\n{% set breadcrumb = {\"Dashboard List\": tr"
  },
  {
    "path": "templates/default/html/page.twig",
    "chars": 10587,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>{% block title %}{{ title }}{% endblock "
  },
  {
    "path": "templates/default/html/report.twig",
    "chars": 11225,
    "preview": "{% extends \"html/page.twig\" %}\n\n{% set collapse_configuration = is_ready %}\n\n{% block title %}{{ Name }}{% endblock %}\n\n"
  },
  {
    "path": "templates/default/html/report_ajax_loading.twig",
    "chars": 809,
    "preview": "<div class='alert alert-block alert-info' id='async_notice' style='text-align: center;'>\n\t<div>\n\t\tYour report is running"
  },
  {
    "path": "templates/default/html/report_content.twig",
    "chars": 2879,
    "preview": "{% block chart_area %}\n    {% if has_charts and not no_charts %}\n\t\t\t\t<iframe src=\"{{base}}/report/chart/?{{report_querys"
  },
  {
    "path": "templates/default/html/report_editor.twig",
    "chars": 3317,
    "preview": "{% extends \"html/page.twig\" %}\n\n{% if options.Name %}\n\t{% set Name = options.Name %}\n{% else %}\n\t{% set Name = report %}"
  },
  {
    "path": "templates/default/html/report_list.twig",
    "chars": 2506,
    "preview": "{% extends \"html/page.twig\" %}\n\n{% block title %}Report List{% endblock %}\n\n{% set breadcrumb = {\"Report List\": true} %}"
  },
  {
    "path": "templates/default/html/report_list_item.twig",
    "chars": 2385,
    "preview": "{% if item.is_dir %}\n\t{% if h is not defined %}\n\t\t{% set h = 2 %}\n\t{% endif %}\n\t{% if item.children %}\n\t\t<a name=\"report"
  },
  {
    "path": "templates/default/html/report_list_table_of_contents_item.twig",
    "chars": 308,
    "preview": "{% if item.is_dir and item.Title %}\n\t<li>\n\t\t<a href='#report_{{item.Id}}'>{{item.Title}}</a>\n\t\t{% if item.children %}\n\t\t"
  },
  {
    "path": "templates/default/html/table.twig",
    "chars": 3681,
    "preview": "{% for dataset in DataSets %}\n    {% if dataset.title %}<h3>{{dataset.title}}</h3>{% endif %}\n\t{% if DataSets|length > 1"
  },
  {
    "path": "templates/default/html/variable_form.twig",
    "chars": 9068,
    "preview": "<form class=\"form-horizontal well well-sm\" role=\"form\" method=\"get\" id=\"variable_form\">\n\t<input type='hidden' name='repo"
  },
  {
    "path": "templates/default/sql/report.twig",
    "chars": 449,
    "preview": "{% for dataset in DataSets %}{% if not loop.first %}\n\n{% endif %}INSERT INTO `{{Name}}_{{loop.index}}`\n\t({% for value in"
  },
  {
    "path": "templates/default/xml/page.twig",
    "chars": 75,
    "preview": "<?xml version=\"1.0\"?>\n\n{% block content %}\n\t{{content|raw}}\n{% endblock %}\n"
  },
  {
    "path": "templates/default/xml/report.twig",
    "chars": 730,
    "preview": "{% extends \"xml/page.twig\" %}\n\n{% block content %}\n{% if dataset_format %}\n<Results>\n{% for dataset in datasets %}\n\t<Dat"
  }
]

About this extraction

This page contains the full source code of the jdorn/php-reports GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 150 files (1.3 MB), approximately 408.9k tokens, and a symbol index with 423 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!