Beispiel #1
0
<?php

require __DIR__ . '/lib/base.php';
#F3::set('CACHE',TRUE);
F3::set('DEBUG', 1);
F3::set('UI', 'ui/');
F3::set('IMPORTS', 'imports/');
F3::set('FACEBOOK.client_id', '***REMOVED***');
F3::set('FACEBOOK.client_secret', '***REMOVED***');
F3::set('FACEBOOK.redirect_uri', 'http://singleyet.com/login/');
F3::set('FACEBOOK.session_key', F3::resolve('fb_{{@FACEBOOK.client_id}}_access_token'));
F3::set('DB', new DB('mysql:host=localhost;port=3306;dbname=singleyet', 'singleyet', '***REMOVED***'));
F3::call('facebook/facebook.php');
F3::set('Facebook', new Facebook(array('appId' => F3::get('FACEBOOK.client_id'), 'secret' => F3::get('FACEBOOK.client_secret'))));
/////////////////////////////////////////////////
// Status Codes                                //
/////////////////////////////////////////////////
# 1 = Single
# 2 = In a relationship
# 3 = Engaged
# 4 = Married
# 5 = It's complicated
# 6 = In an open relationship
# 7 = Widowed
# 8 = Separated
# 9 = Divorced
# 10 = In a civil union
# 11 = In a domestic relationship
# 12 = Not set
/////////////////////////////////////////////////
# Single = 1, 5, 6, 7, 8, 9, 12                //
Beispiel #2
0
 /**
 		Transmit a file for downloading by HTTP client; If kilobytes per
 		second is specified, output is throttled (bandwidth will not be
 		controlled by default); Return TRUE if successful, FALSE otherwise
 			@param $_file string
 			@param $_kbps integer
 			@public
 	**/
 public static function send($_file, $_kbps = 0)
 {
     $_file = F3::resolve($_file);
     if (!file_exists($_file)) {
         F3::http404();
         return FALSE;
     }
     if (PHP_SAPI != 'cli' && !F3::$global['QUIET'] && !headers_sent()) {
         header(F3::HTTP_Content . ': application/octet-stream');
         header(F3::HTTP_Disposition . ': ' . 'attachment; filename=' . basename($_file));
         header(F3::HTTP_Length . ': ' . filesize($_file));
         F3::httpCache(0);
         ob_end_flush();
     }
     $_max = ini_get('max_execution_time');
     $_ctr = 1;
     $_handle = fopen($_file, 'r');
     $_time = time();
     while (!feof($_handle) && !connection_aborted()) {
         if ($_kbps > 0) {
             // Throttle bandwidth
             $_ctr++;
             $_elapsed = microtime(TRUE) - $_time;
             if ($_ctr / $_kbps > $_elapsed) {
                 usleep(1000000.0 * ($_ctr / $_kbps - $_elapsed));
             }
         }
         // Send 1KiB and reset timer
         echo fread($_handle, 1024);
         set_time_limit($_max);
     }
     fclose($_handle);
     return TRUE;
 }
Beispiel #3
0
 /**
 		Grab file contents
 			@return mixed
 			@param $file string
 			@public
 	**/
 function grab($file)
 {
     $file = F3::resolve($file);
     if (!ini_get('short_open_tag')) {
         $text = preg_replace_callback('/<\\?(?:\\s|\\s*(=))(.+?)\\?>/s', function ($tag) {
             return '<?php ' . ($tag[1] ? 'echo ' : '') . trim($tag[2]) . ' ?>';
         }, $orig = self::getfile($file));
         if (ini_get('allow_url_fopen') && ini_get('allow_url_include')) {
             // Stream wrap
             $file = 'data:text/plain,' . urlencode($text);
         } elseif ($text != $orig) {
             // Save re-tagged file in temporary folder
             if (!is_dir($ref = F3::ref('TEMP'))) {
                 F3::mkdir($ref);
             }
             $temp = $ref . $_SERVER['SERVER_NAME'] . '.tpl.' . F3::hash($file);
             if (!is_file($temp)) {
                 self::mutex(function () use($temp, $text) {
                     file_put_contents($temp, $text);
                 });
             }
             $file = $temp;
         }
     }
     ob_start();
     // Render
     $this->sandbox($file);
     return ob_get_clean();
 }
Beispiel #4
0
 /**
 		Assign value to Axon-mapped field
 			@return boolean
 			@param $_name string
 			@param $_value mixed
 			@public
 	**/
 public function __set($_name, $_value)
 {
     if (array_key_exists($_name, $this->fields)) {
         $this->fields[$_name] = is_string($_value) ? F3::resolve($_value) : $_value;
         if (!is_null($_value)) {
             // Axon is now hydrated
             $this->empty = FALSE;
         }
         return;
     }
     if (array_key_exists($_name, $this->virtual)) {
         trigger_error(self::TEXT_AxonReadOnly);
         return;
     }
     F3::$global['CONTEXT'] = $_name;
     trigger_error(self::TEXT_AxonNotMapped);
 }
Beispiel #5
0
 /**
 		Grab file contents
 			@return mixed
 			@param $file string
 			@public
 	**/
 function grab($file)
 {
     $file = F3::resolve($file);
     ob_start();
     if (!ini_get('short_open_tag')) {
         $text = preg_replace_callback('/<\\?(?:\\s|\\s*(=))(.+?)\\?>/s', function ($tag) {
             return '<?php ' . ($tag[1] ? 'echo ' : '') . trim($tag[2]) . ' ?>';
         }, $orig = file_get_contents($file));
         if (ini_get('allow_url_fopen') && ini_get('allow_url_include')) {
             // Stream wrap
             $file = 'data:text/plain,' . urlencode($text);
         } elseif ($text != $orig) {
             // Save re-tagged file in temporary folder
             if (!is_dir($ref = F3::ref('TEMP'))) {
                 F3::mkdir($ref);
             }
             $temp = $ref . $_SERVER['SERVER_NAME'] . '.tpl.' . F3::hash($file);
             if (!is_file($temp)) {
                 // Create semaphore
                 $hash = 'sem.' . F3::hash($file);
                 $cached = Cache::cached($hash);
                 while ($cached) {
                     // Locked by another process
                     usleep(mt_rand(0, 1000));
                 }
                 Cache::set($hash, TRUE);
                 file_put_contents($temp, $text, LOCK_EX);
                 // Remove semaphore
                 Cache::clear($hash);
             }
             $file = $temp;
         }
     }
     // Render
     $this->sandbox($file);
     return ob_get_clean();
 }
Beispiel #6
0
 /**
 		Generate identicon from an MD5 hash value
 			@param $_hash string
 			@param $_size integer
 			@public
 	**/
 public static function identicon($_hash, $_size = NULL)
 {
     $_blox = F3::$global['IBLOCKS'];
     if (is_null($_size)) {
         $_size = F3::$global['IPIXELS'];
     }
     // Rotatable shapes
     $_dynamic = array(array(0.5, 1, 1, 0, 1, 1), array(0.5, 0, 1, 0, 0.5, 1, 0, 1), array(0.5, 0, 1, 0, 1, 1, 0.5, 1, 1, 0.5), array(0, 0.5, 0.5, 0, 1, 0.5, 0.5, 1, 0.5, 0.5), array(0, 0.5, 1, 0, 1, 1, 0, 1, 1, 0.5), array(1, 0, 1, 1, 0.5, 1, 1, 0.5, 0.5, 0.5), array(0, 0, 1, 0, 1, 0.5, 0, 0, 0.5, 1, 0, 1), array(0, 0, 0.5, 0, 1, 0.5, 0.5, 1, 0, 1, 0.5, 0.5), array(0.5, 0, 0.5, 0.5, 1, 0.5, 1, 1, 0.5, 1, 0.5, 0.5, 0, 0.5), array(0, 0, 1, 0, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1), array(0, 0.5, 0.5, 1, 1, 0.5, 0.5, 0, 1, 0, 1, 1, 0, 1), array(0.5, 0, 1, 0, 1, 1, 0.5, 1, 1, 0.75, 0.5, 0.5, 1, 0.25), array(0, 0.5, 0.5, 0, 0.5, 0.5, 1, 0, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1), array(0, 0, 1, 0, 1, 1, 0, 1, 1, 0.5, 0.5, 0.25, 0.5, 0.75, 0, 0.5, 0.5, 0.25), array(0, 0.5, 0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1), array(0, 0, 1, 0, 0.5, 0.5, 0.5, 0, 0, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1));
     // Fixed shapes (for center sprite)
     $_static = array(array(), array(0, 0, 1, 0, 1, 1, 0, 1), array(0.5, 0, 1, 0.5, 0.5, 1, 0, 0.5), array(0, 0, 1, 0, 1, 1, 0, 1, 0, 0.5, 0.5, 1, 1, 0.5, 0.5, 0, 0, 0.5), array(0.25, 0, 0.75, 0, 0.5, 0.5, 1, 0.25, 1, 0.75, 0.5, 0.5, 0.75, 1, 0.25, 1, 0.5, 0.5, 0, 0.75, 0, 0.25, 0.5, 0.5), array(0, 0, 0.5, 0.25, 1, 0, 0.75, 0.5, 1, 1, 0.5, 0.75, 0, 1, 0.25, 0.5), array(0.33, 0.33, 0.67, 0.33, 0.67, 0.67, 0.33, 0.67), array(0, 0, 0.33, 0, 0.33, 0.33, 0.67, 0.33, 0.67, 0, 1, 0, 1, 0.33, 0.67, 0.33, 0.67, 0.67, 1, 0.67, 1, 1, 0.67, 1, 0.67, 0.67, 0.33, 0.67, 0.33, 1, 0, 1, 0, 0.67, 0.33, 0.67, 0.33, 0.33, 0, 0.33));
     // Parse MD5 hash
     $_hash = F3::resolve($_hash);
     list($_bgR, $_bgG, $_bgB) = self::rgb(F3::$global['BGCOLOR']);
     list($_fgR, $_fgG, $_fgB) = self::rgb(substr($_hash, 0, 6));
     $_shapeC = hexdec($_hash[6]);
     $_angleC = hexdec($_hash[7] % 4);
     $_shapeX = hexdec($_hash[8]);
     for ($_i = 0; $_i < $_blox - 2; $_i++) {
         $_shapeS[$_i] = hexdec($_hash[9 + $_i * 2]);
         $_angleS[$_i] = hexdec($_hash[10 + $_i * 2] % 4);
     }
     // Start with NxN blank slate
     $_identicon = imagecreatetruecolor($_size * $_blox, $_size * $_blox);
     imageantialias($_identicon, TRUE);
     $_bg = imagecolorallocate($_identicon, $_bgR, $_bgG, $_bgB);
     $_fg = imagecolorallocate($_identicon, $_fgR, $_fgG, $_fgB);
     // Generate corner sprites
     $_corner = imagecreatetruecolor($_size, $_size);
     imagefill($_corner, 0, 0, $_bg);
     $_sprite = $_dynamic[$_shapeC];
     for ($_i = 0, $_len = count($_sprite); $_i < $_len; $_i++) {
         $_sprite[$_i] = $_sprite[$_i] * $_size;
     }
     imagefilledpolygon($_corner, $_sprite, $_len / 2, $_fg);
     for ($_i = 0; $_i < $_angleC; $_i++) {
         $_corner = imagerotate($_corner, 90, $_bg);
     }
     // Generate side sprites
     for ($_i = 0; $_i < $_blox - 2; $_i++) {
         $_side[$_i] = imagecreatetruecolor($_size, $_size);
         imagefill($_side[$_i], 0, 0, $_bg);
         $_sprite = $_dynamic[$_shapeS[$_i]];
         for ($_j = 0, $_len = count($_sprite); $_j < $_len; $_j++) {
             $_sprite[$_j] = $_sprite[$_j] * $_size;
         }
         imagefilledpolygon($_side[$_i], $_sprite, $_len / 2, $_fg);
         for ($_j = 0; $_j < $_angleS[$_i]; $_j++) {
             $_side[$_i] = imagerotate($_side[$_i], 90, $_bg);
         }
     }
     // Generate center sprites
     for ($_i = 0; $_i < $_blox - 2; $_i++) {
         $_center[$_i] = imagecreatetruecolor($_size, $_size);
         imagefill($_center[$_i], 0, 0, $_bg);
         $_sprite = $_dynamic[$_shapeX];
         if ($_blox % 2 > 0 && $_i == $_blox - 3) {
             // Odd center sprites
             $_sprite = $_static[$_shapeX % 8];
         }
         $_len = count($_sprite);
         if ($_len) {
             for ($_j = 0; $_j < $_len; $_j++) {
                 $_sprite[$_j] = $_sprite[$_j] * $_size;
             }
             imagefilledpolygon($_center[$_i], $_sprite, $_len / 2, $_fg);
         }
         if ($_i < $_blox - 3) {
             for ($_j = 0; $_j < $_angleS[$_i]; $_j++) {
                 $_center[$_i] = imagerotate($_center[$_i], 90, $_bg);
             }
         }
     }
     // Paste sprites
     for ($_i = 0; $_i < 4; $_i++) {
         imagecopy($_identicon, $_corner, 0, 0, 0, 0, $_size, $_size);
         for ($_j = 0; $_j < $_blox - 2; $_j++) {
             imagecopy($_identicon, $_side[$_j], $_size * ($_j + 1), 0, 0, 0, $_size, $_size);
             for ($_k = $_j; $_k < $_blox - 3 - $_j; $_k++) {
                 imagecopy($_identicon, $_center[$_k], $_size * ($_k + 1), $_size * ($_j + 1), 0, 0, $_size, $_size);
             }
         }
         $_identicon = imagerotate($_identicon, 90, $_bg);
     }
     if ($_blox % 2 > 0) {
         // Paste odd center sprite
         imagecopy($_identicon, $_center[$_blox - 3], $_size * floor($_blox / 2), $_size * floor($_blox / 2), 0, 0, $_size, $_size);
     }
     // Resize
     $_resized = imagecreatetruecolor($_size, $_size);
     imagecopyresampled($_resized, $_identicon, 0, 0, 0, 0, $_size, $_size, $_size * $_blox, $_size * $_blox);
     // Make the background transparent
     imagecolortransparent($_resized, $_bg);
     if (PHP_SAPI != 'cli' && !headers_sent()) {
         header(F3::HTTP_Content . ': image/png');
     }
     imagepng($_resized, NULL, self::PNG_Compress, PNG_NO_FILTER);
 }
Beispiel #7
0
 /**
 		Parse all directives and render HTML/XML template
 			@return mixed
 			@param $_file string
 			@param $_ishtml boolean
 			@param $_path string
 			@public
 	**/
 public static function serve($_file, $_ishtml = TRUE, $_path = NULL)
 {
     if (is_null($_path)) {
         $_path = self::fixSlashes(self::$global['GUI']);
     }
     // Remove <F3::exclude> blocks
     $_text = preg_replace('/<(?:F3:)?exclude>.*?<\\/(?:F3:)?exclude>/is', '', self::embed($_file, $_path));
     if (!preg_match('/<.+>/s', $_text)) {
         // Plain text
         return self::resolve($_text);
     }
     // Initialize XML tree
     $_tree = new XMLTree('1.0', self::$global['ENCODING']);
     // Suppress errors caused by invalid HTML structures
     libxml_use_internal_errors($_ishtml);
     // Populate XML tree
     if ($_ishtml) {
         // HTML template; Keep track of existing tags so those
         // added by libxml can be removed later
         $_tags = array('/<!DOCTYPE\\s+html.*?>\\h*\\v*/is', '/<[\\/]?html.*?>\\h*\\v*/is', '/<[\\/]?head.*?>\\h*\\v*/is', '/<[\\/]?body.*?>\\h*\\v*/is');
         $_undef = array();
         foreach ($_tags as $_regex) {
             if (!preg_match($_regex, $_text)) {
                 $_undef[] = $_regex;
             }
         }
         $_tree->loadHTML($_text);
     } else {
         // XML template
         $_tree->loadXML($_text, LIBXML_COMPACT | LIBXML_NOERROR);
     }
     // Prepare for XML tree traversal
     $_tree->fragment = $_tree->createDocumentFragment();
     $_2ndp = FALSE;
     $_tree->traverse(function () use($_tree, &$_2ndp) {
         $_node =& $_tree->nodeptr;
         $_tag = $_node->tagName;
         $_next = $_node;
         $_parent = $_node->parentNode;
         // Node removal flag
         $_remove = FALSE;
         if ($_tag == 'repeat') {
             // Process <F3:repeat> directive
             $_inner = $_tree->innerHTML($_node);
             if ($_inner) {
                 // Process attributes
                 foreach ($_node->attributes as $_attr) {
                     preg_match('/{?@(\\w+(\\[[^\\]]+\\]|\\.\\w+)*)}?/', $_attr->value, $_cap);
                     $_name = $_attr->name;
                     if (!$_cap[1] || isset($_cap[2]) && $_name != 'group') {
                         // Invalid attribute
                         F3::$global['CONTEXT'] = $_attr->value;
                         trigger_error(F3::TEXT_Attrib);
                         return;
                     } elseif ($_name == 'key') {
                         $_kvar = '/@' . $_cap[1] . '\\b/';
                     } elseif ($_name == 'index') {
                         $_ivar = '/@' . $_cap[1] . '\\b/';
                     } elseif ($_name == 'group') {
                         $_gcap = '@' . $_cap[1];
                         $_gvar = F3::get($_cap[1]);
                     }
                 }
                 if (is_array($_gvar) && count($_gvar)) {
                     ob_start();
                     // Iterate thru group elements
                     foreach (array_keys($_gvar) as $_key) {
                         echo preg_replace($_ivar, $_gcap . '[\'' . $_key . '\']', isset($_kvar) ? preg_replace($_kvar, '\'' . $_key . '\'', $_inner) : $_inner);
                     }
                     $_block = ob_get_contents();
                     ob_end_clean();
                     if (strlen($_block)) {
                         $_tree->fragment->appendXML($_block);
                         // Insert fragment before current node
                         $_next = $_parent->insertBefore($_tree->fragment, $_node);
                     }
                 }
             }
             $_remove = TRUE;
         } elseif ($_tag == 'check' && !$_2ndp) {
             // Found <F3:check> directive
             $_2ndp = TRUE;
         } elseif (strpos($_tag, '-')) {
             // Process custom template directive
             list($_class, $_method) = explode('-', $_tag);
             // Invoke template directive handler
             call_user_func(array($_class, $_method), $_tree);
             $_remove = TRUE;
         }
         if ($_remove) {
             // Find next node
             if ($_node->isSameNode($_next)) {
                 $_next = $_node->nextSibling ? $_node->nextSibling : $_parent;
             }
             // Remove current node
             $_parent->removeChild($_node);
             // Replace with next node
             $_node = $_next;
         }
     });
     if ($_2ndp) {
         // Second pass; Template contains <F3:check> directive
         $_tree->traverse(function () use($_tree) {
             $_node =& $_tree->nodeptr;
             $_parent = $_node->parentNode;
             $_tag = $_node->tagName;
             // Process <F3:check> directive
             if ($_tag == 'check') {
                 $_cond = var_export((bool) F3::resolve(rawurldecode($_node->getAttribute('if'))), TRUE);
                 ob_start();
                 foreach ($_node->childNodes as $_child) {
                     if ($_child->nodeType == XML_ELEMENT_NODE && preg_match('/' . $_cond . '/i', $_child->tagName)) {
                         echo $_tree->innerHTML($_child) ?: '';
                     }
                 }
                 $_block = ob_get_contents();
                 ob_end_clean();
                 if (strlen($_block)) {
                     $_tree->fragment->appendXML($_block);
                     $_parent->insertBefore($_tree->fragment, $_node);
                 }
                 // Remove current node
                 $_parent->removeChild($_node);
                 // Re-process parent node
                 $_node = $_parent;
             }
         });
     }
     if ($_ishtml) {
         // Fix empty HTML tags
         $_text = preg_replace('/<((?:' . self::HTML_Tags . ')\\b.*?)\\/?>/is', '<$1/>', self::resolve(rawurldecode($_tree->saveHTML())));
         // Remove tags inserted by libxml
         foreach ($_undef as $_regex) {
             $_text = preg_replace($_regex, '', $_text);
         }
     } else {
         $_text = self::xmlEncode(self::resolve(rawurldecode($_tree->saveXML())), TRUE);
     }
     return $_text;
 }
Beispiel #8
0
 /**
 		Bind values to parameters in SQL statement(s) and execute
 			@return mixed
 			@param $_cmds mixed
 			@param $_bind mixed
 			@param $_id string
 			@param $_ttl integer
 			@public
 	**/
 public static function sqlBind($_cmds, $_bind = NULL, $_id = 'DB', $_ttl = 0)
 {
     $_db =& F3::$global[$_id];
     // Connect to database once
     if (!$_db || !$_db['dsn']) {
         // Can't connect without a DSN!
         trigger_error(self::TEXT_DBConnect);
         return;
     }
     if (!isset($_db['pdo'])) {
         $_ext = 'pdo_' . stristr($_db['dsn'], ':', TRUE);
         if (!in_array($_ext, get_loaded_extensions())) {
             // PHP extension not activated
             F3::$global['CONTEXT'] = $_ext;
             trigger_error(F3::TEXT_PHPExt);
             return;
         }
         try {
             $_db['pdo'] = new PDO($_db['dsn'], $_db['user'], $_db['password'], isset($_db['options']) ?: array(PDO::ATTR_EMULATE_PREPARES => FALSE));
         } catch (Exception $_xcpt) {
         }
         if (!isset($_db['pdo'])) {
             // Unable to connect
             trigger_error(self::TEXT_DBConnect);
             return;
         }
         // Define connection attributes
         $_attrs = explode('|', 'AUTOCOMMIT|ERRMODE|CASE|CLIENT_VERSION|CONNECTION_STATUS|' . 'PERSISTENT|PREFETCH|SERVER_INFO|SERVER_VERSION|TIMEOUT');
         // Save attributes in DB global variable
         foreach ($_attrs as $_attr) {
             // Suppress warning if PDO driver doesn't support attribute
             $_val = @$_db['pdo']->getAttribute(constant('PDO::ATTR_' . $_attr));
             if ($_val) {
                 $_db['attributes'][$_attr] = $_val;
             }
         }
     }
     if (!is_array($_cmds)) {
         // Convert to array to prevent code duplication
         $_cmds = array($_cmds);
     }
     // Remove empty elements
     $_cmds = array_diff($_cmds, array(NULL));
     $_db['result'] = NULL;
     if (count($_cmds) > 1) {
         // More than one SQL statement specified
         $_db['pdo']->beginTransaction();
     }
     foreach ($_cmds as $_cmd) {
         if (F3::$global['ERROR']) {
             break;
         }
         $_cmd = F3::resolve($_cmd);
         if ($_ttl) {
             // Cache results
             self::sqlCache($_cmd, $_bind, $_id, $_ttl);
         } else {
             // Execute SQL statement(s)
             self::sqlExec($_cmd, $_bind, $_id);
         }
     }
     if (count($_cmds) > 1) {
         $_func = F3::$global['ERROR'] ? 'rollBack' : 'commit';
         call_user_func(array($_db['pdo'], $_func));
     }
     return $_db['result'];
 }
Beispiel #9
0
 /**
 		Parse all directives and render HTML/XML template
 			@return mixed
 			@param $_file string
 			@param $_ishtml boolean
 			@param $_path string
 			@public
 	**/
 public static function serve($_file, $_ishtml = TRUE, $_path = NULL)
 {
     if (is_null($_path)) {
         $_path = self::fixSlashes(self::$global['GUI']);
     }
     // Remove <F3::exclude> blocks
     $_text = preg_replace('/<(?:F3:)?exclude>.*?<\\/(?:F3:)?exclude>/is', '', self::embed($_file, $_path));
     if (preg_match('/<.+>/s', $_text)) {
         // Initialize XML tree
         $_tree = new XMLTree('1.0', self::$global['ENCODING']);
         // Suppress errors caused by invalid HTML structures
         libxml_use_internal_errors($_ishtml);
         // Populate XML tree
         if ($_ishtml) {
             // HTML template; Remember defined tags
             $_deftags = array('/<!DOCTYPE\\s+html.*?>\\h*\\v*/is' => FALSE, '/<[\\/]?html.*?>\\h*\\v*/is' => FALSE, '/<[\\/]?head.*?>\\h*\\v*/is' => FALSE, '/<[\\/]?body.*?>\\h*\\v*/is' => FALSE);
             foreach ($_deftags as $_regex => &$_tag) {
                 $_tag = preg_match($_regex, $_text);
             }
             // Destroy reference
             unset($_tag);
             $_tree->loadHTML($_text);
         } else {
             // XML template
             $_tree->loadXML($_text, LIBXML_COMPACT | LIBXML_NOERROR);
         }
         // Prepare for XML tree traversal
         $_tree->fragment = $_tree->createDocumentFragment();
         $_2ndp = FALSE;
         $_tree->traverse(function () use($_tree, &$_2ndp) {
             $_node =& $_tree->nodeptr;
             $_tag = $_node->tagName;
             $_next = $_node;
             $_parent = $_node->parentNode;
             // Node removal flag
             $_remove = FALSE;
             if ($_tag == 'repeat') {
                 // Process <F3:repeat> directive
                 $_inner = $_tree->innerHTML($_node);
                 if ($_inner) {
                     foreach ($_node->attributes as $_attr) {
                         preg_match('/\\{*@(\\w+\\b(\\[[^\\]]+\\]|\\.\\w+\\b)*)\\}*/', $_attr->value, $_cap);
                         if (!$_cap[1] || isset($_cap[2]) && $_attr->name != 'group') {
                             // Invalid attribute
                             F3::$global['CONTEXT'] = $_attr->value;
                             trigger_error(F3::TEXT_Attrib);
                             return;
                         }
                         if ($_attr->name == 'key') {
                             $_kvar = '/@' . $_cap[1] . '\\b/';
                         } elseif ($_attr->name == 'index') {
                             $_ivar = '/@' . $_cap[1] . '\\b/';
                         } elseif ($_attr->name == 'group') {
                             $_gcap = '@' . $_cap[1];
                             $_gvar = F3::get($_cap[1]);
                         }
                     }
                     if (is_array($_gvar) && count($_gvar)) {
                         $_block = '';
                         // Iterate thru group elements
                         foreach (array_keys($_gvar) as $_key) {
                             $_block .= preg_replace($_ivar, $_gcap . '[\'' . $_key . '\']', isset($_kvar) ? preg_replace($_kvar, var_export($_key, TRUE), $_inner) : $_inner);
                         }
                         if (isset($_block[0])) {
                             $_tree->fragment->appendXML($_block);
                             // Insert fragment before current node
                             $_next = $_parent->insertBefore($_tree->fragment, $_node);
                         }
                     }
                 }
                 $_remove = TRUE;
             } elseif ($_tag == 'check' && !$_2ndp) {
                 // Found <F3:check> directive
                 $_2ndp = TRUE;
             } elseif (strpos($_tag, '-')) {
                 // Process custom template directive
                 list($_class, $_method) = explode('-', $_tag);
                 $_found = FALSE;
                 if (!class_exists($_class, FALSE)) {
                     foreach (explode('|', F3::$global['AUTOLOAD']) as $_auto) {
                         $_file = $_auto . $_class . '.php';
                         // Case-insensitive check for file presence
                         $_glob = glob(dirname($_file) . '/*.php');
                         $_fkey = array_search(strtolower($_file), array_map('strtolower', $_glob));
                         if (is_int($_fkey)) {
                             include $_glob[$_fkey];
                             if (method_exists($_class, 'onLoad')) {
                                 call_user_func(array($_class, 'onLoad'));
                             }
                             $_found = TRUE;
                             break;
                         }
                     }
                 } else {
                     $_found = TRUE;
                 }
                 if ($_found) {
                     // Invoke template directive handler
                     call_user_func(array($_class, $_method), $_tree);
                     $_remove = TRUE;
                 }
             }
             if ($_remove) {
                 // Find next node
                 if ($_node->isSameNode($_next)) {
                     $_next = $_node->nextSibling ? $_node->nextSibling : $_parent;
                 }
                 // Remove current node
                 $_parent->removeChild($_node);
                 // Replace with next node
                 $_node = $_next;
             }
         });
         if ($_2ndp) {
             // Second pass; Template contains <F3:check> directive
             $_tree->traverse(function () use($_tree) {
                 $_node =& $_tree->nodeptr;
                 $_parent = $_node->parentNode;
                 $_tag = $_node->tagName;
                 // Process <F3:check> directive
                 if ($_tag == 'check') {
                     $_cond = var_export((bool) F3::resolve(rawurldecode($_node->getAttribute('if'))), TRUE);
                     $_block = '';
                     foreach ($_node->childNodes as $_child) {
                         if ($_child->nodeType != XML_TEXT_NODE && $_child->tagName == $_cond) {
                             $_inner = $_tree->innerHTML($_child);
                             if ($_inner) {
                                 // Replacement
                                 $_block .= $_inner;
                             }
                         }
                     }
                     if (isset($_block[0])) {
                         $_tree->fragment->appendXML($_block);
                         $_parent->insertBefore($_tree->fragment, $_node);
                     }
                     // Remove current node
                     $_parent->removeChild($_node);
                     // Re-process parent node
                     $_node = $_parent;
                 }
             });
         }
         $_text = self::resolve(rawurldecode($_ishtml ? $_tree->saveHTML() : $_tree->saveXML()));
         if ($_ishtml) {
             // Fix empty HTML tags
             $_text = preg_replace('/<((?:area|base|br|col|frame|hr|img|input|' . 'isindex|link|meta|param).*?)\\/?>/is', '<$1/>', $_text);
             // Remove tags inserted by libxml
             foreach ($_deftags as $_regex => $_tag) {
                 if (!$_tag) {
                     $_text = preg_replace($_regex, '', $_text);
                 }
             }
         } else {
             $_text = self::xmlEncode($_text, TRUE);
         }
         unset($_tree);
     } else {
         // Plain text
         $_text = self::resolve($_text);
     }
     // Remove control characters except whitespaces
     return preg_replace('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/', '', $_text);
 }