<?php
///For handling output and templates
class Display{
///searchs in the display logic folder and gets all logic for a page, starting with broader and going to narrower scope
static function getDisplayLogic($page=null){
if($page){
$tokens = explode('/',$page);
}else{
$tokens = RequestHandler::$urlTokens;
}
$base = Config::$x['instanceFolder'].'display/logic/';
Files::inc($base.'logic.php',array('page'));
while($tokens){
$hierarchy[] = array_shift($tokens);
$path = implode('/',$hierarchy);
if(is_file($base.$path.'.php')){
Files::inc($base.$path.'.php',array('page'));
}elseif(is_file($base.$path.'/logic.php')){
Files::inc($base.$path.'/logic.php',array('page'));
}
}
}
///used to get the content of a single template file
/**
@param template string path to template file relative to the templateFolder. .php is appended to this path.
@param vars variables to extract and make available to the template file
@return output from a template
*/
static function getTemplate($template,$vars=null){
ob_start();
Files::req(Config::$x['templateFolder'].$template.'.php',array('page'),$vars);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
///Used to allow display logic to override template choices made in the controller
static $showArgs;
///used as the primary method to show a collection of templates. @attention parameters are the same as the Display::get function
static function show(){
self::$showArgs = func_get_args();
if(Config::$x['showPreHooks']){
foreach(Config::$x['showPreHooks'] as $hook){
call_user_func($hook);
}
}
$output = call_user_func_array(array('self','get'),self::$showArgs);
if(Config::$x['showPostHooks']){
foreach(Config::$x['showPostHooks'] as $hook){
call_user_func($hook,$output);
}
}
echo $output;
}
static private $callInstance = 0;
///used to get a collection of templates without displaying them
/**
@param templates has the following forms:
- single template string
- comma seperated list of templates
- array with each element being a template
- an array of structured template arrays:
@verbatim
array(
array('templateFile','templateName',$subTemplates),
array('templateFile2','templateName2',$subTemplates2),
)
@endverbatim
where subtempltes follow the same pattern as templates. In the case of subtemplates, named ouput of each subtemplate along with the total previous output of the subtemplates is passed to the supertemplate. The output of each subtemplate is passed by name in a $templates array, and the total output is available under the variable $input.
@param level used internally
@param instance used internally
@return output from the templates
*/
static function get($templates,$level=0,$instance=0){
static $addedTemplates;
//Allowing multiple calls to the get function at 0 level simultaneously (possibly in display logic)
if($level == 0){
$instance = self::$callInstance;
self::$callInstance++;
}
$addedTemplates[$instance][$level] = array();
if(!is_array($templates)){
$templates = self::parseTemplateString($templates);
}
while($templates){
$template = array_pop($templates);
if(is_array($template)){
if($template[2]){
$output = self::get($template[2],$level + 1,$instance);
if($template[0]){
$output = self::getTemplate($template[0],array('templates'=>$addedTemplates[$instance][$level+1],'input'=>$output));
}
Arrays::addOnKey($template[0],$output,$addedTemplates[$instance][$level]);
}else{
$output = self::getTemplate($template[0]);
Arrays::addOnKey($template[0],$output,$addedTemplates[$instance][$level]);
}
}else{
$output = self::getTemplate($template);
Arrays::addOnKey($template,$output,$addedTemplates[$instance][$level]);
}
$totalOutput .= $output;
}
if($level == 0){
self::$callInstance--;
}
return $totalOutput;
}
///Will potentially add more systax to template strings, but for now just splits template string on commas and spaces
function parseTemplateString($string){
return preg_split('@[\s,]+@',$string);
}
///page css
static $css = array();
///page css put at the end after self::$css
static $lastCss = array();
///page js
static $js = array();
///page js put at the end after self::$js
static $lastJs = array();
///used internally.
/**
@param type indicates whether tag is css, lastCss, js, or lastJs
@param args additional args taken as files. Each file in the passed parameters has the following special syntax:
-starts with http(s): no modding done
-starts with "/": no modding done
-starts with "inline:": file take to be inline css or js. Code is wrapped in tags before output.
-starts with none of the above: file put in path /instanceToken/type/file
*/
static function addTag($type){
if(in_array($type,array('css','lastCss'))){
$main = 'css';
$last = 'lastCss';
}else{
$main = 'js';
$last = 'lastJs';
}
$files = func_get_args();
array_shift($files);
if($files){
$typeArray =& self::$$type;
foreach($files as $file){
//user is adding it, so assume css is at instance unless it starts with http or /
if(preg_match('@^inline:@',$file)){
$typeArray[] = $file;
}else{
Arrays::remove($file,self::$$main);
Arrays::remove($file,self::$$last);
if(substr($file,0,1) != '/' && !preg_match('@^http(s)?:@',$file)){
$file = '/'.Config::$x['urlInstanceFileToken'].'/'.$main.'/'.$file;
}
$typeArray[] = $file;
}
}
}
}
///Adds to the $css array and overrides duplicate elements. Each argument considered css file. See self::addTag for args details
static function addCss(){
$args = func_get_args();
array_unshift($args,'css');
call_user_func_array(array('self','addTag'),$args);
}
///Adds css that will come after the regularly added css
static function addLastCss(){
$args = func_get_args();
array_unshift($args,'lastCss');
call_user_func_array(array('self','addTag'),$args);
}
///Adds to the $js array and overrides duplicate elements. Each argument considered js file. See self::addTag for args details
static function addJs(){
$args = func_get_args();
array_unshift($args,'js');
call_user_func_array(array('self','addTag'),$args);
}
///Adds js that will come after the regularly added js
static function addLastJs(){
$args = func_get_args();
array_unshift($args,'lastJs');
call_user_func_array(array('self','addTag'),$args);
}
///Outputs css style tags with self::$css
/**
@param urlQuery array key=value array to add to the url query part; potentially used to force browser to refresh cached resources
*/
static function getCss($urlQuery=null){
if(self::$css){
foreach(self::$css as $file){
if(preg_match('@^inline:@',$file)){
$css[] = '<style type="text/css">'.substr($file,7).'</style>';
}else{
if($urlQuery){
$file = Tool::appendsUrl($urlQuery,$file);
}
$css[] = '<link rel="stylesheet" type="text/css" href="'.$file.'"/>';
}
}
return implode("\n",$css);
}
}
/// Outputs js script tags with self::$js
/**
@param urlQuery array key=value array to add to the url query part; potentially used to force browser to refresh cached resources
*/
static function getJs($urlQuery=null){
if(self::$js){
foreach(self::$js as $file){
//Intended to be used for plain script
if(preg_match('@^inline:@',$file)){
$js[] = '<script type="text/javascript">'.substr($file,7).'</script>';
}else{
if($urlQuery){
$file = Tool::appendsUrl($urlQuery,$file);
}
$js[] = '<script type="text/javascript" src="'.$file.'"></script>';
}
}
return implode("\n",$js);
}
}
///Accumulated page json
static $json = null;
///prints the self::$json into the tp.json object. Requires the previous declaration of tp js object on the page
static function getJson(){
echo '<script type="text/javascript">tp.json = '.json_encode(self::$json).';</script>';
}
///print out the ajax then quit
/**
@param ajax ajax content to print out
@param type "xml" or "json"
*/
static function ajaxOut($ajax,$type='xml'){
if($type == 'xml'){
header('Content-type: text/xml');
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo $ajax;
}elseif($type == 'json'){
header('Content-type: application/json');
echo $ajax;
}
exit;
}
static function sendFile($path){
//Might potentially remove ".." from path, but it has already been removed by the time the request gets here by server or browser. Still removing for precuation
$path = Files::removeRelative($path);
if(is_file($path)){
$mime = exec('file -ib '.$path);
/* file -ib command does not determine what type of text a text file is. So, use the type that the browser was expecting*/
if(substr($mime,0,5) == 'text/'){
$mime = array_shift(explode(',',$_SERVER['HTTP_ACCEPT']));
}
header('Content-Type: '.$mime);
echo file_get_contents($path);
}elseif(Config::$x['resourceNotFound']){
Config::loadUserFiles(Config::$x['resourceNotFound'],'controllers');
}else{
Debug::throwError('Request handler encountered unresolvable file. Searched at '.$path);
}
exit;
}
}