protected function getJavascriptCallback()
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
     $config = $this->getConfig();
     $arguments = array($this->owner->getJavascriptValue());
     foreach ($config['arguments'] as $arg) {
         $arguments[] = HTML_QuickForm2_JavascriptBuilder::encode($arg);
     }
     return "function() { return !" . $this->findJavascriptName() . "(" . implode(', ', $arguments) . "); }";
 }
Esempio n. 2
0
 /**
  * Returns the javascript builder object
  *
  * @return   HTML_QuickForm2_JavascriptBuilder
  */
 public function getJavascriptBuilder()
 {
     if (empty($this->jsBuilder)) {
         if (!class_exists('HTML_QuickForm2_JavascriptBuilder')) {
             HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
         }
         $this->jsBuilder = new HTML_QuickForm2_JavascriptBuilder();
     }
     return $this->jsBuilder;
 }
Esempio n. 3
0
 /**
  * Class constructor
  *
  * Possible keys in $data array are:
  *  - 'messageProvider': an instance of a class implementing
  *    HTML_QuickForm2_MessageProvider interface, this will be used to get
  *    localized error messages. Default will be used if not given.
  *  - 'language': language to display error messages in, will be passed to
  *    message provider.
  *  - 'errorMessages': (DEPRECATED, use messageProvider) an array of error
  *    messages with the following format
  *    <pre>
  *      'language code 1' => array(
  *         UPLOAD_ERR_... => 'message',
  *         ...
  *         UPLOAD_ERR_... => 'message'
  *      ),
  *      ...
  *      'language code N' => array(
  *         ...
  *      )
  *    </pre>
  *    Note that error messages for UPLOAD_ERR_INI_SIZE and UPLOAD_ERR_FORM_SIZE
  *    may contain '%d' placeholders that will be automatically replaced by the
  *    appropriate size limits. Note also that you don't need to provide messages
  *    for every possible error code in the arrays, you may e.g. override just
  *    one error message for a particular language.
  *
  * @param    string  Element name
  * @param    mixed   Attributes (either a string or an array)
  * @param    array   Data used to set up error messages for PHP's file
  *                   upload errors.
  */
 public function __construct($name = null, $attributes = null, array $data = array())
 {
     // Using deprecated 'errorMessages' key, let's keep this separate to remove later
     if (isset($data['errorMessages'])) {
         HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_MessageProvider_Default');
         $this->messageProvider = HTML_QuickForm2_MessageProvider_Default::getInstance();
         // neither array_merge() nor array_merge_recursive will do
         foreach ($data['errorMessages'] as $lang => $ary) {
             foreach ($ary as $code => $message) {
                 $this->messageProvider->set(array('file', $code), $lang, $message);
             }
         }
     } elseif (isset($data['messageProvider'])) {
         if (!is_callable($data['messageProvider']) && !$data['messageProvider'] instanceof HTML_QuickForm2_MessageProvider) {
             throw new HTML_QuickForm2_InvalidArgumentException("messageProvider: expecting a callback or an implementation" . " of HTML_QuickForm2_MessageProvider");
         }
         $this->messageProvider = $data['messageProvider'];
     } else {
         HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_MessageProvider_Default');
         $this->messageProvider = HTML_QuickForm2_MessageProvider_Default::getInstance();
     }
     if (isset($data['language'])) {
         $this->language = $data['language'];
     }
     unset($data['messageProvider'], $data['errorMessages'], $data['language']);
     parent::__construct($name, $attributes, $data);
 }
Esempio n. 4
0
 /**
  * Creates a new renderer instance of the given type
  *
  * A renderer is always wrapped by a Proxy, which handles calling its
  * "published" methods and methods of its plugins. Registered plugins are
  * added automagically to the existing renderer instances so that
  * <code>
  * $foo = HTML_QuickForm2_Renderer::factory('foo');
  * // Plugin implementing bar() method
  * HTML_QuickForm2_Renderer::registerPlugin('foo', 'Plugin_Foo_Bar');
  * $foo->bar();
  * </code>
  * will work.
  *
  * @param    string  Type name (treated case-insensitively)
  * @return   HTML_QuickForm2_Renderer_Proxy  A renderer instance of the given
  *                   type wrapped by a Proxy
  * @throws   HTML_QuickForm2_InvalidArgumentException If type name is unknown
  * @throws   HTML_QuickForm2_NotFoundException If class for the renderer can
  *           not be found and/or loaded from file
  */
 public static final function factory($type)
 {
     $type = strtolower($type);
     if (!isset(self::$_types[$type])) {
         throw new HTML_QuickForm2_InvalidArgumentException("Renderer type '{$type}' is not known");
     }
     list($className, $includeFile) = self::$_types[$type];
     HTML_QuickForm2_Loader::loadClass($className, $includeFile);
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_Renderer_Proxy');
     return new HTML_QuickForm2_Renderer_Proxy(new $className(), self::$_pluginClasses[$type]);
 }
 /**
  * Handles an action
  *
  * This will be called if the page itself does not have a handler for a
  * specific action. The method also loads and uses default handlers for
  * common actions, if specific ones were not added.
  *
  * @param HTML_QuickForm2_Controller_Page $page       form page
  * @param string                          $actionName action name
  *
  * @return mixed Return value of action handler
  * @throws HTML_QuickForm2_NotFoundException   if handler for an action is missing
  */
 public function handle(HTML_QuickForm2_Controller_Page $page, $actionName)
 {
     if (!isset($this->handlers[$actionName]) && in_array($actionName, array('next', 'back', 'submit', 'display', 'jump'))) {
         $className = 'HTML_QuickForm2_Controller_Action_' . ucfirst($actionName);
         HTML_QuickForm2_Loader::loadClass($className);
         $this->addHandler($actionName, new $className());
     }
     if (isset($this->handlers[$actionName])) {
         return $this->handlers[$actionName]->perform($page, $actionName);
     } else {
         throw new HTML_QuickForm2_NotFoundException("Unhandled action '{$actionName}' for page '{$page->getForm()->getId()}'");
     }
 }
 /**
  * Class constructor
  *
  * Possible keys in $data array are:
  *  - 'messageProvider': an instance of a class implementing
  *    HTML_QuickForm2_MessageProvider interface, this will be used to get
  *    localized error messages. Default will be used if not given.
  *  - 'language': language to display error messages in, will be passed to
  *    message provider.
  *
  * @param string       $name       Element name
  * @param string|array $attributes Attributes (either a string or an array)
  * @param array        $data       Data used to set up error messages for PHP's
  *                                 file upload errors.
  */
 public function __construct($name = null, $attributes = null, array $data = array())
 {
     if (isset($data['messageProvider'])) {
         if (!is_callable($data['messageProvider']) && !$data['messageProvider'] instanceof HTML_QuickForm2_MessageProvider) {
             throw new HTML_QuickForm2_InvalidArgumentException("messageProvider: expecting a callback or an implementation" . " of HTML_QuickForm2_MessageProvider");
         }
         $this->messageProvider = $data['messageProvider'];
     } else {
         HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_MessageProvider_Default');
         $this->messageProvider = HTML_QuickForm2_MessageProvider_Default::getInstance();
     }
     if (isset($data['language'])) {
         $this->language = $data['language'];
     }
     unset($data['messageProvider'], $data['language']);
     parent::__construct($name, $attributes, $data);
 }
Esempio n. 7
0
 /**
  * Creates a new Rule of the given type
  *
  * @param    string                  Rule type name (treated case-insensitively)
  * @param    HTML_QuickForm2_Node    Element to validate by the rule
  * @param    string                  Message to display if validation fails
  * @param    mixed                   Configuration data for the rule
  * @return   HTML_QuickForm2_Rule    A created Rule
  * @throws   HTML_QuickForm2_InvalidArgumentException If rule type is unknown
  * @throws   HTML_QuickForm2_NotFoundException        If class for the rule
  *           can't be found and/or loaded from file
  * @todo     Remove the trigger_error() call after release 0.4.0
  */
 public static function createRule($type, HTML_QuickForm2_Node $owner, $message = '', $config = null)
 {
     $type = strtolower($type);
     if (!isset(self::$registeredRules[$type])) {
         throw new HTML_QuickForm2_InvalidArgumentException("Rule '{$type}' is not known");
     }
     list($className, $includeFile) = self::$registeredRules[$type];
     HTML_QuickForm2_Loader::loadClass($className, $includeFile);
     if (isset(self::$registeredRules[$type][2])) {
         $config = call_user_func(array($className, 'mergeConfig'), $config, self::$registeredRules[$type][2]);
         if (method_exists($className, 'checkValue')) {
             trigger_error("{$className} may need a custom mergeConfig() method implemented", E_USER_NOTICE);
         }
     }
     return new $className($owner, $message, $config);
 }
Esempio n. 8
0
 /**
  * Updates the list of plugins for the current renderer instance
  *
  * This method checks whether any new plugin classes were registered
  * since its previous invocation and adds instances of these classes to
  * the list. Plugins' methods are imported and can be later called as
  * this object's own.
  *
  * @throws   HTML_QuickForm2_InvalidArgumentException if a plugin has already
  *                   imported name
  */
 protected function updatePlugins()
 {
     for ($i = count($this->_plugins); $i < count($this->_pluginClasses); $i++) {
         list($className, $includeFile) = $this->_pluginClasses[$i];
         HTML_QuickForm2_Loader::loadClass($className, $includeFile);
         $methods = array();
         $plugin = new $className();
         $reflection = new ReflectionObject($plugin);
         foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
             $lower = strtolower($method->getName());
             if ('HTML_QuickForm2_Renderer_Plugin' == $method->getDeclaringClass()->getName()) {
                 continue;
             } elseif (!isset($this->_rendererMethods[$lower]) && !isset($this->_pluginMethods[$lower])) {
                 $methods[$lower] = $i;
             } else {
                 throw new HTML_QuickForm2_InvalidArgumentException('Duplicate method name: name ' . $method->getName() . ' in plugin ' . get_class($plugin) . ' already taken by ' . (isset($this->_rendererMethods[$lower]) ? get_class($this->_renderer) : get_class($this->_plugins[$this->_pluginMethods[$lower]])));
             }
         }
         $plugin->setRenderer($this->_renderer);
         $this->_plugins[$i] = $plugin;
         $this->_pluginMethods += $methods;
     }
 }
Esempio n. 9
0
 /**
  * Returns the client-side representation of the Rule
  *
  * The Javascript object returned contains the following fields:
  *  - callback: {@see getJavascriptCallback()}
  *  - elementId: element ID to set error for if validation fails
  *  - errorMessage: error message to set if validation fails
  *  - chained: chained rules, array of arrays like in $chainedRules property
  *
  * @return   string
  * @throws   HTML_QuickForm2_Exception   if Rule or its chained Rules can only
  *                                       be run server-side
  */
 public function getJavascript($outputTriggers = true)
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
     $js = $this->getJavascriptCallback() . ",\n\t'" . $this->owner->getId() . "', " . HTML_QuickForm2_JavascriptBuilder::encode($this->getMessage());
     $js = $outputTriggers && count($triggers = $this->getJavascriptTriggers()) ? 'new qf.LiveRule(' . $js . ', ' . HTML_QuickForm2_JavascriptBuilder::encode($triggers) : 'new qf.Rule(' . $js;
     if (count($this->chainedRules) > 1 || count($this->chainedRules[0]) > 0) {
         $chained = array();
         foreach ($this->chainedRules as $item) {
             $multipliers = array();
             foreach ($item as $multiplier) {
                 $multipliers[] = $multiplier->getJavascript(false);
             }
             $chained[] = '[' . implode(",\n", $multipliers) . ']';
         }
         $js .= ",\n\t [" . implode(",\n", $chained) . "]";
     }
     return $js . ')';
 }
Esempio n. 10
0
 /**
  * Returns the client-side representation of the Rule
  *
  * The Javascript object returned contains the following fields:
  *  - callback: {@see getJavascriptCallback()}
  *  - elementId: element ID to set error for if validation fails
  *  - errorMessage: error message to set if validation fails
  *  - chained: chained rules, array of arrays like in $chainedRules property
  *
  * @return   string
  * @throws   HTML_QuickForm2_Exception   if Rule or its chained Rules can only
  *                                       be run server-side
  */
 public function getJavascript()
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
     $js = "{\n\tcallback: " . $this->getJavascriptCallback() . ",\n" . "\towner: '" . $this->owner->getId() . "',\n" . "\tmessage: " . HTML_QuickForm2_JavascriptBuilder::encode($this->getMessage());
     if (count($this->chainedRules) > 1 || count($this->chainedRules[0]) > 0) {
         $chained = array();
         foreach ($this->chainedRules as $item) {
             $multipliers = array();
             foreach ($item as $multiplier) {
                 $multipliers[] = $multiplier->getJavascript();
             }
             $chained[] = '[' . implode(",\n", $multipliers) . ']';
         }
         $js .= ",\n\tchained: [" . implode(",\n", $chained) . "]";
     }
     return $js . "\n}";
 }
Esempio n. 11
0
 /**
  * Class constructor
  *
  * The following keys may appear in $data array:
  * - 'messageProvider': a callback or an instance of a class implementing
  *   HTML_QuickForm2_MessageProvider interface, this will be used to get
  *   localized names of months and weekdays. Some of the default ones will
  *   be used if not given.
  * - 'language': date language, use 'locale' here to display month / weekday
  *   names according to the current locale.
  * - 'format': Format of the date, based on PHP's date() function.
  *   The following characters are currently recognised in format string:
  *   <pre>
  *       D => Short names of days
  *       l => Long names of days
  *       d => Day numbers
  *       M => Short names of months
  *       F => Long names of months
  *       m => Month numbers
  *       Y => Four digit year
  *       y => Two digit year
  *       h => 12 hour format
  *       H => 24 hour format
  *       i => Minutes
  *       s => Seconds
  *       a => am/pm
  *       A => AM/PM
  *   </pre>
  * - 'minYear': Minimum year in year select
  * - 'maxYear': Maximum year in year select
  * - 'addEmptyOption': Should an empty option be added to the top of
  *    each select box?
  * - 'emptyOptionValue': The value passed by the empty option.
  * - 'emptyOptionText': The text displayed for the empty option.
  * - 'optionIncrement': Step to increase the option values by (works for 'i' and 's')
  * - 'minHour': Minimum hour in hour select (only for 24 hour format!)
  * - 'maxHour': Maximum hour in hour select (only for 24 hour format!)
  * - 'minMonth': Minimum month in month select
  * - 'maxMonth': Maximum month in month select
  *
  * @param    string  Element name
  * @param    mixed   Attributes (either a string or an array)
  * @param    array   Element data (label, options and data used for element creation)
  */
 public function __construct($name = null, $attributes = null, array $data = array())
 {
     if (isset($data['messageProvider'])) {
         if (!is_callable($data['messageProvider']) && !$data['messageProvider'] instanceof HTML_QuickForm2_MessageProvider) {
             throw new HTML_QuickForm2_InvalidArgumentException("messageProvider: expecting a callback or an implementation" . " of HTML_QuickForm2_MessageProvider");
         }
         $this->messageProvider = $data['messageProvider'];
     } else {
         if (isset($data['language']) && 'locale' == $data['language']) {
             HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_MessageProvider_Strftime');
             $this->messageProvider = new HTML_QuickForm2_MessageProvider_Strftime();
         } else {
             HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_MessageProvider_Default');
             $this->messageProvider = HTML_QuickForm2_MessageProvider_Default::getInstance();
         }
     }
     if (isset($data['language'])) {
         $this->language = $data['language'];
     }
     unset($data['messageProvider'], $data['language']);
     // http://pear.php.net/bugs/bug.php?id=18171
     $this->data['maxYear'] = date('Y');
     parent::__construct($name, $attributes, $data);
     $backslash = false;
     $separators = array();
     $separator = '';
     for ($i = 0, $length = strlen($this->data['format']); $i < $length; $i++) {
         $sign = $this->data['format'][$i];
         if ($backslash) {
             $backslash = false;
             $separator .= $sign;
         } else {
             $loadSelect = true;
             switch ($sign) {
                 case 'D':
                     // Sunday is 0 like with 'w' in date()
                     $options = $this->messageProvider instanceof HTML_QuickForm2_MessageProvider ? $this->messageProvider->get(array('date', 'weekdays_short'), $this->language) : call_user_func($this->messageProvider, array('date', 'weekdays_short'), $this->language);
                     break;
                 case 'l':
                     $options = $this->messageProvider instanceof HTML_QuickForm2_MessageProvider ? $this->messageProvider->get(array('date', 'weekdays_long'), $this->language) : call_user_func($this->messageProvider, array('date', 'weekdays_long'), $this->language);
                     break;
                 case 'd':
                     $options = $this->createOptionList(1, 31);
                     break;
                 case 'M':
                 case 'm':
                 case 'F':
                     $options = $this->createOptionList($this->data['minMonth'], $this->data['maxMonth'], $this->data['minMonth'] > $this->data['maxMonth'] ? -1 : 1);
                     if ('M' == $sign || 'F' == $sign) {
                         $key = 'M' == $sign ? 'months_short' : 'months_long';
                         $names = $this->messageProvider instanceof HTML_QuickForm2_MessageProvider ? $this->messageProvider->get(array('date', $key), $this->language) : call_user_func($this->messageProvider, array('date', $key), $this->language);
                         foreach ($options as $k => &$v) {
                             $v = $names[$k - 1];
                         }
                     }
                     break;
                 case 'Y':
                     $options = $this->createOptionList($this->data['minYear'], $this->data['maxYear'], $this->data['minYear'] > $this->data['maxYear'] ? -1 : 1);
                     break;
                 case 'y':
                     $options = $this->createOptionList($this->data['minYear'], $this->data['maxYear'], $this->data['minYear'] > $this->data['maxYear'] ? -1 : 1);
                     array_walk($options, create_function('&$v,$k', '$v = substr($v,-2);'));
                     break;
                 case 'h':
                     $options = $this->createOptionList(1, 12);
                     break;
                 case 'g':
                     $options = $this->createOptionList(1, 12);
                     array_walk($options, create_function('&$v,$k', '$v = intval($v);'));
                     break;
                 case 'H':
                     $options = $this->createOptionList($this->data['minHour'], $this->data['maxHour'], $this->data['minHour'] > $this->data['maxHour'] ? -1 : 1);
                     break;
                 case 'i':
                     $options = $this->createOptionList(0, 59, $this->data['optionIncrement']['i']);
                     break;
                 case 's':
                     $options = $this->createOptionList(0, 59, $this->data['optionIncrement']['s']);
                     break;
                 case 'a':
                     $options = array('am' => 'am', 'pm' => 'pm');
                     break;
                 case 'A':
                     $options = array('AM' => 'AM', 'PM' => 'PM');
                     break;
                 case 'W':
                     $options = $this->createOptionList(1, 53);
                     break;
                 case '\\':
                     $backslash = true;
                     $loadSelect = false;
                     break;
                 default:
                     $separator .= ' ' == $sign ? '&nbsp;' : $sign;
                     $loadSelect = false;
             }
             if ($loadSelect) {
                 if (0 < count($this)) {
                     $separators[] = $separator;
                 }
                 $separator = '';
                 // Should we add an empty option to the top of the select?
                 if (!is_array($this->data['addEmptyOption']) && $this->data['addEmptyOption'] || is_array($this->data['addEmptyOption']) && !empty($this->data['addEmptyOption'][$sign])) {
                     // Using '+' array operator to preserve the keys
                     if (is_array($this->data['emptyOptionText']) && !empty($this->data['emptyOptionText'][$sign])) {
                         $options = array($this->data['emptyOptionValue'] => $this->data['emptyOptionText'][$sign]) + $options;
                     } else {
                         $options = array($this->data['emptyOptionValue'] => $this->data['emptyOptionText']) + $options;
                     }
                 }
                 $this->addSelect($sign, array('id' => self::generateId($this->getName() . "[{$sign}]")) + $this->getAttributes())->loadOptions($options);
             }
         }
     }
     $separators[] = $separator . ($backslash ? '\\' : '');
     $this->setSeparator($separators);
 }
Esempio n. 12
0
 protected function getJavascriptCallback()
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
     $config = $this->getConfig();
     $operand1 = $this->owner->getJavascriptValue();
     $operand2 = $config['operand'] instanceof HTML_QuickForm2_Node ? $config['operand']->getJavascriptValue() : HTML_QuickForm2_JavascriptBuilder::encode($config['operand']);
     if (!in_array($config['operator'], array('===', '!=='))) {
         $check = "Number({$operand1}) {$config['operator']} Number({$operand2})";
     } else {
         $check = "String({$operand1}) {$config['operator']} String({$operand2})";
     }
     return "function () { return {$check}; }";
 }
Esempio n. 13
0
 /**
  * Magic function; call an imported method of a renderer or its plugin
  *
  * @param    string  method name
  * @param    array   method arguments
  * @return   mixed
  */
 public function __call($name, $arguments)
 {
     $lower = strtolower($name);
     if (isset($this->_rendererMethods[$lower])) {
         // support fluent interfaces
         $ret = call_user_func_array(array($this->_renderer, $name), $arguments);
         return $ret === $this->_renderer ? $this : $ret;
     }
     // any additional plugins since last __call()?
     for ($i = count($this->_plugins); $i < count($this->_pluginClasses); $i++) {
         list($className, $includeFile) = $this->_pluginClasses[$i];
         if (!class_exists($className)) {
             HTML_QuickForm2_Loader::loadClass($className, $includeFile);
         }
         $this->addPlugin($i, new $className());
     }
     if (isset($this->_pluginMethods[$lower])) {
         return call_user_func_array(array($this->_plugins[$this->_pluginMethods[$lower]], $name), $arguments);
     }
     trigger_error("Fatal error: Call to undefined method " . get_class($this->_renderer) . "::" . $name . "()", E_USER_ERROR);
 }
 /**
  * Generates a javascript function call to initialize hierselect behaviour
  *
  * @return string
  */
 private function _generateInitScript()
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_JavascriptBuilder');
     $ids = array();
     /* @var $element HTML_QuickForm2_Element */
     foreach ($this as $element) {
         $ids[] = $element->getId();
     }
     return 'qf.elements.hierselect.init(' . HTML_QuickForm2_JavascriptBuilder::encode($ids) . (empty($this->jsCallback) ? '' : ", {$this->jsCallback}") . ');';
 }
 public function __toString()
 {
     HTML_QuickForm2_Loader::loadClass('HTML_QuickForm2_Renderer');
     $renderer = $this->render(HTML_QuickForm2_Renderer::factory('default'));
     return $renderer->__toString() . $renderer->getJavascriptBuilder()->getSetupCode(null, true);
 }
Esempio n. 16
0
 /**
  * Creates a new Rule of the given type
  *
  * @param    string                  Rule type name (treated case-insensitively)
  * @param    HTML_QuickForm2_Node    Element to validate by the rule
  * @param    string                  Message to display if validation fails
  * @param    mixed                   Configuration data for the rule
  * @return   HTML_QuickForm2_Rule    A created Rule
  * @throws   HTML_QuickForm2_InvalidArgumentException If rule type is unknown
  * @throws   HTML_QuickForm2_NotFoundException        If class for the rule
  *           can't be found and/or loaded from file
  */
 public static function createRule($type, HTML_QuickForm2_Node $owner, $message = '', $config = null)
 {
     $type = strtolower($type);
     if (!isset(self::$registeredRules[$type])) {
         throw new HTML_QuickForm2_InvalidArgumentException("Rule '{$type}' is not known");
     }
     list($className, $includeFile) = self::$registeredRules[$type];
     HTML_QuickForm2_Loader::loadClass($className, $includeFile);
     if (isset(self::$registeredRules[$type][2])) {
         $config = call_user_func(array($className, 'mergeConfig'), $config, self::$registeredRules[$type][2]);
     }
     return new $className($owner, $message, $config);
 }
Esempio n. 17
0
 /**
  * Creates a new Rule of the given type
  *
  * @param    string                  Rule type name (treated case-insensitively)
  * @param    HTML_QuickForm2_Node    Element to validate by the rule
  * @param    string                  Message to display if validation fails
  * @param    mixed                   Additional data for the rule
  * @return   HTML_QuickForm2_Rule    A created Rule
  * @throws   HTML_QuickForm2_InvalidArgumentException If rule type is unknown
  * @throws   HTML_QuickForm2_NotFoundException        If class for the rule
  *           can't be found and/or loaded from file
  */
 public static function createRule($type, HTML_QuickForm2_Node $owner, $message = '', $options = null)
 {
     $type = strtolower($type);
     if (!isset(self::$registeredRules[$type])) {
         throw new HTML_QuickForm2_InvalidArgumentException("Rule '{$type}' is not known");
     }
     list($className, $includeFile) = self::$registeredRules[$type];
     HTML_QuickForm2_Loader::loadClass($className, $includeFile);
     return new $className($owner, $message, $options, $type);
 }