Changeset 455

Show
Ignore:
Timestamp:
09/01/2007 12:19:19 AM (15 months ago)
Author:
Shadowhand
Message:

A few small changes:

  • Removed Core language file
  • Added Exceptions language file
  • Added Kohana_Exception class
  • Updated some Kohana:: functions to use Exceptions, and catch them
  • Updated Kohana::find_file() to not attach the EXT extension on filenames (4th parameter, boolean)
  • Updated View class to allow serving of GIF, JPG, and PNG files through fpassthru() (with correct headers of course!)

A few small changes to the User Guide:

  • Updated menu effects: collapses the entire submenu together, rather than "stacking" the effects, removed "sticky" window.onScroll function, changed the loading panel to use an AJAX spinner, rather than text, added a menu hovering fade
  • Updated layout styles: changed the background color be light, updated menu colors to match, changed to a full-width design
Location:
trunk
Files:
2 added
1 removed
7 modified

Legend:

Unmodified
Added
Removed
  • trunk/modules/user_guide/controllers/user_guide.php

    r450 r455  
    7272        public function _tags() 
    7373        { 
    74                 Kohana::$output = preg_replace_callback('!<(benchmark|event|file|definition)>.+?</[^>]+>!', array($this, '_tag_update'), Kohana::$output); 
     74                self::$output = preg_replace_callback('!<(benchmark|event|file|definition)>.+?</[^>]+>!', array($this, '_tag_update'), self::$output); 
    7575        } 
    7676 
     
    9999                header('Content-type: text/javascript'); 
    100100 
    101                 $this->_media('js', preg_replace('/\.js$/', '', $filename)); 
     101                $this->_media('js', preg_replace('/\.js$/D', '', $filename)); 
    102102        } 
    103103 
     
    106106                header('Content-type: text/css'); 
    107107 
    108                 $this->_media('css', preg_replace('/\.css$/', '', $filename)); 
     108                $this->_media('css', preg_replace('/\.css$/D', '', $filename)); 
    109109        } 
    110110 
     
    118118                        $this->load->view('user_guide/'.$type.'/'.$filename)->render(TRUE); 
    119119                } 
    120                 catch (file_not_found $exception) 
     120                catch (Kohana_Exception $exception) 
    121121                { 
    122122                        print '/* script not found */'; 
  • trunk/modules/user_guide/views/user_guide/css/layout.php

    r452 r455  
    99Default Styles 
    1010*/ 
    11 body { background: #111; color: #111; font-size: 82%; line-height: 140%; font-family: "Lucida Grande", Arial, Verdana, sans-serif; } 
     11body { background: #f7fbf1; color: #111; font-size: 82%; line-height: 140%; font-family: "Lucida Grande", Arial, Verdana, sans-serif; } 
    1212a { color: #449; text-decoration: none; } 
    1313h1 { border-bottom: solid 1px #999; font-size: 2.5em; text-align: center; color: #b43f11; } 
     
    2222Template Styles 
    2323*/ 
    24 #container { position: relative; width: 70%; min-width: 30em; max-width: 60em; margin: 0 auto; } 
    25 #menu { position: absolute; right: -10.1em; width: 10em; padding: 0.2em 0; } 
     24#container { position: relative; margin: 0 12em 2em 1em; border: solid 0 #e7f5d7; border-width: 0 0.2em 0.3em 0.2em; } 
     25#menu { position: fixed; top: 0; right: 1.5em; width: 10em; padding: 0.2em 0; } 
    2626#menu ul { margin: 0 0.2em; list-style: none; color: #b43f11; } 
    27 #menu li ul { padding: 0.1em 0; margin-right: 0; font-size: 0.8em; color: #444; } 
     27#menu li.first { font-size: 1.4em; padding-top: 0.2em; } 
    2828#menu li.active li.lite { color: #b43f11; } 
    29 #menu li ul a { color: #666; } 
    30 #menu ul a:hover { text-decoration: underline; color: #45721d; } 
    31 #menu li.first { font-size: 1.2em; } 
     29#menu li ul { padding: 0.1em 0; margin-right: 0; font-size: 0.7em; color: #444; } 
     30#menu li ul a { color: #333; } 
     31#menu li ul a:hover { font-weight: bold; color: #2f4f14; } 
    3232#menu li span { cursor: pointer; } 
    33 #body, #footer { padding: 0 0.5em; background: #eee; } 
     33#body { padding: 0.3em 1em; background: #fff; } 
    3434#body ol, #body ul { margin: 1em; margin-left: 2.5em; } 
    3535#body ol { list-style: decimal; } 
    3636#body ul { list-style: circle; } 
    3737#body li { padding: 0.1em 0; } 
    38 #loading { position: absolute; top: 0; right: 0; z-index: 9999; padding: 0.5em 2em; background: #fff; border: solid 0 #aaa; border-width: 0 0 1px 1px; text-align: center; text-transform: uppercase; font-size: 0.7em; } 
     38#footer { padding: 0 1em; background: #fff; line-height: 2em; } 
     39#loading { position: absolute; top: 0; right: 0; z-index: 9999; width: 32px; height: 32px; background: transparent url(spinner.gif) center center no-repeat; } 
    3940#loading span { color: #400; } 
    4041/* 
  • trunk/modules/user_guide/views/user_guide/js/effects.php

    r452 r455  
    11// $Id$ 
    22$(document).ready(function(){ 
     3        // Menu opacity hover effect, much fancy pants! 
     4        $('#menu').css('opacity', 0.75).hover(function(){ 
     5                $(this).fadeTo(100, 0.9); 
     6        }, function(){ 
     7                $(this).fadeTo(300, 0.7) 
     8        }); 
    39        // Append the AJAX loader 
    4         $('#container').append('<div id="loading"><span>&hellip;loading</span></div>'); 
    5         // To prevent extra querying 
    6         Kohana.loading = $('#loading').hide().css('opacity', 0.85); 
    7         // Make the menu sticky 
    8         $(window).scroll(function(){ 
    9                 $('#menu').css('top', $(window).scrollTop()); 
    10         }); 
    11         // Apply menu effects 
    12         $('#menu li.first li') 
    13         .hide()   // Hide these li's 
    14         .parent() // Parent ul 
    15         .parent() // Parent li 
    16         .click(function(){ 
    17                         // Hover affects the ul > li inside of this li 
    18                         var curr = $('#menu li.active'); 
    19                         var self = $(this); 
    20                         // Clicks to the same element will do nothing 
    21                         if (self.is('.active') == false) 
    22                         { 
    23                                 // Hide the current elements 
    24                                 curr.removeClass('active') 
    25                                 .find('ul > li') 
    26                                 .stack('animate', {height: 'hide', width: 'hide'}, 100); 
    27                                 // Show the new elements 
    28                                 self.addClass('active') 
    29                                 .find('ul > li') 
    30                                 .stack('animate', {height: 'show', width: 'show'}, 100); 
    31                         } 
     10        $('#container').append('<div id="loading">&nbsp;</div>'); 
     11        // To prevent extra querying, add the loading element to Kohana after hiding it 
     12        Kohana.loading = $('#loading').hide(); 
     13        // Apply menu sliding effect 
     14        $('#menu li.first').click(function(){ 
     15                // Define the current menu and the clicked menu 
     16                var curr = $('#menu li.active'); 
     17                var self = $(this); 
     18                // Clicks to the same menu will do nothing 
     19                if (self.is('.active') == false) 
     20                { 
     21                        // Hide the current elements 
     22                        curr.removeClass('active') 
     23                        .find('ul') 
     24                        .slideUp(250); 
     25                        // Show the new elements 
     26                        self.addClass('active') 
     27                        .find('ul') 
     28                        .slideDown(250); 
    3229                } 
    33         ). 
    34         // Find links in the menu 
    35         find('a') 
    36         .click(function(){ 
     30        }) 
     31        // Find and hide the sub menus that are not in the active menu 
     32        .not('.active').find('ul').hide(); 
     33        // Add AJAX functionality to the menu links 
     34        $('#menu a').click(function(){ 
    3735                // Show loading 
    3836                Kohana.toggleLoading(true); 
    3937                // Fetch the current link 
    4038                var link = $(this); 
    41                 // Make AJAX request 
     39                // Make AJAX request, using ?ajax=true 
    4240                $.get(link.attr('href'), {ajax: 'true'}, function(data) { 
    43                         // Add the "lite" class to the current link 
    44                         $('#menu li.active li.lite').removeClass('lite'); 
     41                        // Add the hilight class to the current link 
     42                        $('#menu ul li.lite').removeClass('lite'); 
    4543                        link.parent().addClass('lite'); 
    4644                        // Load new AJAX content 
     
    5149                return false; 
    5250        }); 
    53         // Show the active menu 
    54         $('#menu li.active ul > li') 
    55         .stack('animate', {height: 'show', width: 'show'}, 100); 
    5651}); 
    5752// Special Kohana functions 
     
    6257                // If we are waiting for an animation, retry in 5ms 
    6358                if (Kohana.waiting == true) { 
    64                         setTimeout('Kohana.toggleLoading('+on+')', 100); 
     59                        setTimeout('Kohana.toggleLoading('+on+')', 20); 
    6560                        return false; 
    6661                } 
     
    6863                Kohana.waiting = true; 
    6964                if (on == true) { // Show loading 
    70                         this.loading.slideDown(200, function() { 
     65                        this.loading.slideDown(250, function() { 
    7166                                Kohana.waiting = false; 
    7267                        }); 
    7368                } else { // Hide loading 
    74                         this.loading.slideUp(200, function() { 
     69                        this.loading.slideUp(250, function() { 
    7570                                Kohana.waiting = false; 
    7671                        }); 
  • trunk/system/core/Kohana.php

    r448 r455  
    2727class Kohana { 
    2828 
    29         public static $buffer_level = 0;       // Ouput buffering level 
    30         public static $error_types  = array(); // Human readable error types 
    31         public static $registry     = array(); // Library registery 
    32  
    33         public static $output = ''; 
    34  
    35         private static $instance = FALSE;   // Controller instance 
     29        // The singleton instance of the controller 
     30        private static $instance = NULL; 
     31 
     32        // Human readable error types, for PHP errors 
     33        private static $error_types = array(); 
     34 
     35        // Error strings that will be displayed by unhandled exceptions 
     36        private static $error_strings = array(); 
     37 
     38        // Library registery, to prevent multiple loads of libraries 
     39        private static $libraries = array(); 
     40 
     41        // Ouput buffering level 
     42        private static $buffer_level = 0; 
     43 
     44        // The final output that will displayed by Kohana 
     45        // This variable is protected, so that the controller can overload it 
     46        protected static $output = ''; 
    3647 
    3748        /** 
     
    4657        public function __construct() 
    4758        { 
    48                 if (self::$instance == FALSE) 
    49                 { 
    50                         self::$instance = $this; 
    51                 } 
    52                 else 
    53                 { 
    54                         trigger_error('<em>&#8220;There can be only one [instance of Kohana]!&#8220;</em>', E_USER_ERROR); 
    55                 } 
     59                if (is_object(self::$instance)) 
     60                        throw new Exception('there_can_be_only_one'); 
     61 
     62                self::$instance = $this; 
    5663        } 
    5764 
     
    7986        { 
    8087                static $run; 
     88 
    8189                // This function can only be run once 
    8290                if ($run === TRUE) return; 
     
    256264        public static function exception_handler($exception) 
    257265        { 
     266 
    258267                /** 
    259268                 * @todo This needs to choose a i18n message based on the type of exception + message 
     
    339348        public static function load_class($class) 
    340349        { 
    341                 if (isset(self::$registry[$class])) 
    342                 { 
    343                         return self::$registry[$class]; 
     350                if (isset(self::$libraries[$class])) 
     351                { 
     352                        return self::$libraries[$class]; 
    344353                } 
    345354 
    346355                if ($class == 'Controller') 
    347356                { 
    348                         self::$registry[$class] = TRUE; 
     357                        self::$libraries[$class] = TRUE; 
    349358                } 
    350359                else 
    351360                { 
    352                         self::$registry[$class] = new $class(); 
    353                 } 
    354  
    355                 return self::$registry[$class]; 
     361                        self::$libraries[$class] = new $class(); 
     362                } 
     363 
     364                return self::$libraries[$class]; 
    356365        } 
    357366 
     
    365374         * @return mixed 
    366375         */ 
    367         public static function find_file($directory, $filename, $required = FALSE) 
     376        public static function find_file($directory, $filename, $required = FALSE, $ext = FALSE) 
    368377        { 
    369378                static $found = array(); 
    370379 
    371380                $search = $directory.DIRECTORY_SEPARATOR.$filename; 
    372  
    373                 if (isset($found[$search])) 
    374                         return $found[$search]; 
    375  
    376                 $paths = Config::include_paths(); 
     381                $hash   = md5($search); 
     382 
     383                if (isset($found[$hash])) 
     384                        return $found[$hash]; 
    377385 
    378386                if ($directory == 'config' OR $directory == 'i18n') 
    379387                { 
     388                        $fnd = array(); 
     389 
    380390                        // Search from SYSPATH up 
    381                         $paths = array_reverse($paths); 
    382  
    383                         // Create a braced list for glob 
    384                         $paths = '{'.implode(',', $paths).'}'; 
    385  
    386                         // Find all matching files, without sorting 
    387                         if (($files = glob($paths.$search.EXT, GLOB_BRACE + GLOB_NOSORT)) != FALSE) 
    388                         { 
    389                                 return $found[$search] = $files; 
    390                         } 
     391                        foreach(array_reverse(Config::include_paths()) as $path) 
     392                        { 
     393                                if (is_file($path.$search.EXT)) $fnd[] = $path.$search.EXT; 
     394                        } 
     395 
     396                        // If required and nothing was found, throw an exception 
     397                        if ($required == TRUE AND $fnd === array()) 
     398                                throw new Kohana_Exception('resource_not_found', $directory, $filename); 
     399 
     400                        return $found[$hash] = $fnd; 
    391401                } 
    392402                else 
    393403                { 
     404                        // Users can define their own extensions, .css, etc 
     405                        $ext = ($ext == FALSE) ? EXT : ''; 
     406 
    394407                        // Find the file and return it's filename 
    395                         foreach ($paths as $path) 
    396                         { 
    397                                 if (is_file($path.$search.EXT)) 
     408                        foreach (Config::include_paths() as $path) 
     409                        { 
     410                                if (is_file($path.$search.$ext)) 
    398411                                { 
    399                                         return $found[$search] = $path.$search.EXT; 
     412                                        return $found[$hash] = $path.$search.$ext; 
    400413                                } 
    401414                        } 
    402                 } 
    403  
    404                 // If the function gets this far in execution, it means that no file 
    405                 // was located. If the file was flagged as required, return an error. 
    406                 ($required === TRUE) and trigger_error 
    407                 ( 
    408                         'Unable to locate the requested file, <tt>'.$filename.EXT.'</tt>', 
    409                         E_USER_ERROR 
    410                 ); 
    411  
    412                 // File is not required, return FALSE 
    413                 return FALSE; 
     415 
     416                        // If the file is required, throw an exception 
     417                        if ($required == TRUE) 
     418                                throw new Kohana_Exception('resource_not_found', $directory, $filename); 
     419 
     420                        return $found[$hash] = FALSE; 
     421                } 
     422        } 
     423 
     424        public static function lang($type = FALSE, $name = TRUE) 
     425        { 
     426                static $found = array(); 
     427 
     428                // Throw an exception if the type is not specified 
     429                if ($type == FALSE) 
     430                        throw new Kohana_Exception('A language message type must be used when calling <code>Kohan::lang()</code>.'); 
     431 
     432                if ( ! isset($found[$type])) 
     433                { 
     434                        // Messages from this file 
     435                        $messages = array(); 
     436 
     437                        // The name of the file to search for 
     438                        $filename = Config::item('core.locale').DIRECTORY_SEPARATOR.ucfirst($type); 
     439 
     440                        // Loop through the files and include each one, so SYSPATH files 
     441                        // can be overloaded by more localized files 
     442                        foreach(array_reverse(self::find_file('i18n', $filename)) as $filename) 
     443                        { 
     444                                include $filename; 
     445 
     446                                // Merge in configuration 
     447                                if (isset($lang) AND is_array($lang)) 
     448                                { 
     449                                        $messages = array_merge($messages, $lang); 
     450                                } 
     451                        } 
     452 
     453                        // Cache the type 
     454                        $found[$type] = $messages; 
     455                } 
     456 
     457                // Return something 
     458                if ($name === TRUE) 
     459                { 
     460                        return $found[$type]; 
     461                } 
     462                elseif ($found[$type] == FALSE OR ! isset($found[$type][$name])) 
     463                { 
     464                        return FALSE; 
     465                } 
     466                else 
     467                { 
     468                        return $found[$type][$name]; 
     469                } 
    414470        } 
    415471 
     
    460516 
    461517} // End Kohana class 
     518 
     519class Kohana_Exception extends Exception { 
     520 
     521        protected $message = 'Unknown Exception: '; 
     522 
     523        protected $file = ''; 
     524        protected $line = 0; 
     525        protected $code = 0; 
     526 
     527 
     528        function __construct($error) 
     529        { 
     530                // Fetch the error message 
     531                $message = Kohana::lang('exceptions', $error); 
     532 
     533                // Handle error messages that are not set 
     534                if ($message == '') 
     535                { 
     536                        $this->message .= $error; 
     537                } 
     538                else 
     539                { 
     540                        // Add in the args if necessary, removing the $error arg 
     541                        if (count($args = array_slice(func_get_args(), 1)) > 0) 
     542                        { 
     543                                $this->message = vsprintf($message, $args); 
     544                        } 
     545                        else 
     546                        { 
     547                                $this->message = $message; 
     548                        } 
     549                } 
     550        } 
     551 
     552        public function __toString() 
     553        { 
     554                return $this->message; 
     555        } 
     556 
     557} // End Kohana Exception Class 
  • trunk/system/libraries/Controller.php

    r450 r455  
    2222        public function kohana_include_view($kohana_view_filename, $kohana_input_data) 
    2323        { 
     24                if ($kohana_view_filename == '') return; 
     25 
    2426                // Buffering on 
    2527                ob_start(); 
  • trunk/system/libraries/View.php

    r447 r455  
    3333class View_Core { 
    3434 
    35         private $kohana_view_filename; 
    36         private $kohana_renderer = ''; 
     35        private $kohana_filename  = FALSE; 
     36        private $kohana_renderer  = FALSE; 
     37        private $kohana_filetype  = FALSE; 
     38        private $kohana_protected = FALSE; 
    3739        private $data = array(); 
    3840 
    3941        public function __construct($name, $data = NULL) 
    4042        { 
    41                 $this->kohana_view_filename = Kohana::find_file('views', $name, TRUE); 
     43                if (preg_match('/\.(gif|jpg|png|swf)$/Di', $name, $type)) 
     44                { 
     45                        $type = $type[1]; 
     46                        $this->kohana_filename = Kohana::find_file('views', $name, TRUE, $type); 
     47                        $this->kohana_filetype = image_type_to_mime_type(exif_imagetype($this->kohana_filename)); 
     48                } 
     49                else 
     50                { 
     51                        $this->kohana_filename = Kohana::find_file('views', $name, TRUE); 
     52                } 
    4253 
    4354                // Preload data 
     
    5970        public function __set($name, $value) 
    6071        { 
    61                 if ($name == 'kohana_view_filename' AND $this->kohana_view_filename == FALSE) 
     72                $protected = array('kohana_filename', 'kohana_renderer', 'kohana_filetype'); 
     73 
     74                if (in_array($name,  $protected) AND $this->$name === FALSE) 
    6275                { 
    63                         $this->kohana_view_filename = $value; 
     76                        $this->$name = $value; 
    6477                } 
    6578                else 
     
    8497        public function render($print = FALSE, $callback = FALSE) 
    8598        { 
    86                 $output = Kohana::instance()->kohana_include_view($this->kohana_view_filename, $this->data); 
     99                if ($this->kohana_filetype == FALSE) 
     100                { 
     101                        $output = Kohana::instance()->kohana_include_view($this->kohana_filename, $this->data); 
    87102 
    88                 if ($callback != FALSE) 
    89                 { 
    90                         $output = Kohana::callback($callback, $output); 
    91                 } 
     103                        if ($callback != FALSE) 
     104                        { 
     105                                $output = Kohana::callback($callback, $output); 
     106                        } 
    92107 
    93                 if ($print == TRUE) 
    94                 { 
    95                         print $output; 
     108                        if ($print == TRUE) 
     109                        { 
     110 
     111                                print $output; 
     112                        } 
     113                        else 
     114                        { 
     115                                return $output; 
     116                        } 
    96117                } 
    97118                else 
    98119                { 
    99                         return $output; 
     120                        header('Content-type: '.$this->kohana_filetype); 
     121 
     122                        if ($print == TRUE) 
     123                        { 
     124                                fpassthru(($file = fopen($this->kohana_filename, 'rb'))); 
     125                                fclose($file); 
     126                        } 
     127                        else 
     128                        { 
     129                                return file_get_contents($this->kohana_filename); 
     130                        } 
    100131                } 
    101132        } 
  • trunk/system/views/kohana_holiday.php

    r447 r455  
    1 <?php 
    2 header('Content-type: image/gif'); 
    3 exit(base64_decode('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