<?php

/*
	Googlyzer
	Contact: Bill Edgar (bill.edgar@oaktondata.com)
	http://www.oaktondata.com/googlyzer
	
	Copyright (c) 2011-2012, Oakton Data LLC
	All rights reserved.

	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
	    
	    * Redistributions of source code must retain the above copyright
	      notice, this list of conditions and the following disclaimer.
	    * Redistributions in binary form must reproduce the above copyright
	      notice, this list of conditions and the following disclaimer in the
	      documentation and/or other materials provided with the distribution.
	    * Neither the name of the Oakton Data LLC nor the
	      names of its contributors may be used to endorse or promote products
	      derived from this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
	ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
	WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL Oakton Data LLC BE LIABLE FOR ANY
	DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
	ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	
	This is the googlyzer_options PHP class file.
*/

if (!class_exists('googlyzer_options')) {
	
	// googlyzer_options class
	class googlyzer_options {
		/* 
		 * public variables
		 */
		public $CHARTS = 				array();
		public $DATE_RANGE_SELECTIONS = array( 
			'Custom Range', 
			'05 days', 
			'10 days', 
			'20 days', 
			'30 days', 
			'60 days', 
			'90 days' 
			);
		public $DIMENSION_SELECTIONS = 	array( 
			'ga:city' =>				'City',
			'ga:continent' =>			'Continent',
			'ga:country'=>				'Country',
			'ga:date' => 				'Day',
			'ga:hostname' =>			'Hostname',
			'ga:keyword' =>				'Keyword(s)',
			'ga:landingPagePath' =>		'Landing Page',
			'ga:pagePath' =>			'Page Path',
			'ga:pageTitle' =>			'Page Title',
			'ga:region' =>				'Region',
			'ga:subContinent' =>		'Subcontinent'
			);
		public $GA_REQUEST_DIMENSION_FILTER_OPS = array(
			'regex_match' =>					'Contains Regex Match (=~)',
			'contains_substring' =>				'Contains Substring (=@)',
			'does_not_contain_substring' =>		'Does not Contain Substring (!@)',
			'no_match' =>						'Does not Match (!=)',
			'regex_no_match' =>					'Does not Match Regex (!~)',
			'exact_match' =>					'Exact Match (==)',
			'regex_match_start' =>				'Regex Match at Beginning (=~^)'
			);
		public $GA_REQUEST_METRIC_FILTER_OPS = array(
			'equals' =>							'Equals (==)',
			'does_not_equal' =>					'Does not Equal (!=)',
			'greater_than' =>					'Greater Than (>)',
			'greater_than_or_equal_to' =>		'Greater Than or Equal to (>=)',
			'less_than' =>						'Less Than (<)',
			'less_than_or_equal_to' =>			'Less Than or Equal to (<=)'
			);
		public $GA_TRACKING_PLACEMENT = array( 'footer', 'header' );
		public $METRIC_SELECTIONS = 	array( 
			'ga:avgPageLoadTime' =>		'Average Page Load Time',
			'ga:avgSearchDepth' =>		'Average Search Depth',
			'ga:avgTimeOnSite' => 		'Average Time on Site',
			'ga:bounces' => 			'Bounces',
			'ga:entrances' =>			'Entrances',
			'ga:entranceBounceRate' =>	'Entrance Bounce Rate',
			'ga:newVisits' => 			'New Visits',
			'ga:organicSearches' =>		'Organic Searches',
			'ga:pageviews' => 			'Pageviews', 
			'ga:pageviewsPerVisit' => 	'Pageviews Per Visit',
			'ga:percentNewVisits' => 	'Percent New Visits', 
			'ga:searchDepth' =>			'Search Depth',
			'ga:searchResultViews' =>	'Search Result Views',
			'ga:searchVisits' =>		'Search Visits',
			'ga:visits' => 				'Visits',
			'ga:visitors' => 			'Visitors',  
			'ga:visitBounceRate' => 	'Visit Bounce Rate',
			'none' =>					'none'
			);
		public $OPTIONS = 				array(
			'googlyzer-composites' => 	array(
				'options' => 			array(),
				'defaults' => 			array(),
				'page' => 				'composites'
				),
			'googlyzer-ga_tracking' =>	array(
				'options' =>			array(),
				'defaults' =>			array(),
				'page' =>				'ga_tracking'
				),
			'googlyzer-linecharts' => 	array(
				'options' => 			array(),
				'defaults' => 			array(),
				'page' => 				'linecharts'
				),
			'googlyzer-options' => 		array(
				'options' => 			array(),
				'defaults' => 			array(),
				'page' => 				'general'
				),
			'googlyzer-piecharts' => 	array(
				'options' =>			array(),
				'defaults' => 			array(),
				'page' => 				'piecharts'
				),
			'googlyzer-tables' => 		array(
				'options' => 			array(),
				'defaults' => 			array(),
				'page' => 				'tables'
				)
			);
		public $PAGE_HOOKS = 			array();
		public $PIECHART_SELECTIONS = 	array (
			'browser' =>				'Visits by Browser',
			'referral_source' =>		'Referrals by Source',
			'visitor_types' =>			'New & Returning Visitors'
			);
		public $PIECHART_SLICE_SELECTIONS = array( 1, 2, 3, 4, 5 );
		public $SCRIPTS = 				array();
		public $SETTINGS = 				array();
		public $SORT_SELECTIONS = 		array( 'ascending', 'descending' );
		public $STYLES = 				array();
		public $TABLE_SELECTIONS = 		null;
		public $TABS = 					array( 
			'general' =>				'General',
			'ga_tracking' =>			'GA Tracking Code',
			'composites' =>				'Composite Chart',
			'linecharts' =>				'Line Charts', 
			'piecharts' =>				'Pie Charts', 
			'tables' =>					'Tables',
			'about' =>					'About'
			);
		
		/*
		 * private variables
		 */
		private $parentInstance = 		null;
		private $accountList = 			array();		
		
		// public constructor
		public function googlyzer_options( $parentInstance ) {
			$this->__construct( $parentInstance );
		}
		
		// public constructor
		public function __construct( $parentInstance ) {
			// record parent object reference
			$this->parentInstance = $parentInstance;
			// set up array of selections for table rows
			$this->TABLE_SELECTIONS = range( 1,15 );
			// initialize options for composite chart
			$this->OPTIONS['googlyzer-composites']['options'] = array(
				'composite_general' => array(
					'width' => 			null,
					'height' => 		null,
					'max_results' => 	null
					),
				'composite1' => array(
					'dimensions'=>		null,
					'enable'=>			null,
					'line_color_1'=>	null,
					'line_color_2'=>	null,
					'line_color_3'=>	null,
					'line_color_4'=>	null,
					'line_color_5'=>	null,
					'metric_1'=>		null,
					'metric_2'=>		null,
					'metric_3'=>		null,
					'metric_4'=>		null,
					'metric_5'=>		null,
					'metadata_enable'=>	null,
					'sort_order' => 	null,
					'sort_column' => 	null
					)
				);
			// load defaults for composite chart
			$this->OPTIONS['googlyzer-composites']['defaults'] = array(
				'composite_general' => array(
					'width' => 			'485px',
					'height' => 		'100px',
					'max_results' => 	10000
					),
				'composite1' => array(
					'dimensions'=>		'ga:date',
					'enable'=>			'enabled',
					'line_color_1'=>	'#FF0000', // red
					'line_color_2'=>	'#009900', // green
					'line_color_3'=>	'#0000FF', // blue
					'line_color_4'=>	'#FF6600', // orange
					'line_color_5'=>	'#FF00FF', // fuscia
					'metric_1'=>		'ga:pageviews',
					'metric_2'=>		'ga:visitors', 
					'metric_3'=>		'ga:newVisits',
					'metric_4'=>		'none',
					'metric_5'=>		'none',
					'metadata_enable'=>	'enabled',
					'sort_order' => 	'ascending',
					'sort_column' => 	'ga:date'
					)
				);
			// initialize ga_tracking options
			$this->OPTIONS['googlyzer-ga_tracking']['options'] = array(
				'ga_tracking_general' => 	array(
					'enable'=>				null,
					'placement'=>			null,
					'track_admin'=>			null,
					'ga_tableid'=>			null,
					'ga_profileid' =>		null,
					'ga_webpropertyid' =>	null,
					'ga_accountname' =>		null,
					'ga_accountid' =>		null,
					'ga_beginning' =>		null
					)
				);
			// load ga_tracking options defaults
			$this->OPTIONS['googlyzer-ga_tracking']['defaults'] = array(
				'ga_tracking_general' => 	array(
					'enable'=>				'disabled',
					'placement'=>			'header',
					'track_admin'=>			'disabled',
					'ga_tableid'=>			'',
					'ga_profileid' =>		'',
					'ga_webpropertyid' =>	'',
					'ga_accountname' =>		'',
					'ga_accountid' =>		'',
					'ga_beginning' =>		''
					)
				);
			// initialize general options
			$this->OPTIONS['googlyzer-options']['options'] = array(
				'standard' => array(
					'website'=>				null,
					'ga_tableid'=>			null,
					'date_range'=>			null,
					'today_enable'=>		null, 
					'start'=>				null, 
					'end'=>					null,
					'dash_enable'=>			null,
					'ga_auth_token' =>		null,
					'ga_login' =>			null,
					'ga_password' =>		null
					)
				);
			// load general options defaults
			$this->OPTIONS['googlyzer-options']['defaults'] = array(
				'standard' => array(
					'website'=>				'',
					'ga_tableid'=>			'',
					'date_range'=>			'30 days', 
					'today_enable'=>		'enabled',
					'start'=>				'', 
					'end'=>					'',
					'dash_enable'=>			'enabled',
					'ga_auth_token' =>		null,
					'ga_login' =>			null,
					'ga_password' =>		null
					)
				);
			// initialize linechart options
			$this->OPTIONS['googlyzer-linecharts']['options'] = array(
				'linechart_general' => array(
					'width' => 			null,
					'height' => 		null,
					'max_results' => 	null
					),
				'linechart1' => array(
					'color'=>			null,
					'dimensions'=>		null,
					'enable'=>			null,
					'history_enable'=>	null,
					'history_color'=>	null,
					'metrics'=>			null,
					'metadata_enable'=>	null,
					'sort_order' => 	null,
					'sort_column' => 	null,
					'trend_enable'=>	null
					),
				'linechart2' => array(
					'color'=>			null,
					'dimensions'=>		null,
					'enable'=>			null,
					'history_enable'=>	null,
					'history_color'=>	null,
					'metrics'=>			null,
					'metadata_enable'=>	null,
					'sort_order' => 	null,
					'sort_column' => 	null,
					'trend_enable'=>	null
					),
				'linechart3' => array(
					'color'=>			null,
					'dimensions'=>		null,
					'enable'=>			null,
					'history_enable'=>	null,
					'history_color'=>	null,
					'metrics'=>			null,
					'metadata_enable'=>	null,
					'sort_order' => 	null,
					'sort_column' => 	null,
					'trend_enable'=>	null
					),
				'linechart4' => array(
					'color'=>			null,
					'dimensions'=>		null,
					'enable'=>			null,
					'history_enable'=>	null,
					'history_color'=>	null,
					'metrics'=>			null,
					'metadata_enable'=>	null,
					'sort_order' => 	null,
					'sort_column' => 	null,
					'trend_enable'=>	null
					)
				);
			// load linechart defaults
			$this->OPTIONS['googlyzer-linecharts']['defaults'] = array(
				'linechart_general' => array(
					'width' => 			'225px',
					'height' => 		'50px',
					'max_results' => 	10000
					),
				'linechart1' => array(
					'color'=>			'#FF0000', // red
					'dimensions'=>		'ga:date',
					'enable'=>			'enabled',
					'history_enable'=>	'enabled',
					'history_color'=>	'#7D7D7D', // gray
					'metrics'=>			'ga:pageviews',
					'metadata_enable'=>	'enabled',
					'sort_order' => 	'ascending',
					'sort_column' => 	'ga:date',
					'trend_enable'=>	'enabled'
					),
				'linechart2' => array(
					'color'=>			'#009900', // green
					'dimensions'=>		'ga:date',
					'enable'=>			'enabled',
					'history_enable'=>	'enabled',
					'history_color'=>	'#7D7D7D', // gray
					'metrics'=>			'ga:visitors',
					'metadata_enable'=>	'enabled',
					'sort_order' => 	'ascending',
					'sort_column' => 	'ga:date',
					'trend_enable'=>	'enabled'
					),
				'linechart3' => array(
					'color'=>			'#0000FF', // blue
					'dimensions'=>		'ga:date',
					'enable'=>			'enabled',
					'history_enable'=>	'enabled',
					'history_color'=>	'#7D7D7D', // gray
					'metrics'=>			'ga:newVisits',
					'metadata_enable'=>	'enabled',
					'sort_order' => 	'ascending',
					'sort_column' => 	'ga:date',
					'trend_enable'=>	'enabled'
					),
				'linechart4' => array(
					'color'=>			'#FF6600', // orange
					'dimensions'=>		'ga:date',
					'enable'=>			'enabled',
					'history_enable'=>	'enabled',
					'history_color'=> 	'#7D7D7D', // gray
					'metrics'=>			'ga:visitBounceRate',
					'metadata_enable'=>	'enabled',
					'sort_order' => 	'ascending',
					'sort_column' => 	'ga:date',
					'trend_enable'=>	'enabled'
					)
				);
			// initialize piechart options
			$this->OPTIONS['googlyzer-piecharts']['options'] = array(
				'piechart_general' => array(
					'height' =>			null
					),
				'piechart1' => array(
					'chart_selection'=>	null,
					'enable'=>			null,
					'max_results'=>		null,
					'slice_color_1'=>	null,
					'slice_color_2'=>	null,
					'slice_color_3'=>	null,
					'slice_color_4'=>	null,
					'slice_color_5'=>	null
					),
				'piechart2' => array(
					'chart_selection'=>	null,
					'enable'=>			null,
					'max_results'=>		null,
					'slice_color_1'=>	null,
					'slice_color_2'=>	null,
					'slice_color_3'=>	null,
					'slice_color_4'=>	null,
					'slice_color_5'=>	null
					)
				);
			// load piechart defaults
			$this->OPTIONS['googlyzer-piecharts']['defaults'] = array(
				'piechart_general' => array(
					'height' =>			'80px'
					),
				'piechart1' => array(
					'chart_selection'=>	'browser',
					'enable'=>			'enabled',
					'max_results'=>		4,
					'slice_color_1'=>	'#FF0000', // red
					'slice_color_2'=>	'#009900', // green
					'slice_color_3'=>	'#0000FF', // blue
					'slice_color_4'=>	'#FF9900', // orange
					'slice_color_5'=>	'#FF00FF' // fuscia
					),
				'piechart2' => array(
					'chart_selection'=>	'referral_source',
					'enable'=>			'enabled',
					'max_results'=>		10000,
					'slice_color_1'=>	'#FF0000', // red
					'slice_color_2'=>	'#009900', // green
					'slice_color_3'=>	'#0000FF', // blue
					'slice_color_4'=>	'#FF9900', // orange
					'slice_color_5'=>	'#FF00FF' // fuscia
					)
				);
			// initialize table options
			$this->OPTIONS['googlyzer-tables']['options'] = array(
				'table_general' => array(),
				'table1' => array(
					'dimensions'=>					null,
					'dimension_filter_enable' =>	null,
					'dimension_filter_op' =>		null,
					'dimension_filter_string' =>	null,
					'enable'=>						null,
					'metrics'=>						null,
					'metric_filter_enable' =>		null,
					'metric_filter_op' =>			null,
					'metric_filter_value' =>		null,
					'num_rows'=>					null,
					'sort_order'=>					null,
					'sort_column'=>					null
					),
				'table2' => array(
					'dimensions'=>					null,
					'dimension_filter_enable' =>	null,
					'dimension_filter_op' =>		null,
					'dimension_filter_string' =>	null,
					'enable'=>						null,
					'metrics'=>						null,
					'metric_filter_enable' =>		null,
					'metric_filter_op' =>			null,
					'metric_filter_value' =>		null,
					'num_rows'=>					null,
					'sort_order'=>					null,
					'sort_column'=>					null
					)
				);
			// load table defaults
			$this->OPTIONS['googlyzer-tables']['defaults'] = array(
				'table_general' => array(),
				'table1' => array(
					'dimensions'=>					'ga:pageTitle',
					'dimension_filter_enable' =>	'disabled',
					'dimension_filter_op' =>		'regex_match',
					'dimension_filter_string' =>	'',
					'enable'=>						'enabled',
					'metrics'=>						'ga:pageviews',
					'metric_filter_enable' =>		'disabled',
					'metric_filter_op' =>			'equals',
					'metric_filter_value' =>		0,
					'num_rows'=>					5,
					'sort_order'=>					'descending',
					'sort_column'=>					'ga:pageviews'
					),
				'table2' => array(
					'dimensions'=>					'ga:keyword',
					'dimension_filter_enable' =>	'disabled',
					'dimension_filter_op' =>		'regex_match',
					'dimension_filter_string' =>	'',
					'enable'=>						'enabled',
					'metrics'=>						'ga:organicSearches',
					'metric_filter_enable' =>		'disabled',
					'metric_filter_op' =>			'equals',
					'metric_filter_value' =>		0,
					'num_rows'=>					5,
					'sort_order'=>					'descending',
					'sort_column'=>					'ga:organicSearches'
					)
				);

			// actions
			// add action for admin initialization
			add_action(
				$tag = 'admin_init',
				$callback = array( &$this, 'admin_init' ),
				$priority = 1
				);
			// add menu item for options page
			add_action(
				$tag = 'admin_menu',
				$callback = array( &$this, 'admin_menu' ),
				$priority = 1
				);
			// add admin footer callback
			add_action(
				$tag = 'admin_footer', 
				$callback = array( &$this, 'admin_footer' ),
				$priority = 100
				);
				
			// filters
		
		}
		
		/*
		 * private functions
		*/
		
		function updateGATracking() {
			foreach ( $this->accountList as $account ) {
				if ( $this->OPTIONS['googlyzer-options']['options']['standard']['website'] == $account['tableId'] ) {
					try {
						$data_start = googlyzer_Utilities::determineGADataStart( $this->OPTIONS['googlyzer-options']['options']['standard'] );
						$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_beginning'] = googlyzer_Utilities::convertDateString( $data_start, 'googlyzer' );
					} catch ( googlyzer_Exception $e ) {
						// echo $e->getError();
						$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_beginning'] = '';
					}
					$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_tableid'] = $account['tableId'];
					$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_profileid'] = $account['profileId'];
					$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_webpropertyid'] = $account['webPropertyId'];
					$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_accountname'] = $account['accountName'];
					$this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_accountid'] = $account['accountId'];
					break;
				}
			}
			// update options array in WP database
			update_option( $option_name = 'googlyzer-ga_tracking',
				$newvalue = $this->OPTIONS['googlyzer-ga_tracking']['options'] );
		}
		
		/*
		 * public functions
		 */
		
		// method to add plugin options
		function add_options() {
			
			// load settings from external file
		    $return = $this->load_file_input(
		    	$file = GOOGLYZER_ADD_OPTIONS,
		    	$object = $this,
		    	$target = 'SETTINGS',
		    	$pattern = "/obj=/",
		    	$delimiter = "/[\s]+/",
		    	$operator = "/=/"
		    	);
		    	
			// verify load, report error
		    if ( !$return['retval'] ) {
		    	die( $return['msg'] );
		    }
			
			// loop through each setting object
	    	foreach ( $this->SETTINGS as $key => $setting ) {
	    		switch ( $setting['obj'] ) {
	    			case 'setting':
	    				// register setting with wordpress
	    				register_setting(
	    					$option_group = $setting['option_group'],
	    					$option_name = $setting['option_name'],
	    					$sanitize_callback = array( &$this, $setting['sanitize_callback'] )
	    					);
	    				break;
	    			case 'section':
	    				// add settings section to wordpress
	    				add_settings_section(
	    					$id = $setting['id'],
	    					$title = $setting['title'],
	    					$callback = array( &$this, $setting['callback'] ),
	    					$page = $setting['page']
	    					);
	    				break;
	    			case 'field':
	    				// add field to settings section
	    				if ( key_exists( 'selections', $setting['args'] ) ) {
	    					if ( isset( $this->$setting['args']['selections'] ) ) {
	    						$setting['args']['selections'] = $this->$setting['args']['selections'];
	    					}
	    				}
	    				add_settings_field(
	    					$id = $setting['id'],
	    					$title = $setting['title'],
	    					$callback = array( &$this, $setting['callback'] ),
	    					$page = $setting['page'],
	    					$section = $setting['section'],
	    					$args = $setting['args']
	    					);
	    				break;
	    		}
	    	}
	    	// initialize plugin options
			$this->options_init();
		}
		
		// method to modify admin footer for insertion of jQuery datepicker class
		function admin_footer() {
			?>
			<script type="text/javascript">
			jQuery(document).ready(function(){
				if ( document.title.indexOf("Googlyzer Options") != -1 ) {
					jQuery('.mydatepicker').datepicker({
						dateFormat : 'mm-dd-yy'
					});
				}
			});
			</script>
			<?php
		}
		
		// method to initialize admin options and scripts
		function admin_init() {		
			// load options from database
			$this->retrieve_options();
			// register options groups, sections, and fields
			$this->add_options();
			
			// instantiate googlyzer_homeWidget class
			if ( class_exists( 'googlyzer_homeWidget' ) 
				&& $this->OPTIONS['googlyzer-options']['options']['standard']['dash_enable'] === 'enabled' ) {
				new googlyzer_homeWidget( $this );
			}
			
			// scripts, styles, and charts options to be loaded
			$loads = array(
				'SCRIPTS' => GOOGLYZER_REGISTER_SCRIPTS,
				'STYLES' => GOOGLYZER_REGISTER_STYLES
				);
			
			// loop for each file
			foreach ( $loads as $key => $value ) {
				// load external file
				$return = $this->load_file_input(
			    	$file = $value,
			    	$object = $this,
			    	$target = $key,
			    	$pattern = "/obj=/",
			    	$delimiter = "/[\s]+/",
			    	$operator = "/=/"
			    	);
			    // verify load, report error
			    if ( !$return['retval'] ) {
			    	die( $return['msg'] );
			    }
			}
		    
		    // register scripts with wordpress
		    $this->process_scripts();
		    // register styles with wordpress
		    $this->process_styles();
				
		}
		
		// method to create admin menu items
		function admin_menu() {
			
			// add googlyzer options page link to settings menu
			$page_hook = add_options_page(
				$page_title = 'Googlyzer Options',
				$menu_title = 'Googlyzer',
				$capability = 'manage_options',
				$menu_slug = 'googlyzer',
				$callback = array( &$this, 'admin_page' )
				);
				
			$this->PAGE_HOOKS['OPTIONS'] = $page_hook;
			
			// add action to output scripts for options page
			add_action(
				$tag = 'admin_print_scripts-' . $this->PAGE_HOOKS['OPTIONS'],
				$callback = array( &$this, 'admin_scripts' ),
				$priority = 1
				);
			
			// add action to output stylesheets for options page
			add_action(
				$tag = 'admin_print_styles-' . $this->PAGE_HOOKS['OPTIONS'],
				$callback = array( &$this, 'admin_styles' ),
				$priority = 1
				);
				
			// add action to output stylesheets for home dashboard page
			add_action(
				$tag = 'admin_print_styles-index.php',
				$callback = array( &$this, 'admin_styles' ),
				$priority = 1
				);
				
			// add action to output scripts for home dashboard page
			add_action(
				$tag = 'admin_print_scripts-index.php',
				$callback = array( &$this, 'admin_scripts' ),
				$priority = 1
				);
				
			// add action to output scripts for widget page
			add_action(
				$tag = 'admin_print_scripts-widgets.php',
				$callback = array( &$this, 'admin_scripts' ),
				$priority = 1
				);
				
			// add dashboard page link to tools menu
			$page_hook = add_dashboard_page(
				$page_title = 'Googlyzer Dashboard',
				$menu_title = 'Googlyzer Dashboard',
				$capability = 'manage_options',
				$menu_slug = 'googlyzer-dashboard',
				$callback = array( &$this, 'dashboard_page' )
				);
				
			$this->PAGE_HOOKS['DASHBOARD'] = $page_hook;
				
			// add action to output scripts for dashboard page
			add_action(
				$tag = 'admin_print_scripts-' . $this->PAGE_HOOKS['DASHBOARD'],
				$callback = array( &$this, 'admin_scripts' ),
				$priority = 1
				);
			
			// add action to output stylesheets for dashboard page
			add_action(
				$tag = 'admin_print_styles-' . $this->PAGE_HOOKS['DASHBOARD'],
				$callback = array( &$this, 'admin_styles' ),
				$priority = 1
				);						
		}
		
		// method to create options page
		function admin_page() {
			try {
				if ( class_exists( 'googlyzer_AdminPage' ) ) {
					new googlyzer_AdminPage( $parentInstance = $this );
				} else {
					// failed to load googlyzer_AdminPage class
					throw new googlyzer_Exception( 'googlyzer_AdminPage class not loaded.' );
				}
			} catch ( googlyzer_Exception $e ) {
				echo $e->getError();
			}
		}
		
		// method to load custom javascripts
		function admin_scripts() {
			// loop through each script object
		    foreach ( $this->SCRIPTS as $key => $script ) {
		    	wp_enqueue_script( $handle = $script['handle'] );
		    	// catch localize flag
		    	if ( $script['localize'] === 'yes' ) {
		    		// encode PHP options array into json string
		    		$parameters = array( 'json_str' => json_encode( $this->$script['l10n'] ) );
		    		// localize script with json encoded options string as variable
		    		wp_localize_script(
		    			$handle = $script['handle'],
		    			$object_name = $script['object_name'],
		    			$l10n = $parameters
		    			);
		    	}
		    }			
		}
		
		// method to load css styles
		function admin_styles() {
			// loop through each style object
			foreach ( $this->STYLES as $key => $style ) {
				wp_enqueue_style( $handle = $style['handle'] );
			}
		}
		
		// method to output googlyzer dashboard page
		function dashboard_page() {
			if ( class_exists( 'googlyzer_dashboard' ) ) {
				new googlyzer_dashboard( $this );	
			}
		}
		
		public function getDimensionLabel( $dimension ) {
			return $this->DIMENSION_SELECTIONS[$dimension];
		}
		
		/**
		 * 
		 * Method to retrieve domain list for dropdown in general options page.
		 * 
		 * @throws googlyzer_Exception
		 * @return array
		 */
		public function getDomainList() {
			$retry = true;
			$retry_count = 0;
			$max_retries = 1;
			while ( $retry && $retry_count <= $max_retries ) {
				try {
					// check to see if ga_auth_token exists
					if ( !empty( $this->OPTIONS['googlyzer-options']['options']['standard']['ga_auth_token'] ) ) {
						// ga_auth_token exists, let's set up the account feed
						$gaAcctFeed = new googlyzer_AccountFeed(
							$feedType = 			'account_feed',
							$url = 					'https://www.google.com/analytics/feeds/accounts/default?',
							$method = 				'GET',
							$feedSpecificSettings = array(
								'GA_AUTH_TOKEN' => 		$this->OPTIONS['googlyzer-options']['options']['standard']['ga_auth_token']
								)
							);
						// get the list of profiles for the account
						$gaAcctFeed->getFeed();
						$profileList = $gaAcctFeed->handleFeed();
						// store account list
						$this->accountList = $gaAcctFeed->getAccountList();
						$this->updateGATracking();
						return $profileList; // pass profiles list to selections array
					}
					// if ga_auth_token doesn't exist, domain dropdown will be empty
				} catch ( googlyzer_Exception $e ) {
					if ( $ga_auth_token = googlyzer_Utilities::checkException( $e, $this->OPTIONS['googlyzer-options']['options']['standard'] ) ) {
						$this->OPTIONS['googlyzer-options']['options']['standard']['ga_auth_token'] = $ga_auth_token;
					} else {
						$retry = false;
					}
				}
				$retry_count++;
			}			
		}
		
		public function getDomainName() {
			$myDomain = false;
			try {
				$domains = $this->getDomainList();
				foreach ( $domains as $ga_tableid => $domain ) {
					if ( $this->OPTIONS['googlyzer-options']['options']['standard']['ga_tableid'] == $ga_tableid ) {
						$myDomain = $domain;
					}
				}
				return $myDomain;
			} catch ( googlyzer_Exception $e ) {
				echo $e->getError();
			}
		}
		
		public function getGATableId() {
			return $this->OPTIONS['googlyzer-options']['options']['standard']['ga_tableid'];
		}
		
		public function getGABeginning() {
			return $this->OPTIONS['googlyzer-ga_tracking']['options']['ga_tracking_general']['ga_beginning'];
		}
		
		public function getMetricLabel( $metric ) {
			return $this->METRIC_SELECTIONS[$metric];
		}
		
		public function getPieChartLabel( $chartSelection ) {
			return $this->PIECHART_SELECTIONS[$chartSelection];
		}
		
		// method to load scripts & styles from external file
		function load_file_input( $file, $object, $target, $pattern, $delimiter, $operator ) {
			// read contents of script registration file
			$input_stream = file_get_contents(
		    	$filename = $file,
		    	$use_include_path = false
		    	);
		    // if input file is not able to be read, alert
		    if ( !$input_stream ) {
		    	return array( 'retval' => false, 'msg' => "error: unable to load $file." );
		    // otherwise proceed
		    } else {
		    	// process input stream
		    	$this->process_file_input(
		    		$input = $input_stream,
		    		$load_obj = $object,
		    		$load_target = $target,
		    		$obj_pattern = $pattern,
		    		$item_delimiter = $delimiter,
		    		$assignment_operator = $operator
		    		);
		    	if ( $load_obj->$load_target ) {
		    		return array( 'retval' => true, 'msg' => "info: loaded $file." );
		    	} else {
		    		return array( 'retval' => false, 'msg' => "error: unable to load $file." );
		    	}
		    }
		}
		
		// method to initialize options array if necessary
		function options_init() {
			// outer loop for options groups (googlyzer-options, googlyzer-linecharts, etc.)
			foreach ( $this->OPTIONS as $group => $section ) {
				// loop for options items (general, linechart1, linechart2, etc.)
				foreach ( $this->OPTIONS[$group]['options'] as $item => $properties ) {
					// check for values
					if ( array_filter( $properties ) ) {
						// options array contains at least 1 non-empty element
						foreach ( $properties as $key => $value ) {
							// check to see if value is set, if not set to default
							if ( !isset( $value ) ) {
								$this->OPTIONS[$group]['options'][$item][$key] = $this->OPTIONS[$group]['defaults'][$item][$key];
							}
						}
					} else {
						// options array contains 0 elements or all elements are empty
						foreach ( $properties as $key => $value ) {
							// retrieve default values and populate options array
							$this->OPTIONS[$group]['options'][$item][$key] = $this->OPTIONS[$group]['defaults'][$item][$key];
						}
					}
				}
				// update options in wordpress database
				update_option( $option_name = $group, 
					$newvalue = $this->OPTIONS[$group]['options'] );
			}
		}
		
		// method to output settings section text
		function output_section_text( $section ) {
			// explode the section array into variables
			extract( $section );
			$text = '';
			switch ( $id ) {
				case 'googlyzer-general' :
					break;
				case 'googlyzer-ga_tracking':
					break;
				default :
					foreach ( $this->SETTINGS as $key => $setting ) {
						if ( array_key_exists( 'id', $setting ) && $setting['id'] == $id ) {
							$text = $setting['text'];
							break;
						}
					}
					echo '<p>' . $text . '</p>';
					break;
			}
		} // end method output_section_text	    
		
		/*
		 * function to output setting fields on options pages
		 * 
		 * inputs:
		 * 
		 *		class			desired field class
		 *		id				desired field id
		 *		onchange		onchange callback function name
		 *		onload			onload callback function name
		 *		readonly		set field to read only
		 *		selections		array of values to be listed as selections for dropdown lists
		 *		size			desired field size
		 *		type			desired field type (text, checkbox, dropdown, date, etc.)
		 *
		 *	return:
		 *
		 *		none
		 */
		function output_setting_field( $args ) {
			// explode the args array into variables
			extract( $args );
			
			/*
			 * Special handling required for standard_website (Domain) dropdown 
			 * and standard_ga_table_id (Google Analytics Table ID).
			 * Need to populate dropdown and table_id if auth_token exists
			 * 
			 * Special handling also needed for standard_ga_beginning text field.
			 * 
			 */
			
			// first check $input_id to see if we're on the right field
			if ( $input_id === 'standard_website' ) {
				try {
					$selections = $this->getDomainList();
				} catch ( googlyzer_Exception $e ) {
					// echo $e->getError();
					$selections = array();
				}
			} elseif ( $input_id === 'standard_ga_beginning' ) {
				
			} elseif ( $input_id === 'ga_tracking_general_enable' ) {
				$this->updateGATracking();
			}
			
			// check class, onchange, onclick, onload, and readonly properties
			$class = ( isset( $class ) ) ? 'class="' . $class . '" ' : '';
			$cols = ( isset( $cols ) ) ? 'cols="' . $cols . '" ' : '';
			$end_tag = '';
			$id = 'id="' . $input_id . '" ';
			$onchange = ( isset( $onchange ) ) ? 'onchange="' . $onchange . '()" ' : '';
			$onclick = ( isset( $onclick ) ) ? 'onclick="' . $onclick . '()" ' : '';
			$onload = ( isset( $onload ) ) ? 'onload="' . $onload . '()" ' : '';
			$readonly = ( isset( $readonly ) ) ? 'readonly="' . $readonly . '()" ' : '';
			$rows = ( isset( $rows ) ) ? 'rows="' . $rows . '" ' : '';
			$size = ( isset( $size ) ) ? 'size="' . $size . '" ' : '';
			$style = ( isset( $style ) ) ? 'style="' . $style . '" ' : '';
			// set up options for checkbox
			if ( $type === 'checkbox' ) {
				// detect check box and set value
				$checked = ( $this->OPTIONS[$group]['options'][$item][$name] === 'enabled' ) ? 'checked="checked" ' : '';
				$val = 'value="enabled" />';
			// otherwise, it's not a checkbox
			} else {
				$checked = '';
				$val = 'value="' . $this->OPTIONS[$group]['options'][$item][$name] . '" />';
			}
			// output input tag, if appropriate
			if ( $type !== 'dropdown' && $type !== 'textarea' ) {
				$type = 'type="' . $type . '" ';
				echo '<input '; 
			} else if ( $type === 'textarea' ) {
				$end_tag = $this->OPTIONS[$group]['options'][$item][$name] . '</textarea>';
				$val = '';
				$type = ' />';
				echo '<textarea ';	
			}
			if ( $type !== 'dropdown' ) {
				// text, date, or password field
				echo $id .
					'name="'.$group.'['.$item.']['.$name.']" '.
					$checked .
					$onchange .
					$onclick .
					$onload .
					$readonly .
					$class .
					$rows . 
					$cols .
					$size .
					$style .
					$type .
					$val .
					$end_tag;
				// dropdown
			} else {
				echo '<select id="'.$input_id.'"
					name="'.$group.'['.$item.']['.$name.']" '.$onchange.$onload.$class.$style.'>';
				if ( is_array( $selections ) ) {
					// if selections array is standard array
					if ( !googlyzer_Utilities::isAssociative( $selections ) ) {
						foreach ( $selections as $option_val ) {
							$selected = ( $this->OPTIONS[$group]['options'][$item][$name] == $option_val ) ? 'selected="selected"' : '';
							echo "<option value='$option_val' $selected>$option_val</option>";
						}
					// otherwise selections array is associative array
					} else {
						foreach ( $selections as $key => $value ) {
							$selected = ( $this->OPTIONS[$group]['options'][$item][$name] == $key ) ? 'selected="selected"' : '';
							echo "<option value='$key' $selected>$value</option>";
						}
					}
				}
				echo "</select>";
			}
		}
		
		function process_file_input( $input, $load_obj, $load_target, $obj_pattern, $item_delimiter, $assignment_operator ) {
			
			// array for script items as single raw string captured from file input
	    	$script_string = array();
	    	// match all occurrences of script identification pattern in input
	    	preg_match_all( $obj_pattern, $input, $matches, PREG_OFFSET_CAPTURE );
	    	// loop through all matches for script or style objects and push into temporary stack
	    	foreach ( $matches[0] as $key => $value ) {
	    		// check for end of input and grab appropriately
    			if ( $key+1 === count( $matches[0] ) ) {
    				// for each match occurrence, capture script/style item as single string
    				array_push( $script_string, trim( substr( $input, $value[1] ) ) );
    			} else {
    				// for each match occurrence, capture script/style item as single string
	    			array_push( $script_string, trim( substr( $input, $value[1], $matches[0][$key+1][1]-1-$value[1] ) ) );
    			}
	    	}
	    	// initialize array to hold associative arrays for script items
	    	$script_array = array();
	    	// loop through each raw script string
	    	foreach ( $script_string as $key => $value ) {
	    		// check for quoted items, and temporarily replace with underscores if necessary
	    		if ( preg_match_all( "/'/", $value, $matches, PREG_OFFSET_CAPTURE ) ) {
	    			for ( $k=0; $k<count($matches[0]); $k++ ) {
	    				if ( $matches[0][$k+1][1] - $matches[0][$k][1] > 1 ) {
		    				$raw_quoted_string = substr( $value, $matches[0][$k][1], $matches[0][$k+1][1]-$matches[0][$k][1]+1 );
		    				$mod_quoted_string = str_replace( " ", "_", $raw_quoted_string );
		    				$value = str_replace( $raw_quoted_string, $mod_quoted_string, $value );
	    				}
	    				$k++;
	    			}
	    			
	    		}
				// tokenize based on whitespace
	    		$lines = preg_split( $item_delimiter, $value );
	    		// initialize array to hold actual attributes and values
	    		$item = array();
	    		// initialize array to hold custom callback parameters
	    		$args = array();
	    		// set callback argument flag to false
	    		$args_flag = false;
	    		// loop through each line
	    		foreach ( $lines as $idx => $val ) {
	    			// check for comment character, throw line out if found
	    			if ( !preg_match( "/^\*|\/\*/", $val ) ) {
		    			// tokenize based on '='
		    			$line = preg_split( $assignment_operator, $val );
		    			// check to see if item is source, which may require imploding
		    			if ( $line[0] === 'src' ) {
		    				array_shift( $line );
		    				$src = implode( "=", $line );
		    				$line[0] = 'src';
		    				$line[1] = $src;
		    			// if item is comma delimited list, push into array
		    			} elseif ( preg_match( "/,/", $line[1] ) ) {
		    				$line[1] = preg_split( "/,/", $line[1] );
		    			// check for start of callback args
		    			} elseif ( $line[0] === 'args' && $line[1] === 'start' ) {
		    				$args_flag = true;
		    			// check for end of callback args
		    			} elseif ( $line[0] === 'args' && $line[1] === 'end' ) {
		    				$args_flag = false;
		    				$line[1] = $args;
		    			// if args_flag is already set, grab custom callback parameter
		    			} elseif ( $args_flag ) {
		    				$args[$line[0]] = $line[1];
		    			}
		    			// if args_flag is not set, proceed with line processing and recording
		    			if ( !$args_flag ) {
		    				// check for array in line[1] before reinserting whitespace
		    				if ( is_array( $line[1] ) ) {
		    					foreach ( $line[1] as $k => $v ) {
		    						if ( preg_match( "/'.+_/", $v ) ) {
		    							$line[1][$k] = str_replace( "_", " ", $v );
		    						}
		    						$line[1][$k] = trim( $line[1][$k], "'" );
		    					}
		    				} else {
				    			// re-insert spaces in place of underscores
				    			if ( preg_match( "/'.+_/", $line[1] ) ) {
				    				$line[1] = str_replace( "_", " ", $line[1] );
				    			}
				    			$line[1] = trim( $line[1], "'" );
		    				}
			    			// store item in temporary associative array
			    			$item[$line[0]] = $line[1];
		    			}
	    			}
	    		}
	    		// push item into object attribute
	    		array_push( $load_obj->$load_target, $item );
	    	}
		}
		
		function process_scripts() {
			
			// loop through each script object
	    	foreach ( $this->SCRIPTS as $key => $script ) {
	    		// check path and set appropriately
	    		if ( $script['path'] !== 'web' ) {
	    			$script['src'] = GOOGLYZER_JS . $script['src'];
	    		}
	    		// register script with wordpress
	    		wp_register_script(
	    			$handle = $script['handle'],
		    		$src = $script['src'],
		    		$deps = $script['deps']
	    			);
	    	}
		}
		
		function process_styles() {
			// loop through each style object
	    	foreach ( $this->STYLES as $key => $style ) {
	    		switch ( $style['path'] ) {
    				case 'THEMES':
    					$style['src'] = GOOGLYZER_THEMES . $style['src'];
    					break;
    				case 'CSS':
    					$style['src'] = GOOGLYZER_CSS . $style['src'];
    					break;
	    		}
	    		// finally, register style
	    		wp_register_style(
	    			$handle = $style['handle'],
	    			$src = $style['src']
	    			);		    		
	    	}
		}
		
		// method to load options from database
		function retrieve_options() {
			// outer loop for options groups
			foreach ( $this->OPTIONS as $group => $section ) {
				// first get options array from wordpress database
				$arr = get_option(
					$show = $group,
					$default = false
					);
				// check to see if option array was successfully retrieved
				if ( $arr !== false ) {
					// loop through options array
					foreach ( $this->OPTIONS[$group]['options'] as $item => $properties ) {
						// check to see if array is empty
						if ( !empty( $arr[$item]) ) {
							// loop for actual option keys and values
							foreach ( $properties as $key => $value ) {
								// if valid array value exists in database options array, store it
								if ( array_key_exists( $key, $arr[$item] ) ) {
									if ( $arr[$item][$key] != null ) {
										$this->OPTIONS[$group]['options'][$item][$key] = $arr[$item][$key];
									} else {
										$this->OPTIONS[$group]['options'][$item][$key] = $this->OPTIONS[$group]['defaults'][$item][$key];
									}
								// otherwise, set default
								} else {
									$this->OPTIONS[$group]['options'][$item][$key] = $this->OPTIONS[$group]['defaults'][$item][$key];
								}
							}
						} else {
							// first time that this version of Googlyzer has been loaded, option group not found in database
						}
					}
				} else {
					// first time that Googlyzer has been loaded, options array not found in database	
				}
			}
		} // end method retrieve_options
		
		// method for trimming options values and eliminating bogus array keys
		function sanitize_section( $item, $properties ) {
			$sanitized = Array();
			
			// loop through actual item properties (fields and values)
			foreach ( $item as $key => $value ) {
				// look for field in rawinput
				if ( array_key_exists( $key, $properties ) ) {
					// catch properties that are stored as arrays (dimensions, metrics, etc.)
					if ( is_array( $properties[$key] ) ) {
						// initialize key/values array
						$sanitized[$key] = Array();
						// loop for fields stored as arrays
						foreach ( $properties[$key] as $k => $v ) {
							$sanitized[$key][$k] = trim( $v );
						}
					// otherwise it's a single value
					} else {
						$sanitized[$key] = trim( $properties[$key] );
					}
				// catch checkboxes that are not enabled (not submitted as part of rawinput array unless checked)
				} else if ( preg_match( "/enable/", $key ) ) {
					$sanitized[$key] = 'disabled';
				}
			} // end foreach properties
			return $sanitized;
		} // end method sanitize_section
		
		// method for validation of options
		function validate_options( $rawinput ) {
			$sanitized = Array();
			// set up selection lists for validation
			$piecharts = googlyzer_Utilities::getSelections( $this->PIECHART_SELECTIONS );
			$metrics = googlyzer_Utilities::getSelections( $this->METRIC_SELECTIONS );
			$dimensions = googlyzer_Utilities::getSelections( $this->DIMENSION_SELECTIONS );
			foreach ( $rawinput as $raw_item => $raw_properties ) {
				// check based on raw item value
				/*
				 * composite option checks
				 */
				if ( preg_match( "/composite/", $raw_item ) ) {
					// perform initial input sanitization
					$sanitized[$raw_item] = $this->sanitize_section( $this->OPTIONS['googlyzer-composites']['options'][$raw_item], $raw_properties );
					/* exclude general options (FOR NOW)
					 *
					 * @todo: include general options in list of modifiable settings
					 */
					if ( !preg_match( "/general/", $raw_item ) ) {
						// loop for item properties... actual fields and values
						foreach ( $sanitized[$raw_item] as $key => $value ) {
							// look for any color properties
							if ( preg_match( "/color/", $key ) ) {
								// check for valid hex color code
								if ( !googlyzer_Utilities::validHexColor( $value ) ) {
									// reset to default if not a valid hex color code
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];
								}
							} elseif ( preg_match( "/metric/", $key ) ) {
								if ( !in_array( $value, $metrics ) ) {
									$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];
								}
							}
							switch ( $key ) {
								case 'dimensions': // validate dimension selection
									if ( !in_array( $value, $dimensions ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];
									}
									break;
								case 'enable': // chart display toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];	
									}
									break;
								case 'metadata_enable' : // metadata toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];	
									}
									break;
								case 'sort_order': // validate sort order selection
									if ( !in_array( $value, $this->SORT_SELECTIONS ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-composites']['defaults'][$raw_item][$key];
									}
									break;
							}
						} // end for each $key => $value
					} // end if $raw_item !match general
				} elseif ( preg_match( "/ga_tracking_general/", $raw_item ) ) {
					// perform initial input sanitization
					$sanitized[$raw_item] =
						$this->sanitize_section( $this->OPTIONS['googlyzer-ga_tracking']['options'][$raw_item],
							$raw_properties );
				/*
				 * standard option checks
				 */
				} elseif ( preg_match( "/standard/", $raw_item ) ) {
					// perform initial input sanitization
					$sanitized[$raw_item] =
						$this->sanitize_section( $this->OPTIONS['googlyzer-options']['options'][$raw_item],
							$raw_properties );
					// loop for item properties... actual fields and values
					foreach ( $sanitized[$raw_item] as $key => $value ) {
						switch ( $key ) {
							case 'website' : // website dropdown selector
								/*
								 * @todo: validation for website selection 
								 */
								break;
							case 'date_range' : // date range dropdown selector
								if ( !in_array( $value, $this->DATE_RANGE_SELECTIONS ) ) {
									$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
								}
								break;
							case 'start' : // start date field
								if ( $sanitized[$raw_item]['date_range'] === 'Custom Range' ) {
									if ( !googlyzer_Utilities::validDate( $value, 'm-d-Y' ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
									}
								}
								break;
							case 'end' : // end date field
								if ( $sanitized[$raw_item]['date_range'] === 'Custom Range' ) {
									if ( !googlyzer_Utilities::validDate( $value, 'm-d-Y' ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
									}
								}
								break;
							case 'ga_tableid' : // google analytics table id
								if ( !googlyzer_Utilities::validGoogleAnalyticsTableId( $value ) ) {
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
								}
								break;
							case 'ga_beginning' : // first occurrence of GA data
								/*
								if ( !googlyzer_Utilities::validDate( $value, 'm-d-Y' ) ) {
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
								}
								*/
								break;
							case 'today_enable' : // include today's data
								if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];	
								}
								break;
							case 'dash_enable' : // enable WP Home Dashboard widget
								if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-options']['defaults'][$raw_item][$key];
								}
								break;
							case 'ga_auth_token' : // GA authorization token
								$sanitized[$raw_item][$key] = $value;
								break;
						} // end switch for $key
					} // end for each $key => $value
				/*
				 * linechart option checks
				 */
				} elseif ( preg_match( "/linechart/", $raw_item ) ) { 
					// perform initial input sanitization
					$sanitized[$raw_item] = $this->sanitize_section( $this->OPTIONS['googlyzer-linecharts']['options'][$raw_item], $raw_properties );
					/* exclude general options (FOR NOW)
					 *
					 * @todo: include general options in list of modifiable settings
					 */
					if ( !preg_match( "/general/", $raw_item ) ) {
						// loop for item properties... actual fields and values
						foreach ( $sanitized[$raw_item] as $key => $value ) {
							// look for any color properties
							if ( preg_match( "/color/", $key ) ) {
								// check for valid hex color code
								if ( !googlyzer_Utilities::validHexColor( $value ) ) {
									// reset to default if not a valid hex color code
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];
								}
							}
							switch ( $key ) {
								case 'dimensions': // validate dimension selection
									if ( !in_array( $value, $dimensions ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];
									}
									break;
								case 'enable': // chart display toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];	
									}
									break;
								case 'history_enable' : // history toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];	
									}
									break;
								case 'metadata_enable' : // metadata toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];	
									}
									break;
								case 'metrics' : // validate metric selection
									if ( $value === 'none' ) {
										$sanitized[$raw_item]['enable'] = 'disabled';
									} elseif ( !in_array( $value, $metrics ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];
									}
									break;
								case 'sort_order': // validate sort order selection
									if ( !in_array( $value, $this->SORT_SELECTIONS ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];
									}
									break;
								case 'trend_enable' : // trend toggle
									if ( !googlyzer_Utilities::validCheckbox( $value ) ) {
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-linecharts']['defaults'][$raw_item][$key];	
									}
									break;
							} // end switch for $key
						} // end for each $key => $value
					} // end for each $item => $properties
				/*
				 * piechart option checks
				 */
				} elseif ( preg_match( "/piechart/", $raw_item ) ) { 
					// perform initial input sanitization
					$sanitized[$raw_item] = $this->sanitize_section( $this->OPTIONS['googlyzer-piecharts']['options'][$raw_item], $raw_properties );
					/* exclude general options (FOR NOW)
					 *
					 * @todo: include general options in list of modifiable settings
					 */
					if ( !preg_match( "/general/", $raw_item ) ) {
						// loop for item properties... actual fields and values
						foreach ( $sanitized[$raw_item] as $key => $value ) {
							// look for any color properties
							if ( preg_match( "/color/", $key ) ) {
								// check for valid hex color code
								if ( !googlyzer_Utilities::validHexColor( $value ) ) {
									// reset to default if not a valid hex color code
									$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-piecharts']['defaults'][$raw_item][$key];
								}
							}
							switch ( $key ) {
								// validate max_results
								case 'max_results':
									// check for integer >= 2
									if ( $value < 2 ) {
										// reset to default if not a valid integer >= 2
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-piecharts']['defaults'][$raw_item][$key];
									}
									break;
								case 'chart_selection':
									if ( !in_array( $value, $piecharts ) ) {
										echo "<p>value = $value</p>";
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-piecharts']['defaults'][$raw_item][$key];
									}
									break;
							} // end switch for $key
						} // end for each $key => $value
					} // end if $item != general
				/*
				 * table option checks
				 */
				} elseif ( preg_match( "/table/", $raw_item ) ) { 
					// perform initial input sanitization
					$sanitized[$raw_item] = $this->sanitize_section( $this->OPTIONS['googlyzer-tables']['options'][$raw_item], $raw_properties );
					/* exclude general options (FOR NOW)
					 *
					 * @todo: include general options in list of modifiable settings
					 */
					if ( !preg_match( "/general/", $raw_item ) ) {
						// loop for item properties... actual fields and values
						foreach ( $sanitized[$raw_item] as $key => $value ) {
							switch ( $key ) {
								// validate dimension selection
								case 'dimensions':
									if ( !in_array( $value, $dimensions ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-tables']['defaults'][$raw_item][$key];
									}
									break;
								// validate metric selection
								case 'metrics':
									if ( $value === 'none' ) {
										// if no metric is selected, disable table to avoid google api request errors
										$sanitized[$raw_item]['enable'] = 'disabled';
									} elseif ( !in_array( $value, $metrics ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-tables']['defaults'][$raw_item][$key];
									}
									break;
								// validate max_results
								case 'num_rows':
									// check for integer >= 1 and <= 15
									if ( $value < 1 || $value > 15 ) {
										// reset to default if not a valid integer >= 1 and <= 15
										$sanitized[$raw_item][$key] =  $this->OPTIONS['googlyzer-tables']['defaults'][$raw_item][$key];
									}
									break;
								case 'sort_order':
									if ( !in_array( $value, $this->SORT_SELECTIONS ) ) {
										$sanitized[$raw_item][$key] = $this->OPTIONS['googlyzer-tables']['defaults'][$raw_item][$key];
									}
									break;
							} // end switch for $key
						} // end for each $key => $value
					} // end if $item != general
				} // end if for $raw_item
			} // end foreach $raw_item
		return $sanitized;
		} // end method validate_options()
	} // end class googlyzer_options
} // end if googlyzer_options class exists
?>