summaryrefslogtreecommitdiff
path: root/framework/web/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'framework/web/helpers')
-rw-r--r--framework/web/helpers/CGoogleApi.php71
-rw-r--r--framework/web/helpers/CHtml.php2122
-rw-r--r--framework/web/helpers/CJSON.php704
-rw-r--r--framework/web/helpers/CJavaScript.php120
4 files changed, 3017 insertions, 0 deletions
diff --git a/framework/web/helpers/CGoogleApi.php b/framework/web/helpers/CGoogleApi.php
new file mode 100644
index 0000000..7bb08fe
--- /dev/null
+++ b/framework/web/helpers/CGoogleApi.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * CGoogleApi class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CGoogleApi provides helper methods to easily access {@link http://code.google.com/apis/ajax/ Google AJAX APIs}.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CGoogleApi.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.helpers
+ */
+class CGoogleApi
+{
+ public static $bootstrapUrl='http://www.google.com/jsapi';
+
+ /**
+ * Renders the jsapi script file.
+ * @param string $apiKey the API key. Null if you do not have a key.
+ * @return string the script tag that loads Google jsapi.
+ */
+ public static function init($apiKey=null)
+ {
+ if($apiKey===null)
+ return CHtml::scriptFile(self::$bootstrapUrl);
+ else
+ return CHtml::scriptFile(self::$bootstrapUrl.'?key='.$apiKey);
+ }
+
+ /**
+ * Loads the specified API module.
+ * Note that you should call {@link init} first.
+ * @param string $name the module name
+ * @param string $version the module version
+ * @param array $options additional js options that are to be passed to the load() function.
+ * @return string the js code for loading the module. You can use {@link CHtml::script()}
+ * to enclose it in a script tag.
+ */
+ public static function load($name,$version='1',$options=array())
+ {
+ if(empty($options))
+ return "google.load(\"{$name}\",\"{$version}\");";
+ else
+ return "google.load(\"{$name}\",\"{$version}\",".CJavaScript::encode($options).");";
+ }
+
+ /**
+ * Registers the specified API module.
+ * This is similar to {@link load} except that it registers the loading code
+ * with {@link CClientScript} instead of returning it.
+ * This method also registers the jsapi script needed by the loading call.
+ * @param string $name the module name
+ * @param string $version the module version
+ * @param array $options additional js options that are to be passed to the load() function.
+ * @param string $apiKey the API key. Null if you do not have a key.
+ */
+ public static function register($name,$version='1',$options=array(),$apiKey=null)
+ {
+ $cs=Yii::app()->getClientScript();
+ $url=$apiKey===null?self::$bootstrapUrl:self::$bootstrapUrl.'?key='.$apiKey;
+ $cs->registerScriptFile($url);
+
+ $js=self::load($name,$version,$options);
+ $cs->registerScript($name,$js,CClientScript::POS_HEAD);
+ }
+} \ No newline at end of file
diff --git a/framework/web/helpers/CHtml.php b/framework/web/helpers/CHtml.php
new file mode 100644
index 0000000..cc265d6
--- /dev/null
+++ b/framework/web/helpers/CHtml.php
@@ -0,0 +1,2122 @@
+<?php
+/**
+ * CHtml class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+
+/**
+ * CHtml is a static class that provides a collection of helper methods for creating HTML views.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CHtml.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.web.helpers
+ * @since 1.0
+ */
+class CHtml
+{
+ const ID_PREFIX='yt';
+ /**
+ * @var string the CSS class for displaying error summaries (see {@link errorSummary}).
+ */
+ public static $errorSummaryCss='errorSummary';
+ /**
+ * @var string the CSS class for displaying error messages (see {@link error}).
+ */
+ public static $errorMessageCss='errorMessage';
+ /**
+ * @var string the CSS class for highlighting error inputs. Form inputs will be appended
+ * with this CSS class if they have input errors.
+ */
+ public static $errorCss='error';
+ /**
+ * @var string the CSS class for required labels. Defaults to 'required'.
+ * @see label
+ */
+ public static $requiredCss='required';
+ /**
+ * @var string the HTML code to be prepended to the required label.
+ * @see label
+ */
+ public static $beforeRequiredLabel='';
+ /**
+ * @var string the HTML code to be appended to the required label.
+ * @see label
+ */
+ public static $afterRequiredLabel=' <span class="required">*</span>';
+ /**
+ * @var integer the counter for generating automatic input field names.
+ */
+ public static $count=0;
+
+ /**
+ * Sets the default style for attaching jQuery event handlers.
+ *
+ * If set to true (default), live/delegated style is used. Event handlers
+ * are attached to the document body and can process events from descendant
+ * elements that are added to the document at a later time.
+ *
+ * If set to false, direct style is used. Event handlers are attached directly
+ * to the DOM element, that must already exist on the page. Elements injected
+ * into the page at a later time will not be processed.
+ *
+ * You can override this setting for a particular element by setting the htmlOptions live attribute
+ * (see {@link clientChange}).
+ *
+ * For more information about attaching jQuery event handler see {@link http://api.jquery.com/on/}
+ * @since 1.1.9
+ * @see clientChange
+ */
+ public static $liveEvents = true;
+
+ /**
+ * Encodes special characters into HTML entities.
+ * The {@link CApplication::charset application charset} will be used for encoding.
+ * @param string $text data to be encoded
+ * @return string the encoded data
+ * @see http://www.php.net/manual/en/function.htmlspecialchars.php
+ */
+ public static function encode($text)
+ {
+ return htmlspecialchars($text,ENT_QUOTES,Yii::app()->charset);
+ }
+
+ /**
+ * Decodes special HTML entities back to the corresponding characters.
+ * This is the opposite of {@link encode()}.
+ * @param string $text data to be decoded
+ * @return string the decoded data
+ * @see http://www.php.net/manual/en/function.htmlspecialchars-decode.php
+ * @since 1.1.8
+ */
+ public static function decode($text)
+ {
+ return htmlspecialchars_decode($text,ENT_QUOTES);
+ }
+
+ /**
+ * Encodes special characters in an array of strings into HTML entities.
+ * Both the array keys and values will be encoded if needed.
+ * If a value is an array, this method will also encode it recursively.
+ * The {@link CApplication::charset application charset} will be used for encoding.
+ * @param array $data data to be encoded
+ * @return array the encoded data
+ * @see http://www.php.net/manual/en/function.htmlspecialchars.php
+ */
+ public static function encodeArray($data)
+ {
+ $d=array();
+ foreach($data as $key=>$value)
+ {
+ if(is_string($key))
+ $key=htmlspecialchars($key,ENT_QUOTES,Yii::app()->charset);
+ if(is_string($value))
+ $value=htmlspecialchars($value,ENT_QUOTES,Yii::app()->charset);
+ else if(is_array($value))
+ $value=self::encodeArray($value);
+ $d[$key]=$value;
+ }
+ return $d;
+ }
+
+ /**
+ * Generates an HTML element.
+ * @param string $tag the tag name
+ * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
+ * If an 'encode' attribute is given and its value is false,
+ * the rest of the attribute values will NOT be HTML-encoded.
+ * Since version 1.1.5, attributes whose value is null will not be rendered.
+ * @param mixed $content the content to be enclosed between open and close element tags. It will not be HTML-encoded.
+ * If false, it means there is no body content.
+ * @param boolean $closeTag whether to generate the close tag.
+ * @return string the generated HTML element tag
+ */
+ public static function tag($tag,$htmlOptions=array(),$content=false,$closeTag=true)
+ {
+ $html='<' . $tag . self::renderAttributes($htmlOptions);
+ if($content===false)
+ return $closeTag ? $html.' />' : $html.'>';
+ else
+ return $closeTag ? $html.'>'.$content.'</'.$tag.'>' : $html.'>'.$content;
+ }
+
+ /**
+ * Generates an open HTML element.
+ * @param string $tag the tag name
+ * @param array $htmlOptions the element attributes. The values will be HTML-encoded using {@link encode()}.
+ * If an 'encode' attribute is given and its value is false,
+ * the rest of the attribute values will NOT be HTML-encoded.
+ * Since version 1.1.5, attributes whose value is null will not be rendered.
+ * @return string the generated HTML element tag
+ */
+ public static function openTag($tag,$htmlOptions=array())
+ {
+ return '<' . $tag . self::renderAttributes($htmlOptions) . '>';
+ }
+
+ /**
+ * Generates a close HTML element.
+ * @param string $tag the tag name
+ * @return string the generated HTML element tag
+ */
+ public static function closeTag($tag)
+ {
+ return '</'.$tag.'>';
+ }
+
+ /**
+ * Encloses the given string within a CDATA tag.
+ * @param string $text the string to be enclosed
+ * @return string the CDATA tag with the enclosed content.
+ */
+ public static function cdata($text)
+ {
+ return '<![CDATA[' . $text . ']]>';
+ }
+
+ /**
+ * Generates a meta tag that can be inserted in the head section of HTML page.
+ * @param string $content content attribute of the meta tag
+ * @param string $name name attribute of the meta tag. If null, the attribute will not be generated
+ * @param string $httpEquiv http-equiv attribute of the meta tag. If null, the attribute will not be generated
+ * @param array $options other options in name-value pairs (e.g. 'scheme', 'lang')
+ * @return string the generated meta tag
+ */
+ public static function metaTag($content,$name=null,$httpEquiv=null,$options=array())
+ {
+ if($name!==null)
+ $options['name']=$name;
+ if($httpEquiv!==null)
+ $options['http-equiv']=$httpEquiv;
+ $options['content']=$content;
+ return self::tag('meta',$options);
+ }
+
+ /**
+ * Generates a link tag that can be inserted in the head section of HTML page.
+ * Do not confuse this method with {@link link()}. The latter generates a hyperlink.
+ * @param string $relation rel attribute of the link tag. If null, the attribute will not be generated.
+ * @param string $type type attribute of the link tag. If null, the attribute will not be generated.
+ * @param string $href href attribute of the link tag. If null, the attribute will not be generated.
+ * @param string $media media attribute of the link tag. If null, the attribute will not be generated.
+ * @param array $options other options in name-value pairs
+ * @return string the generated link tag
+ */
+ public static function linkTag($relation=null,$type=null,$href=null,$media=null,$options=array())
+ {
+ if($relation!==null)
+ $options['rel']=$relation;
+ if($type!==null)
+ $options['type']=$type;
+ if($href!==null)
+ $options['href']=$href;
+ if($media!==null)
+ $options['media']=$media;
+ return self::tag('link',$options);
+ }
+
+ /**
+ * Encloses the given CSS content with a CSS tag.
+ * @param string $text the CSS content
+ * @param string $media the media that this CSS should apply to.
+ * @return string the CSS properly enclosed
+ */
+ public static function css($text,$media='')
+ {
+ if($media!=='')
+ $media=' media="'.$media.'"';
+ return "<style type=\"text/css\"{$media}>\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n</style>";
+ }
+
+ /**
+ * Registers a 'refresh' meta tag.
+ * This method can be invoked anywhere in a view. It will register a 'refresh'
+ * meta tag with {@link CClientScript} so that the page can be refreshed in
+ * the specified seconds.
+ * @param integer $seconds the number of seconds to wait before refreshing the page
+ * @param string $url the URL to which the page should be redirected to. If empty, it means the current page.
+ * @since 1.1.1
+ */
+ public static function refresh($seconds, $url='')
+ {
+ $content="$seconds";
+ if($url!=='')
+ $content.=';'.self::normalizeUrl($url);
+ Yii::app()->clientScript->registerMetaTag($content,null,'refresh');
+ }
+
+ /**
+ * Links to the specified CSS file.
+ * @param string $url the CSS URL
+ * @param string $media the media that this CSS should apply to.
+ * @return string the CSS link.
+ */
+ public static function cssFile($url,$media='')
+ {
+ if($media!=='')
+ $media=' media="'.$media.'"';
+ return '<link rel="stylesheet" type="text/css" href="'.self::encode($url).'"'.$media.' />';
+ }
+
+ /**
+ * Encloses the given JavaScript within a script tag.
+ * @param string $text the JavaScript to be enclosed
+ * @return string the enclosed JavaScript
+ */
+ public static function script($text)
+ {
+ return "<script type=\"text/javascript\">\n/*<![CDATA[*/\n{$text}\n/*]]>*/\n</script>";
+ }
+
+ /**
+ * Includes a JavaScript file.
+ * @param string $url URL for the JavaScript file
+ * @return string the JavaScript file tag
+ */
+ public static function scriptFile($url)
+ {
+ return '<script type="text/javascript" src="'.self::encode($url).'"></script>';
+ }
+
+ /**
+ * Generates an opening form tag.
+ * This is a shortcut to {@link beginForm}.
+ * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
+ * @param string $method form method (e.g. post, get)
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated form tag.
+ */
+ public static function form($action='',$method='post',$htmlOptions=array())
+ {
+ return self::beginForm($action,$method,$htmlOptions);
+ }
+
+ /**
+ * Generates an opening form tag.
+ * Note, only the open tag is generated. A close tag should be placed manually
+ * at the end of the form.
+ * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
+ * @param string $method form method (e.g. post, get)
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated form tag.
+ * @see endForm
+ */
+ public static function beginForm($action='',$method='post',$htmlOptions=array())
+ {
+ $htmlOptions['action']=$url=self::normalizeUrl($action);
+ $htmlOptions['method']=$method;
+ $form=self::tag('form',$htmlOptions,false,false);
+ $hiddens=array();
+ if(!strcasecmp($method,'get') && ($pos=strpos($url,'?'))!==false)
+ {
+ foreach(explode('&',substr($url,$pos+1)) as $pair)
+ {
+ if(($pos=strpos($pair,'='))!==false)
+ $hiddens[]=self::hiddenField(urldecode(substr($pair,0,$pos)),urldecode(substr($pair,$pos+1)),array('id'=>false));
+ }
+ }
+ $request=Yii::app()->request;
+ if($request->enableCsrfValidation && !strcasecmp($method,'post'))
+ $hiddens[]=self::hiddenField($request->csrfTokenName,$request->getCsrfToken(),array('id'=>false));
+ if($hiddens!==array())
+ $form.="\n".self::tag('div',array('style'=>'display:none'),implode("\n",$hiddens));
+ return $form;
+ }
+
+ /**
+ * Generates a closing form tag.
+ * @return string the generated tag
+ * @see beginForm
+ */
+ public static function endForm()
+ {
+ return '</form>';
+ }
+
+ /**
+ * Generates a stateful form tag.
+ * A stateful form tag is similar to {@link form} except that it renders an additional
+ * hidden field for storing persistent page states. You should use this method to generate
+ * a form tag if you want to access persistent page states when the form is submitted.
+ * @param mixed $action the form action URL (see {@link normalizeUrl} for details about this parameter.)
+ * @param string $method form method (e.g. post, get)
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated form tag.
+ */
+ public static function statefulForm($action='',$method='post',$htmlOptions=array())
+ {
+ return self::form($action,$method,$htmlOptions)."\n".
+ self::tag('div',array('style'=>'display:none'),self::pageStateField(''));
+ }
+
+ /**
+ * Generates a hidden field for storing persistent page states.
+ * This method is internally used by {@link statefulForm}.
+ * @param string $value the persistent page states in serialized format
+ * @return string the generated hidden field
+ */
+ public static function pageStateField($value)
+ {
+ return '<input type="hidden" name="'.CController::STATE_INPUT_NAME.'" value="'.$value.'" />';
+ }
+
+ /**
+ * Generates a hyperlink tag.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
+ * @param mixed $url a URL or an action route that can be used to create a URL.
+ * See {@link normalizeUrl} for more details about how to specify this parameter.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated hyperlink
+ * @see normalizeUrl
+ * @see clientChange
+ */
+ public static function link($text,$url='#',$htmlOptions=array())
+ {
+ if($url!=='')
+ $htmlOptions['href']=self::normalizeUrl($url);
+ self::clientChange('click',$htmlOptions);
+ return self::tag('a',$htmlOptions,$text);
+ }
+
+ /**
+ * Generates a mailto link.
+ * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code such as an image tag.
+ * @param string $email email address. If this is empty, the first parameter (link body) will be treated as the email address.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated mailto link
+ * @see clientChange
+ */
+ public static function mailto($text,$email='',$htmlOptions=array())
+ {
+ if($email==='')
+ $email=$text;
+ return self::link($text,'mailto:'.$email,$htmlOptions);
+ }
+
+ /**
+ * Generates an image tag.
+ * @param string $src the image URL
+ * @param string $alt the alternative text display
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated image tag
+ */
+ public static function image($src,$alt='',$htmlOptions=array())
+ {
+ $htmlOptions['src']=$src;
+ $htmlOptions['alt']=$alt;
+ return self::tag('img',$htmlOptions);
+ }
+
+ /**
+ * Generates a button.
+ * @param string $label the button label
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function button($label='button',$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['name']))
+ {
+ if(!array_key_exists('name',$htmlOptions))
+ $htmlOptions['name']=self::ID_PREFIX.self::$count++;
+ }
+ if(!isset($htmlOptions['type']))
+ $htmlOptions['type']='button';
+ if(!isset($htmlOptions['value']))
+ $htmlOptions['value']=$label;
+ self::clientChange('click',$htmlOptions);
+ return self::tag('input',$htmlOptions);
+ }
+
+ /**
+ * Generates a button using HTML button tag.
+ * This method is similar to {@link button} except that it generates a 'button'
+ * tag instead of 'input' tag.
+ * @param string $label the button label. Note that this value will be directly inserted in the button element
+ * without being HTML-encoded.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function htmlButton($label='button',$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['name']))
+ $htmlOptions['name']=self::ID_PREFIX.self::$count++;
+ if(!isset($htmlOptions['type']))
+ $htmlOptions['type']='button';
+ self::clientChange('click',$htmlOptions);
+ return self::tag('button',$htmlOptions,$label);
+ }
+
+ /**
+ * Generates a submit button.
+ * @param string $label the button label
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function submitButton($label='submit',$htmlOptions=array())
+ {
+ $htmlOptions['type']='submit';
+ return self::button($label,$htmlOptions);
+ }
+
+ /**
+ * Generates a reset button.
+ * @param string $label the button label
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function resetButton($label='reset',$htmlOptions=array())
+ {
+ $htmlOptions['type']='reset';
+ return self::button($label,$htmlOptions);
+ }
+
+ /**
+ * Generates an image submit button.
+ * @param string $src the image URL
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function imageButton($src,$htmlOptions=array())
+ {
+ $htmlOptions['src']=$src;
+ $htmlOptions['type']='image';
+ return self::button('submit',$htmlOptions);
+ }
+
+ /**
+ * Generates a link submit button.
+ * @param string $label the button label
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button tag
+ * @see clientChange
+ */
+ public static function linkButton($label='submit',$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['submit']))
+ $htmlOptions['submit']=isset($htmlOptions['href']) ? $htmlOptions['href'] : '';
+ return self::link($label,'#',$htmlOptions);
+ }
+
+ /**
+ * Generates a label tag.
+ * @param string $label label text. Note, you should HTML-encode the text if needed.
+ * @param string $for the ID of the HTML element that this label is associated with.
+ * If this is false, the 'for' attribute for the label tag will not be rendered.
+ * @param array $htmlOptions additional HTML attributes.
+ * The following HTML option is recognized:
+ * <ul>
+ * <li>required: if this is set and is true, the label will be styled
+ * with CSS class 'required' (customizable with CHtml::$requiredCss),
+ * and be decorated with {@link CHtml::beforeRequiredLabel} and
+ * {@link CHtml::afterRequiredLabel}.</li>
+ * </ul>
+ * @return string the generated label tag
+ */
+ public static function label($label,$for,$htmlOptions=array())
+ {
+ if($for===false)
+ unset($htmlOptions['for']);
+ else
+ $htmlOptions['for']=$for;
+ if(isset($htmlOptions['required']))
+ {
+ if($htmlOptions['required'])
+ {
+ if(isset($htmlOptions['class']))
+ $htmlOptions['class'].=' '.self::$requiredCss;
+ else
+ $htmlOptions['class']=self::$requiredCss;
+ $label=self::$beforeRequiredLabel.$label.self::$afterRequiredLabel;
+ }
+ unset($htmlOptions['required']);
+ }
+ return self::tag('label',$htmlOptions,$label);
+ }
+
+ /**
+ * Generates a text field input.
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated input field
+ * @see clientChange
+ * @see inputField
+ */
+ public static function textField($name,$value='',$htmlOptions=array())
+ {
+ self::clientChange('change',$htmlOptions);
+ return self::inputField('text',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a hidden input.
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated input field
+ * @see inputField
+ */
+ public static function hiddenField($name,$value='',$htmlOptions=array())
+ {
+ return self::inputField('hidden',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a password field input.
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated input field
+ * @see clientChange
+ * @see inputField
+ */
+ public static function passwordField($name,$value='',$htmlOptions=array())
+ {
+ self::clientChange('change',$htmlOptions);
+ return self::inputField('password',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a file input.
+ * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
+ * After the form is submitted, the uploaded file information can be obtained via $_FILES[$name] (see
+ * PHP documentation).
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated input field
+ * @see inputField
+ */
+ public static function fileField($name,$value='',$htmlOptions=array())
+ {
+ return self::inputField('file',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a text area input.
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated text area
+ * @see clientChange
+ * @see inputField
+ */
+ public static function textArea($name,$value='',$htmlOptions=array())
+ {
+ $htmlOptions['name']=$name;
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=self::getIdByName($name);
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ self::clientChange('change',$htmlOptions);
+ return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $value : self::encode($value));
+ }
+
+ /**
+ * Generates a radio button.
+ * @param string $name the input name
+ * @param boolean $checked whether the radio button is checked
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
+ * the value returned when the radio button is not checked. When set, a hidden field is rendered so that
+ * when the radio button is not checked, we can still obtain the posted uncheck value.
+ * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
+ * @return string the generated radio button
+ * @see clientChange
+ * @see inputField
+ */
+ public static function radioButton($name,$checked=false,$htmlOptions=array())
+ {
+ if($checked)
+ $htmlOptions['checked']='checked';
+ else
+ unset($htmlOptions['checked']);
+ $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
+ self::clientChange('click',$htmlOptions);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck=null;
+
+ if($uncheck!==null)
+ {
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
+ $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
+ else
+ $uncheckOptions=array('id'=>false);
+ $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
+ }
+ else
+ $hidden='';
+
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ return $hidden . self::inputField('radio',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a check box.
+ * @param string $name the input name
+ * @param boolean $checked whether the check box is checked
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * Since version 1.1.2, a special option named 'uncheckValue' is available that can be used to specify
+ * the value returned when the checkbox is not checked. When set, a hidden field is rendered so that
+ * when the checkbox is not checked, we can still obtain the posted uncheck value.
+ * If 'uncheckValue' is not set or set to NULL, the hidden field will not be rendered.
+ * @return string the generated check box
+ * @see clientChange
+ * @see inputField
+ */
+ public static function checkBox($name,$checked=false,$htmlOptions=array())
+ {
+ if($checked)
+ $htmlOptions['checked']='checked';
+ else
+ unset($htmlOptions['checked']);
+ $value=isset($htmlOptions['value']) ? $htmlOptions['value'] : 1;
+ self::clientChange('click',$htmlOptions);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck=null;
+
+ if($uncheck!==null)
+ {
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ if(isset($htmlOptions['id']) && $htmlOptions['id']!==false)
+ $uncheckOptions=array('id'=>self::ID_PREFIX.$htmlOptions['id']);
+ else
+ $uncheckOptions=array('id'=>false);
+ $hidden=self::hiddenField($name,$uncheck,$uncheckOptions);
+ }
+ else
+ $hidden='';
+
+ // add a hidden field so that if the checkbox is not selected, it still submits a value
+ return $hidden . self::inputField('checkbox',$name,$value,$htmlOptions);
+ }
+
+ /**
+ * Generates a drop down list.
+ * @param string $name the input name
+ * @param string $select the selected value
+ * @param array $data data for generating the list options (value=>display).
+ * You may use {@link listData} to generate this data.
+ * Please refer to {@link listOptions} on how this data is used to generate the list options.
+ * Note, the values and labels will be automatically HTML-encoded by this method.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
+ * In addition, the following options are also supported specifically for dropdown list:
+ * <ul>
+ * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
+ * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
+ * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
+ * The 'empty' option can also be an array of value-label pairs.
+ * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
+ * <li>options: array, specifies additional attributes for each OPTION tag.
+ * The array keys must be the option values, and the array values are the extra
+ * OPTION tag attributes in the name-value pairs. For example,
+ * <pre>
+ * array(
+ * 'value1'=>array('disabled'=>true, 'label'=>'value 1'),
+ * 'value2'=>array('label'=>'value 2'),
+ * );
+ * </pre>
+ * </li>
+ * </ul>
+ * @return string the generated drop down list
+ * @see clientChange
+ * @see inputField
+ * @see listData
+ */
+ public static function dropDownList($name,$select,$data,$htmlOptions=array())
+ {
+ $htmlOptions['name']=$name;
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=self::getIdByName($name);
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ self::clientChange('change',$htmlOptions);
+ $options="\n".self::listOptions($select,$data,$htmlOptions);
+ return self::tag('select',$htmlOptions,$options);
+ }
+
+ /**
+ * Generates a list box.
+ * @param string $name the input name
+ * @param mixed $select the selected value(s). This can be either a string for single selection or an array for multiple selections.
+ * @param array $data data for generating the list options (value=>display)
+ * You may use {@link listData} to generate this data.
+ * Please refer to {@link listOptions} on how this data is used to generate the list options.
+ * Note, the values and labels will be automatically HTML-encoded by this method.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized. See {@link clientChange} and {@link tag} for more details.
+ * In addition, the following options are also supported specifically for list box:
+ * <ul>
+ * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
+ * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
+ * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
+ * The 'empty' option can also be an array of value-label pairs.
+ * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
+ * <li>options: array, specifies additional attributes for each OPTION tag.
+ * The array keys must be the option values, and the array values are the extra
+ * OPTION tag attributes in the name-value pairs. For example,
+ * <pre>
+ * array(
+ * 'value1'=>array('disabled'=>true, 'label'=>'value 1'),
+ * 'value2'=>array('label'=>'value 2'),
+ * );
+ * </pre>
+ * </li>
+ * </ul>
+ * @return string the generated list box
+ * @see clientChange
+ * @see inputField
+ * @see listData
+ */
+ public static function listBox($name,$select,$data,$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['size']))
+ $htmlOptions['size']=4;
+ if(isset($htmlOptions['multiple']))
+ {
+ if(substr($name,-2)!=='[]')
+ $name.='[]';
+ }
+ return self::dropDownList($name,$select,$data,$htmlOptions);
+ }
+
+ /**
+ * Generates a check box list.
+ * A check box list allows multiple selection, like {@link listBox}.
+ * As a result, the corresponding POST value is an array.
+ * @param string $name name of the check box list. You can use this name to retrieve
+ * the selected value(s) once the form is submitted.
+ * @param mixed $select selection of the check boxes. This can be either a string
+ * for single selection or an array for multiple selections.
+ * @param array $data value-label pairs used to generate the check box list.
+ * Note, the values will be automatically HTML-encoded, while the labels will not.
+ * @param array $htmlOptions addtional HTML options. The options will be applied to
+ * each checkbox input. The following special options are recognized:
+ * <ul>
+ * <li>template: string, specifies how each checkbox is rendered. Defaults
+ * to "{input} {label}", where "{input}" will be replaced by the generated
+ * check box input tag while "{label}" be replaced by the corresponding check box label.</li>
+ * <li>separator: string, specifies the string that separates the generated check boxes.</li>
+ * <li>checkAll: string, specifies the label for the "check all" checkbox.
+ * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
+ * this checkbox will cause all checkboxes checked or unchecked.</li>
+ * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
+ * displayed at the end of the checkbox list. If this option is not set (default)
+ * or is false, the 'check all' checkbox will be displayed at the beginning of
+ * the checkbox list.</li>
+ * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
+ * for every label tag in the list.</li>
+ * </ul>
+ * @return string the generated check box list
+ */
+ public static function checkBoxList($name,$select,$data,$htmlOptions=array())
+ {
+ $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
+ $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"<br/>\n";
+ unset($htmlOptions['template'],$htmlOptions['separator']);
+
+ if(substr($name,-2)!=='[]')
+ $name.='[]';
+
+ if(isset($htmlOptions['checkAll']))
+ {
+ $checkAllLabel=$htmlOptions['checkAll'];
+ $checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
+ }
+ unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']);
+
+ $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
+ unset($htmlOptions['labelOptions']);
+
+ $items=array();
+ $baseID=self::getIdByName($name);
+ $id=0;
+ $checkAll=true;
+
+ foreach($data as $value=>$label)
+ {
+ $checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select);
+ $checkAll=$checkAll && $checked;
+ $htmlOptions['value']=$value;
+ $htmlOptions['id']=$baseID.'_'.$id++;
+ $option=self::checkBox($name,$checked,$htmlOptions);
+ $label=self::label($label,$htmlOptions['id'],$labelOptions);
+ $items[]=strtr($template,array('{input}'=>$option,'{label}'=>$label));
+ }
+
+ if(isset($checkAllLabel))
+ {
+ $htmlOptions['value']=1;
+ $htmlOptions['id']=$id=$baseID.'_all';
+ $option=self::checkBox($id,$checkAll,$htmlOptions);
+ $label=self::label($checkAllLabel,$id,$labelOptions);
+ $item=strtr($template,array('{input}'=>$option,'{label}'=>$label));
+ if($checkAllLast)
+ $items[]=$item;
+ else
+ array_unshift($items,$item);
+ $name=strtr($name,array('['=>'\\[',']'=>'\\]'));
+ $js=<<<EOD
+$('#$id').click(function() {
+ $("input[name='$name']").prop('checked', this.checked);
+});
+$("input[name='$name']").click(function() {
+ $('#$id').prop('checked', !$("input[name='$name']:not(:checked)").length);
+});
+$('#$id').prop('checked', !$("input[name='$name']:not(:checked)").length);
+EOD;
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('jquery');
+ $cs->registerScript($id,$js);
+ }
+
+ return self::tag('span',array('id'=>$baseID),implode($separator,$items));
+ }
+
+ /**
+ * Generates a radio button list.
+ * A radio button list is like a {@link checkBoxList check box list}, except that
+ * it only allows single selection.
+ * @param string $name name of the radio button list. You can use this name to retrieve
+ * the selected value(s) once the form is submitted.
+ * @param string $select selection of the radio buttons.
+ * @param array $data value-label pairs used to generate the radio button list.
+ * Note, the values will be automatically HTML-encoded, while the labels will not.
+ * @param array $htmlOptions addtional HTML options. The options will be applied to
+ * each radio button input. The following special options are recognized:
+ * <ul>
+ * <li>template: string, specifies how each radio button is rendered. Defaults
+ * to "{input} {label}", where "{input}" will be replaced by the generated
+ * radio button input tag while "{label}" will be replaced by the corresponding radio button label.</li>
+ * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
+ * <li>labelOptions: array, specifies the additional HTML attributes to be rendered
+ * for every label tag in the list.</li>
+ * </ul>
+ * @return string the generated radio button list
+ */
+ public static function radioButtonList($name,$select,$data,$htmlOptions=array())
+ {
+ $template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
+ $separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:"<br/>\n";
+ unset($htmlOptions['template'],$htmlOptions['separator']);
+
+ $labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
+ unset($htmlOptions['labelOptions']);
+
+ $items=array();
+ $baseID=self::getIdByName($name);
+ $id=0;
+ foreach($data as $value=>$label)
+ {
+ $checked=!strcmp($value,$select);
+ $htmlOptions['value']=$value;
+ $htmlOptions['id']=$baseID.'_'.$id++;
+ $option=self::radioButton($name,$checked,$htmlOptions);
+ $label=self::label($label,$htmlOptions['id'],$labelOptions);
+ $items[]=strtr($template,array('{input}'=>$option,'{label}'=>$label));
+ }
+ return self::tag('span',array('id'=>$baseID),implode($separator,$items));
+ }
+
+ /**
+ * Generates a link that can initiate AJAX requests.
+ * @param string $text the link body (it will NOT be HTML-encoded.)
+ * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
+ * @param array $ajaxOptions AJAX options (see {@link ajax})
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated link
+ * @see normalizeUrl
+ * @see ajax
+ */
+ public static function ajaxLink($text,$url,$ajaxOptions=array(),$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['href']))
+ $htmlOptions['href']='#';
+ $ajaxOptions['url']=$url;
+ $htmlOptions['ajax']=$ajaxOptions;
+ self::clientChange('click',$htmlOptions);
+ return self::tag('a',$htmlOptions,$text);
+ }
+
+ /**
+ * Generates a push button that can initiate AJAX requests.
+ * @param string $label the button label
+ * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
+ * @param array $ajaxOptions AJAX options (see {@link ajax})
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button
+ */
+ public static function ajaxButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
+ {
+ $ajaxOptions['url']=$url;
+ $htmlOptions['ajax']=$ajaxOptions;
+ return self::button($label,$htmlOptions);
+ }
+
+ /**
+ * Generates a push button that can submit the current form in POST method.
+ * @param string $label the button label
+ * @param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {@link normalizeUrl} for more details.
+ * @param array $ajaxOptions AJAX options (see {@link ajax})
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated button
+ */
+ public static function ajaxSubmitButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
+ {
+ $ajaxOptions['type']='POST';
+ $htmlOptions['type']='submit';
+ return self::ajaxButton($label,$url,$ajaxOptions,$htmlOptions);
+ }
+
+ /**
+ * Generates the JavaScript that initiates an AJAX request.
+ * @param array $options AJAX options. The valid options are specified in the jQuery ajax documentation.
+ * The following special options are added for convenience:
+ * <ul>
+ * <li>update: string, specifies the selector whose HTML content should be replaced
+ * by the AJAX request result.</li>
+ * <li>replace: string, specifies the selector whose target should be replaced
+ * by the AJAX request result.</li>
+ * </ul>
+ * Note, if you specify the 'success' option, the above options will be ignored.
+ * @return string the generated JavaScript
+ * @see http://docs.jquery.com/Ajax/jQuery.ajax#options
+ */
+ public static function ajax($options)
+ {
+ Yii::app()->getClientScript()->registerCoreScript('jquery');
+ if(!isset($options['url']))
+ $options['url']='js:location.href';
+ else
+ $options['url']=self::normalizeUrl($options['url']);
+ if(!isset($options['cache']))
+ $options['cache']=false;
+ if(!isset($options['data']) && isset($options['type']))
+ $options['data']='js:jQuery(this).parents("form").serialize()';
+ foreach(array('beforeSend','complete','error','success') as $name)
+ {
+ if(isset($options[$name]) && strpos($options[$name],'js:')!==0)
+ $options[$name]='js:'.$options[$name];
+ }
+ if(isset($options['update']))
+ {
+ if(!isset($options['success']))
+ $options['success']='js:function(html){jQuery("'.$options['update'].'").html(html)}';
+ unset($options['update']);
+ }
+ if(isset($options['replace']))
+ {
+ if(!isset($options['success']))
+ $options['success']='js:function(html){jQuery("'.$options['replace'].'").replaceWith(html)}';
+ unset($options['replace']);
+ }
+ return 'jQuery.ajax('.CJavaScript::encode($options).');';
+ }
+
+ /**
+ * Generates the URL for the published assets.
+ * @param string $path the path of the asset to be published
+ * @param boolean $hashByName whether the published directory should be named as the hashed basename.
+ * If false, the name will be the hashed dirname of the path being published.
+ * Defaults to false. Set true if the path being published is shared among
+ * different extensions.
+ * @return string the asset URL
+ */
+ public static function asset($path,$hashByName=false)
+ {
+ return Yii::app()->getAssetManager()->publish($path,$hashByName);
+ }
+
+ /**
+ * Normalizes the input parameter to be a valid URL.
+ *
+ * If the input parameter is an empty string, the currently requested URL will be returned.
+ *
+ * If the input parameter is a non-empty string, it is treated as a valid URL and will
+ * be returned without any change.
+ *
+ * If the input parameter is an array, it is treated as a controller route and a list of
+ * GET parameters, and the {@link CController::createUrl} method will be invoked to
+ * create a URL. In this case, the first array element refers to the controller route,
+ * and the rest key-value pairs refer to the additional GET parameters for the URL.
+ * For example, <code>array('post/list', 'page'=>3)</code> may be used to generate the URL
+ * <code>/index.php?r=post/list&page=3</code>.
+ *
+ * @param mixed $url the parameter to be used to generate a valid URL
+ * @return string the normalized URL
+ */
+ public static function normalizeUrl($url)
+ {
+ if(is_array($url))
+ {
+ if(isset($url[0]))
+ {
+ if(($c=Yii::app()->getController())!==null)
+ $url=$c->createUrl($url[0],array_splice($url,1));
+ else
+ $url=Yii::app()->createUrl($url[0],array_splice($url,1));
+ }
+ else
+ $url='';
+ }
+ return $url==='' ? Yii::app()->getRequest()->getUrl() : $url;
+ }
+
+ /**
+ * Generates an input HTML tag.
+ * This method generates an input HTML tag based on the given input name and value.
+ * @param string $type the input type (e.g. 'text', 'radio')
+ * @param string $name the input name
+ * @param string $value the input value
+ * @param array $htmlOptions additional HTML attributes for the HTML tag (see {@link tag}).
+ * @return string the generated input tag
+ */
+ protected static function inputField($type,$name,$value,$htmlOptions)
+ {
+ $htmlOptions['type']=$type;
+ $htmlOptions['value']=$value;
+ $htmlOptions['name']=$name;
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=self::getIdByName($name);
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ return self::tag('input',$htmlOptions);
+ }
+
+ /**
+ * Generates a label tag for a model attribute.
+ * The label text is the attribute label and the label is associated with
+ * the input for the attribute (see {@link CModel::getAttributeLabel}.
+ * If the attribute has input error, the label's CSS class will be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. The following special options are recognized:
+ * <ul>
+ * <li>required: if this is set and is true, the label will be styled
+ * with CSS class 'required' (customizable with CHtml::$requiredCss),
+ * and be decorated with {@link CHtml::beforeRequiredLabel} and
+ * {@link CHtml::afterRequiredLabel}.</li>
+ * <li>label: this specifies the label to be displayed. If this is not set,
+ * {@link CModel::getAttributeLabel} will be called to get the label for display.
+ * If the label is specified as false, no label will be rendered.</li>
+ * </ul>
+ * @return string the generated label tag
+ */
+ public static function activeLabel($model,$attribute,$htmlOptions=array())
+ {
+ if(isset($htmlOptions['for']))
+ {
+ $for=$htmlOptions['for'];
+ unset($htmlOptions['for']);
+ }
+ else
+ $for=self::getIdByName(self::resolveName($model,$attribute));
+ if(isset($htmlOptions['label']))
+ {
+ if(($label=$htmlOptions['label'])===false)
+ return '';
+ unset($htmlOptions['label']);
+ }
+ else
+ $label=$model->getAttributeLabel($attribute);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ return self::label($label,$for,$htmlOptions);
+ }
+
+ /**
+ * Generates a label tag for a model attribute.
+ * This is an enhanced version of {@link activeLabel}. It will render additional
+ * CSS class and mark when the attribute is required.
+ * In particular, it calls {@link CModel::isAttributeRequired} to determine
+ * if the attribute is required.
+ * If so, it will add a CSS class {@link CHtml::requiredCss} to the label,
+ * and decorate the label with {@link CHtml::beforeRequiredLabel} and
+ * {@link CHtml::afterRequiredLabel}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated label tag
+ */
+ public static function activeLabelEx($model,$attribute,$htmlOptions=array())
+ {
+ $realAttribute=$attribute;
+ self::resolveName($model,$attribute); // strip off square brackets if any
+ $htmlOptions['required']=$model->isAttributeRequired($attribute);
+ return self::activeLabel($model,$realAttribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a text field input for a model attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated input field
+ * @see clientChange
+ * @see activeInputField
+ */
+ public static function activeTextField($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ self::clientChange('change',$htmlOptions);
+ return self::activeInputField('text',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a hidden input for a model attribute.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes.
+ * @return string the generated input field
+ * @see activeInputField
+ */
+ public static function activeHiddenField($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ return self::activeInputField('hidden',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a password field input for a model attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated input field
+ * @see clientChange
+ * @see activeInputField
+ */
+ public static function activePasswordField($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ self::clientChange('change',$htmlOptions);
+ return self::activeInputField('password',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a text area input for a model attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * @return string the generated text area
+ * @see clientChange
+ */
+ public static function activeTextArea($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ self::clientChange('change',$htmlOptions);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ $text=self::resolveValue($model,$attribute);
+ return self::tag('textarea',$htmlOptions,isset($htmlOptions['encode']) && !$htmlOptions['encode'] ? $text : self::encode($text));
+ }
+
+ /**
+ * Generates a file input for a model attribute.
+ * Note, you have to set the enclosing form's 'enctype' attribute to be 'multipart/form-data'.
+ * After the form is submitted, the uploaded file information can be obtained via $_FILES (see
+ * PHP documentation).
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes (see {@link tag}).
+ * @return string the generated input field
+ * @see activeInputField
+ */
+ public static function activeFileField($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ // add a hidden field so that if a model only has a file field, we can
+ // still use isset($_POST[$modelClass]) to detect if the input is submitted
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
+ return self::hiddenField($htmlOptions['name'],'',$hiddenOptions)
+ . self::activeInputField('file',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a radio button for a model attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * A special option named 'uncheckValue' is available that can be used to specify
+ * the value returned when the radio button is not checked. By default, this value is '0'.
+ * Internally, a hidden field is rendered so that when the radio button is not checked,
+ * we can still obtain the posted uncheck value.
+ * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
+ * @return string the generated radio button
+ * @see clientChange
+ * @see activeInputField
+ */
+ public static function activeRadioButton($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ if(!isset($htmlOptions['value']))
+ $htmlOptions['value']=1;
+ if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
+ $htmlOptions['checked']='checked';
+ self::clientChange('click',$htmlOptions);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck='0';
+
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
+ $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
+
+ // add a hidden field so that if the radio button is not selected, it still submits a value
+ return $hidden . self::activeInputField('radio',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a check box for a model attribute.
+ * The attribute is assumed to take either true or false value.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are also recognized (see {@link clientChange} and {@link tag} for more details.)
+ * A special option named 'uncheckValue' is available that can be used to specify
+ * the value returned when the checkbox is not checked. By default, this value is '0'.
+ * Internally, a hidden field is rendered so that when the checkbox is not checked,
+ * we can still obtain the posted uncheck value.
+ * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
+ * @return string the generated check box
+ * @see clientChange
+ * @see activeInputField
+ */
+ public static function activeCheckBox($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ if(!isset($htmlOptions['value']))
+ $htmlOptions['value']=1;
+ if(!isset($htmlOptions['checked']) && self::resolveValue($model,$attribute)==$htmlOptions['value'])
+ $htmlOptions['checked']='checked';
+ self::clientChange('click',$htmlOptions);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck='0';
+
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
+ $hidden=$uncheck!==null ? self::hiddenField($htmlOptions['name'],$uncheck,$hiddenOptions) : '';
+
+ return $hidden . self::activeInputField('checkbox',$model,$attribute,$htmlOptions);
+ }
+
+ /**
+ * Generates a drop down list for a model attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data data for generating the list options (value=>display)
+ * You may use {@link listData} to generate this data.
+ * Please refer to {@link listOptions} on how this data is used to generate the list options.
+ * Note, the values and labels will be automatically HTML-encoded by this method.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
+ * In addition, the following options are also supported:
+ * <ul>
+ * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
+ * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
+ * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
+ * The 'empty' option can also be an array of value-label pairs.
+ * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
+ * <li>options: array, specifies additional attributes for each OPTION tag.
+ * The array keys must be the option values, and the array values are the extra
+ * OPTION tag attributes in the name-value pairs. For example,
+ * <pre>
+ * array(
+ * 'value1'=>array('disabled'=>true, 'label'=>'value 1'),
+ * 'value2'=>array('label'=>'value 2'),
+ * );
+ * </pre>
+ * </li>
+ * </ul>
+ * @return string the generated drop down list
+ * @see clientChange
+ * @see listData
+ */
+ public static function activeDropDownList($model,$attribute,$data,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ $selection=self::resolveValue($model,$attribute);
+ $options="\n".self::listOptions($selection,$data,$htmlOptions);
+ self::clientChange('change',$htmlOptions);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ if(isset($htmlOptions['multiple']))
+ {
+ if(substr($htmlOptions['name'],-2)!=='[]')
+ $htmlOptions['name'].='[]';
+ }
+ return self::tag('select',$htmlOptions,$options);
+ }
+
+ /**
+ * Generates a list box for a model attribute.
+ * The model attribute value is used as the selection.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data data for generating the list options (value=>display)
+ * You may use {@link listData} to generate this data.
+ * Please refer to {@link listOptions} on how this data is used to generate the list options.
+ * Note, the values and labels will be automatically HTML-encoded by this method.
+ * @param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
+ * attributes are recognized. See {@link clientChange} and {@link tag} for more details.
+ * In addition, the following options are also supported:
+ * <ul>
+ * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
+ * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
+ * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
+ * The 'empty' option can also be an array of value-label pairs.
+ * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
+ * <li>options: array, specifies additional attributes for each OPTION tag.
+ * The array keys must be the option values, and the array values are the extra
+ * OPTION tag attributes in the name-value pairs. For example,
+ * <pre>
+ * array(
+ * 'value1'=>array('disabled'=>true, 'label'=>'value 1'),
+ * 'value2'=>array('label'=>'value 2'),
+ * );
+ * </pre>
+ * </li>
+ * </ul>
+ * @return string the generated list box
+ * @see clientChange
+ * @see listData
+ */
+ public static function activeListBox($model,$attribute,$data,$htmlOptions=array())
+ {
+ if(!isset($htmlOptions['size']))
+ $htmlOptions['size']=4;
+ return self::activeDropDownList($model,$attribute,$data,$htmlOptions);
+ }
+
+ /**
+ * Generates a check box list for a model attribute.
+ * The model attribute value is used as the selection.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * Note that a check box list allows multiple selection, like {@link listBox}.
+ * As a result, the corresponding POST value is an array. In case no selection
+ * is made, the corresponding POST value is an empty string.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data value-label pairs used to generate the check box list.
+ * Note, the values will be automatically HTML-encoded, while the labels will not.
+ * @param array $htmlOptions addtional HTML options. The options will be applied to
+ * each checkbox input. The following special options are recognized:
+ * <ul>
+ * <li>template: string, specifies how each checkbox is rendered. Defaults
+ * to "{input} {label}", where "{input}" will be replaced by the generated
+ * check box input tag while "{label}" will be replaced by the corresponding check box label.</li>
+ * <li>separator: string, specifies the string that separates the generated check boxes.</li>
+ * <li>checkAll: string, specifies the label for the "check all" checkbox.
+ * If this option is specified, a 'check all' checkbox will be displayed. Clicking on
+ * this checkbox will cause all checkboxes checked or unchecked.</li>
+ * <li>checkAllLast: boolean, specifies whether the 'check all' checkbox should be
+ * displayed at the end of the checkbox list. If this option is not set (default)
+ * or is false, the 'check all' checkbox will be displayed at the beginning of
+ * the checkbox list.</li>
+ * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
+ * </ul>
+ * Since 1.1.7, a special option named 'uncheckValue' is available. It can be used to set the value
+ * that will be returned when the checkbox is not checked. By default, this value is ''.
+ * Internally, a hidden field is rendered so when the checkbox is not checked, we can still
+ * obtain the value. If 'uncheckValue' is set to NULL, there will be no hidden field rendered.
+ * @return string the generated check box list
+ * @see checkBoxList
+ */
+ public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ $selection=self::resolveValue($model,$attribute);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ $name=$htmlOptions['name'];
+ unset($htmlOptions['name']);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck='';
+
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
+ $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
+
+ return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions);
+ }
+
+ /**
+ * Generates a radio button list for a model attribute.
+ * The model attribute value is used as the selection.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $data value-label pairs used to generate the radio button list.
+ * Note, the values will be automatically HTML-encoded, while the labels will not.
+ * @param array $htmlOptions addtional HTML options. The options will be applied to
+ * each radio button input. The following special options are recognized:
+ * <ul>
+ * <li>template: string, specifies how each radio button is rendered. Defaults
+ * to "{input} {label}", where "{input}" will be replaced by the generated
+ * radio button input tag while "{label}" will be replaced by the corresponding radio button label.</li>
+ * <li>separator: string, specifies the string that separates the generated radio buttons. Defaults to new line (<br/>).</li>
+ * <li>encode: boolean, specifies whether to encode HTML-encode tag attributes and values. Defaults to true.</li>
+ * </ul>
+ * Since version 1.1.7, a special option named 'uncheckValue' is available that can be used to specify the value
+ * returned when the radio button is not checked. By default, this value is ''. Internally, a hidden field is
+ * rendered so that when the radio button is not checked, we can still obtain the posted uncheck value.
+ * If 'uncheckValue' is set as NULL, the hidden field will not be rendered.
+ * @return string the generated radio button list
+ * @see radioButtonList
+ */
+ public static function activeRadioButtonList($model,$attribute,$data,$htmlOptions=array())
+ {
+ self::resolveNameID($model,$attribute,$htmlOptions);
+ $selection=self::resolveValue($model,$attribute);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ $name=$htmlOptions['name'];
+ unset($htmlOptions['name']);
+
+ if(array_key_exists('uncheckValue',$htmlOptions))
+ {
+ $uncheck=$htmlOptions['uncheckValue'];
+ unset($htmlOptions['uncheckValue']);
+ }
+ else
+ $uncheck='';
+
+ $hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
+ $hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
+
+ return $hidden . self::radioButtonList($name,$selection,$data,$htmlOptions);
+ }
+
+ /**
+ * Displays a summary of validation errors for one or several models.
+ * @param mixed $model the models whose input errors are to be displayed. This can be either
+ * a single model or an array of models.
+ * @param string $header a piece of HTML code that appears in front of the errors
+ * @param string $footer a piece of HTML code that appears at the end of the errors
+ * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
+ * A special option named 'firstError' is recognized, which when set true, will
+ * make the error summary to show only the first error message of each attribute.
+ * If this is not set or is false, all error messages will be displayed.
+ * This option has been available since version 1.1.3.
+ * @return string the error summary. Empty if no errors are found.
+ * @see CModel::getErrors
+ * @see errorSummaryCss
+ */
+ public static function errorSummary($model,$header=null,$footer=null,$htmlOptions=array())
+ {
+ $content='';
+ if(!is_array($model))
+ $model=array($model);
+ if(isset($htmlOptions['firstError']))
+ {
+ $firstError=$htmlOptions['firstError'];
+ unset($htmlOptions['firstError']);
+ }
+ else
+ $firstError=false;
+ foreach($model as $m)
+ {
+ foreach($m->getErrors() as $errors)
+ {
+ foreach($errors as $error)
+ {
+ if($error!='')
+ $content.="<li>$error</li>\n";
+ if($firstError)
+ break;
+ }
+ }
+ }
+ if($content!=='')
+ {
+ if($header===null)
+ $header='<p>'.Yii::t('yii','Please fix the following input errors:').'</p>';
+ if(!isset($htmlOptions['class']))
+ $htmlOptions['class']=self::$errorSummaryCss;
+ return self::tag('div',$htmlOptions,$header."\n<ul>\n$content</ul>".$footer);
+ }
+ else
+ return '';
+ }
+
+ /**
+ * Displays the first validation error for a model attribute.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute name
+ * @param array $htmlOptions additional HTML attributes to be rendered in the container div tag.
+ * @return string the error display. Empty if no errors are found.
+ * @see CModel::getErrors
+ * @see errorMessageCss
+ */
+ public static function error($model,$attribute,$htmlOptions=array())
+ {
+ self::resolveName($model,$attribute); // turn [a][b]attr into attr
+ $error=$model->getError($attribute);
+ if($error!='')
+ {
+ if(!isset($htmlOptions['class']))
+ $htmlOptions['class']=self::$errorMessageCss;
+ return self::tag('div',$htmlOptions,$error);
+ }
+ else
+ return '';
+ }
+
+ /**
+ * Generates the data suitable for list-based HTML elements.
+ * The generated data can be used in {@link dropDownList}, {@link listBox}, {@link checkBoxList},
+ * {@link radioButtonList}, and their active-versions (such as {@link activeDropDownList}).
+ * Note, this method does not HTML-encode the generated data. You may call {@link encodeArray} to
+ * encode it if needed.
+ * Please refer to the {@link value} method on how to specify value field, text field and group field.
+ * @param array $models a list of model objects. This parameter
+ * can also be an array of associative arrays (e.g. results of {@link CDbCommand::queryAll}).
+ * @param string $valueField the attribute name for list option values
+ * @param string $textField the attribute name for list option texts
+ * @param string $groupField the attribute name for list option group names. If empty, no group will be generated.
+ * @return array the list data that can be used in {@link dropDownList}, {@link listBox}, etc.
+ */
+ public static function listData($models,$valueField,$textField,$groupField='')
+ {
+ $listData=array();
+ if($groupField==='')
+ {
+ foreach($models as $model)
+ {
+ $value=self::value($model,$valueField);
+ $text=self::value($model,$textField);
+ $listData[$value]=$text;
+ }
+ }
+ else
+ {
+ foreach($models as $model)
+ {
+ $group=self::value($model,$groupField);
+ $value=self::value($model,$valueField);
+ $text=self::value($model,$textField);
+ $listData[$group][$value]=$text;
+ }
+ }
+ return $listData;
+ }
+
+ /**
+ * Evaluates the value of the specified attribute for the given model.
+ * The attribute name can be given in a dot syntax. For example, if the attribute
+ * is "author.firstName", this method will return the value of "$model->author->firstName".
+ * A default value (passed as the last parameter) will be returned if the attribute does
+ * not exist or is broken in the middle (e.g. $model->author is null).
+ * The model can be either an object or an array. If the latter, the attribute is treated
+ * as a key of the array. For the example of "author.firstName", if would mean the array value
+ * "$model['author']['firstName']".
+ * @param mixed $model the model. This can be either an object or an array.
+ * @param string $attribute the attribute name (use dot to concatenate multiple attributes)
+ * @param mixed $defaultValue the default value to return when the attribute does not exist
+ * @return mixed the attribute value
+ */
+ public static function value($model,$attribute,$defaultValue=null)
+ {
+ foreach(explode('.',$attribute) as $name)
+ {
+ if(is_object($model))
+ $model=$model->$name;
+ else if(is_array($model) && isset($model[$name]))
+ $model=$model[$name];
+ else
+ return $defaultValue;
+ }
+ return $model;
+ }
+
+ /**
+ * Generates a valid HTML ID based on name.
+ * @param string $name name from which to generate HTML ID
+ * @return string the ID generated based on name.
+ */
+ public static function getIdByName($name)
+ {
+ return str_replace(array('[]', '][', '[', ']'), array('', '_', '_', ''), $name);
+ }
+
+ /**
+ * Generates input field ID for a model attribute.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @return string the generated input field ID
+ */
+ public static function activeId($model,$attribute)
+ {
+ return self::getIdByName(self::activeName($model,$attribute));
+ }
+
+ /**
+ * Generates input field name for a model attribute.
+ * Unlike {@link resolveName}, this method does NOT modify the attribute name.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @return string the generated input field name
+ */
+ public static function activeName($model,$attribute)
+ {
+ $a=$attribute; // because the attribute name may be changed by resolveName
+ return self::resolveName($model,$a);
+ }
+
+ /**
+ * Generates an input HTML tag for a model attribute.
+ * This method generates an input HTML tag based on the given data model and attribute.
+ * If the attribute has input error, the input field's CSS class will
+ * be appended with {@link errorCss}.
+ * This enables highlighting the incorrect input.
+ * @param string $type the input type (e.g. 'text', 'radio')
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions additional HTML attributes for the HTML tag
+ * @return string the generated input tag
+ */
+ protected static function activeInputField($type,$model,$attribute,$htmlOptions)
+ {
+ $htmlOptions['type']=$type;
+ if($type==='text' || $type==='password')
+ {
+ if(!isset($htmlOptions['maxlength']))
+ {
+ foreach($model->getValidators($attribute) as $validator)
+ {
+ if($validator instanceof CStringValidator && $validator->max!==null)
+ {
+ $htmlOptions['maxlength']=$validator->max;
+ break;
+ }
+ }
+ }
+ else if($htmlOptions['maxlength']===false)
+ unset($htmlOptions['maxlength']);
+ }
+
+ if($type==='file')
+ unset($htmlOptions['value']);
+ else if(!isset($htmlOptions['value']))
+ $htmlOptions['value']=self::resolveValue($model,$attribute);
+ if($model->hasErrors($attribute))
+ self::addErrorCss($htmlOptions);
+ return self::tag('input',$htmlOptions);
+ }
+
+ /**
+ * Generates the list options.
+ * @param mixed $selection the selected value(s). This can be either a string for single selection or an array for multiple selections.
+ * @param array $listData the option data (see {@link listData})
+ * @param array $htmlOptions additional HTML attributes. The following two special attributes are recognized:
+ * <ul>
+ * <li>encode: boolean, specifies whether to encode the values. Defaults to true.</li>
+ * <li>prompt: string, specifies the prompt text shown as the first list option. Its value is empty. Note, the prompt text will NOT be HTML-encoded.</li>
+ * <li>empty: string, specifies the text corresponding to empty selection. Its value is empty.
+ * The 'empty' option can also be an array of value-label pairs.
+ * Each pair will be used to render a list option at the beginning. Note, the text label will NOT be HTML-encoded.</li>
+ * <li>options: array, specifies additional attributes for each OPTION tag.
+ * The array keys must be the option values, and the array values are the extra
+ * OPTION tag attributes in the name-value pairs. For example,
+ * <pre>
+ * array(
+ * 'value1'=>array('disabled'=>true, 'label'=>'value 1'),
+ * 'value2'=>array('label'=>'value 2'),
+ * );
+ * </pre>
+ * </li>
+ * <li>key: string, specifies the name of key attribute of the selection object(s).
+ * This is used when the selection is represented in terms of objects. In this case,
+ * the property named by the key option of the objects will be treated as the actual selection value.
+ * This option defaults to 'primaryKey', meaning using the 'primaryKey' property value of the objects in the selection.
+ * This option has been available since version 1.1.3.</li>
+ * </ul>
+ * @return string the generated list options
+ */
+ public static function listOptions($selection,$listData,&$htmlOptions)
+ {
+ $raw=isset($htmlOptions['encode']) && !$htmlOptions['encode'];
+ $content='';
+ if(isset($htmlOptions['prompt']))
+ {
+ $content.='<option value="">'.strtr($htmlOptions['prompt'],array('<'=>'&lt;', '>'=>'&gt;'))."</option>\n";
+ unset($htmlOptions['prompt']);
+ }
+ if(isset($htmlOptions['empty']))
+ {
+ if(!is_array($htmlOptions['empty']))
+ $htmlOptions['empty']=array(''=>$htmlOptions['empty']);
+ foreach($htmlOptions['empty'] as $value=>$label)
+ $content.='<option value="'.self::encode($value).'">'.strtr($label,array('<'=>'&lt;', '>'=>'&gt;'))."</option>\n";
+ unset($htmlOptions['empty']);
+ }
+
+ if(isset($htmlOptions['options']))
+ {
+ $options=$htmlOptions['options'];
+ unset($htmlOptions['options']);
+ }
+ else
+ $options=array();
+
+ $key=isset($htmlOptions['key']) ? $htmlOptions['key'] : 'primaryKey';
+ if(is_array($selection))
+ {
+ foreach($selection as $i=>$item)
+ {
+ if(is_object($item))
+ $selection[$i]=$item->$key;
+ }
+ }
+ else if(is_object($selection))
+ $selection=$selection->$key;
+
+ foreach($listData as $key=>$value)
+ {
+ if(is_array($value))
+ {
+ $content.='<optgroup label="'.($raw?$key : self::encode($key))."\">\n";
+ $dummy=array('options'=>$options);
+ if(isset($htmlOptions['encode']))
+ $dummy['encode']=$htmlOptions['encode'];
+ $content.=self::listOptions($selection,$value,$dummy);
+ $content.='</optgroup>'."\n";
+ }
+ else
+ {
+ $attributes=array('value'=>(string)$key, 'encode'=>!$raw);
+ if(!is_array($selection) && !strcmp($key,$selection) || is_array($selection) && in_array($key,$selection))
+ $attributes['selected']='selected';
+ if(isset($options[$key]))
+ $attributes=array_merge($attributes,$options[$key]);
+ $content.=self::tag('option',$attributes,$raw?(string)$value : self::encode((string)$value))."\n";
+ }
+ }
+
+ unset($htmlOptions['key']);
+
+ return $content;
+ }
+
+ /**
+ * Generates the JavaScript with the specified client changes.
+ * @param string $event event name (without 'on')
+ * @param array $htmlOptions HTML attributes which may contain the following special attributes
+ * specifying the client change behaviors:
+ * <ul>
+ * <li>submit: string, specifies the URL that the button should submit to. If empty, the current requested URL will be used.</li>
+ * <li>params: array, name-value pairs that should be submitted together with the form. This is only used when 'submit' option is specified.</li>
+ * <li>csrf: boolean, whether a CSRF token should be submitted when {@link CHttpRequest::enableCsrfValidation} is true. Defaults to false.
+ * You may want to set this to be true if there is no enclosing form around this element.
+ * This option is meaningful only when 'submit' option is set.</li>
+ * <li>return: boolean, the return value of the javascript. Defaults to false, meaning that the execution of
+ * javascript would not cause the default behavior of the event.</li>
+ * <li>confirm: string, specifies the message that should show in a pop-up confirmation dialog.</li>
+ * <li>ajax: array, specifies the AJAX options (see {@link ajax}).</li>
+ * <li>live: boolean, whether the event handler should be attached with live/delegate or direct style. If not set, {@link liveEvents} will be used. This option has been available since version 1.1.6.</li>
+ * </ul>
+ * This parameter has been available since version 1.1.1.
+ */
+ protected static function clientChange($event,&$htmlOptions)
+ {
+ if(!isset($htmlOptions['submit']) && !isset($htmlOptions['confirm']) && !isset($htmlOptions['ajax']))
+ return;
+
+ if(isset($htmlOptions['live']))
+ {
+ $live=$htmlOptions['live'];
+ unset($htmlOptions['live']);
+ }
+ else
+ $live = self::$liveEvents;
+
+ if(isset($htmlOptions['return']) && $htmlOptions['return'])
+ $return='return true';
+ else
+ $return='return false';
+
+ if(isset($htmlOptions['on'.$event]))
+ {
+ $handler=trim($htmlOptions['on'.$event],';').';';
+ unset($htmlOptions['on'.$event]);
+ }
+ else
+ $handler='';
+
+ if(isset($htmlOptions['id']))
+ $id=$htmlOptions['id'];
+ else
+ $id=$htmlOptions['id']=isset($htmlOptions['name'])?$htmlOptions['name']:self::ID_PREFIX.self::$count++;
+
+ $cs=Yii::app()->getClientScript();
+ $cs->registerCoreScript('jquery');
+
+ if(isset($htmlOptions['submit']))
+ {
+ $cs->registerCoreScript('yii');
+ $request=Yii::app()->getRequest();
+ if($request->enableCsrfValidation && isset($htmlOptions['csrf']) && $htmlOptions['csrf'])
+ $htmlOptions['params'][$request->csrfTokenName]=$request->getCsrfToken();
+ if(isset($htmlOptions['params']))
+ $params=CJavaScript::encode($htmlOptions['params']);
+ else
+ $params='{}';
+ if($htmlOptions['submit']!=='')
+ $url=CJavaScript::quote(self::normalizeUrl($htmlOptions['submit']));
+ else
+ $url='';
+ $handler.="jQuery.yii.submitForm(this,'$url',$params);{$return};";
+ }
+
+ if(isset($htmlOptions['ajax']))
+ $handler.=self::ajax($htmlOptions['ajax'])."{$return};";
+
+ if(isset($htmlOptions['confirm']))
+ {
+ $confirm='confirm(\''.CJavaScript::quote($htmlOptions['confirm']).'\')';
+ if($handler!=='')
+ $handler="if($confirm) {".$handler."} else return false;";
+ else
+ $handler="return $confirm;";
+ }
+
+ if($live)
+ $cs->registerScript('Yii.CHtml.#' . $id, "$('body').on('$event','#$id',function(){{$handler}});");
+ else
+ $cs->registerScript('Yii.CHtml.#' . $id, "$('#$id').on('$event', function(){{$handler}});");
+ unset($htmlOptions['params'],$htmlOptions['submit'],$htmlOptions['ajax'],$htmlOptions['confirm'],$htmlOptions['return'],$htmlOptions['csrf']);
+ }
+
+ /**
+ * Generates input name and ID for a model attribute.
+ * This method will update the HTML options by setting appropriate 'name' and 'id' attributes.
+ * This method may also modify the attribute name if the name
+ * contains square brackets (mainly used in tabular input).
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @param array $htmlOptions the HTML options
+ */
+ public static function resolveNameID($model,&$attribute,&$htmlOptions)
+ {
+ if(!isset($htmlOptions['name']))
+ $htmlOptions['name']=self::resolveName($model,$attribute);
+ if(!isset($htmlOptions['id']))
+ $htmlOptions['id']=self::getIdByName($htmlOptions['name']);
+ else if($htmlOptions['id']===false)
+ unset($htmlOptions['id']);
+ }
+
+ /**
+ * Generates input name for a model attribute.
+ * Note, the attribute name may be modified after calling this method if the name
+ * contains square brackets (mainly used in tabular input) before the real attribute name.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute
+ * @return string the input name
+ */
+ public static function resolveName($model,&$attribute)
+ {
+ if(($pos=strpos($attribute,'['))!==false)
+ {
+ if($pos!==0) // e.g. name[a][b]
+ return get_class($model).'['.substr($attribute,0,$pos).']'.substr($attribute,$pos);
+ if(($pos=strrpos($attribute,']'))!==false && $pos!==strlen($attribute)-1) // e.g. [a][b]name
+ {
+ $sub=substr($attribute,0,$pos+1);
+ $attribute=substr($attribute,$pos+1);
+ return get_class($model).$sub.'['.$attribute.']';
+ }
+ if(preg_match('/\](\w+\[.*)$/',$attribute,$matches))
+ {
+ $name=get_class($model).'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']';
+ $attribute=$matches[1];
+ return $name;
+ }
+ }
+ return get_class($model).'['.$attribute.']';
+ }
+
+ /**
+ * Evaluates the attribute value of the model.
+ * This method can recognize the attribute name written in array format.
+ * For example, if the attribute name is 'name[a][b]', the value "$model->name['a']['b']" will be returned.
+ * @param CModel $model the data model
+ * @param string $attribute the attribute name
+ * @return mixed the attribute value
+ * @since 1.1.3
+ */
+ public static function resolveValue($model,$attribute)
+ {
+ if(($pos=strpos($attribute,'['))!==false)
+ {
+ if($pos===0) // [a]name[b][c], should ignore [a]
+ {
+ if(preg_match('/\](\w+)/',$attribute,$matches))
+ $attribute=$matches[1];
+ if(($pos=strpos($attribute,'['))===false)
+ return $model->$attribute;
+ }
+ $name=substr($attribute,0,$pos);
+ $value=$model->$name;
+ foreach(explode('][',rtrim(substr($attribute,$pos+1),']')) as $id)
+ {
+ if(is_array($value) && isset($value[$id]))
+ $value=$value[$id];
+ else
+ return null;
+ }
+ return $value;
+ }
+ else
+ return $model->$attribute;
+ }
+
+ /**
+ * Appends {@link errorCss} to the 'class' attribute.
+ * @param array $htmlOptions HTML options to be modified
+ */
+ protected static function addErrorCss(&$htmlOptions)
+ {
+ if(isset($htmlOptions['class']))
+ $htmlOptions['class'].=' '.self::$errorCss;
+ else
+ $htmlOptions['class']=self::$errorCss;
+ }
+
+ /**
+ * Renders the HTML tag attributes.
+ * Since version 1.1.5, attributes whose value is null will not be rendered.
+ * Special attributes, such as 'checked', 'disabled', 'readonly', will be rendered
+ * properly based on their corresponding boolean value.
+ * @param array $htmlOptions attributes to be rendered
+ * @return string the rendering result
+ */
+ public static function renderAttributes($htmlOptions)
+ {
+ static $specialAttributes=array(
+ 'checked'=>1,
+ 'declare'=>1,
+ 'defer'=>1,
+ 'disabled'=>1,
+ 'ismap'=>1,
+ 'multiple'=>1,
+ 'nohref'=>1,
+ 'noresize'=>1,
+ 'readonly'=>1,
+ 'selected'=>1,
+ );
+
+ if($htmlOptions===array())
+ return '';
+
+ $html='';
+ if(isset($htmlOptions['encode']))
+ {
+ $raw=!$htmlOptions['encode'];
+ unset($htmlOptions['encode']);
+ }
+ else
+ $raw=false;
+
+ if($raw)
+ {
+ foreach($htmlOptions as $name=>$value)
+ {
+ if(isset($specialAttributes[$name]))
+ {
+ if($value)
+ $html .= ' ' . $name . '="' . $name . '"';
+ }
+ else if($value!==null)
+ $html .= ' ' . $name . '="' . $value . '"';
+ }
+ }
+ else
+ {
+ foreach($htmlOptions as $name=>$value)
+ {
+ if(isset($specialAttributes[$name]))
+ {
+ if($value)
+ $html .= ' ' . $name . '="' . $name . '"';
+ }
+ else if($value!==null)
+ $html .= ' ' . $name . '="' . self::encode($value) . '"';
+ }
+ }
+ return $html;
+ }
+}
diff --git a/framework/web/helpers/CJSON.php b/framework/web/helpers/CJSON.php
new file mode 100644
index 0000000..f329453
--- /dev/null
+++ b/framework/web/helpers/CJSON.php
@@ -0,0 +1,704 @@
+<?php
+/**
+* JSON (JavaScript Object Notation) is a lightweight data-interchange
+* format. It is easy for humans to read and write. It is easy for machines
+* to parse and generate. It is based on a subset of the JavaScript
+* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
+* This feature can also be found in Python. JSON is a text format that is
+* completely language independent but uses conventions that are familiar
+* to programmers of the C-family of languages, including C, C++, C#, Java,
+* JavaScript, Perl, TCL, and many others. These properties make JSON an
+* ideal data-interchange language.
+*
+* This package provides a simple encoder and decoder for JSON notation. It
+* is intended for use with client-side Javascript applications that make
+* use of HTTPRequest to perform server communication functions - data can
+* be encoded into JSON notation for use in a client-side javascript, or
+* decoded from incoming Javascript requests. JSON format is native to
+* Javascript, and can be directly eval()'ed with no further parsing
+* overhead
+*
+* All strings should be in ASCII or UTF-8 format!
+*
+* LICENSE: 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.
+*
+* THIS SOFTWARE IS PROVIDED ``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 CONTRIBUTORS 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.
+*
+* @author Michal Migurski <mike-json@teczno.com>
+* @author Matt Knapp <mdknapp[at]gmail[dot]com>
+* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+* @copyright 2005 Michal Migurski
+* @license http://www.opensource.org/licenses/bsd-license.php
+* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
+*/
+
+/**
+ * CJSON converts PHP data to and from JSON format.
+ *
+ * @author Michal Migurski <mike-json@teczno.com>
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
+ * @version $Id: CJSON.php 3204 2011-05-05 21:36:32Z alexander.makarow $
+ * @package system.web.helpers
+ * @since 1.0
+ */
+class CJSON
+{
+ /**
+ * Marker constant for JSON::decode(), used to flag stack state
+ */
+ const JSON_SLICE = 1;
+
+ /**
+ * Marker constant for JSON::decode(), used to flag stack state
+ */
+ const JSON_IN_STR = 2;
+
+ /**
+ * Marker constant for JSON::decode(), used to flag stack state
+ */
+ const JSON_IN_ARR = 4;
+
+ /**
+ * Marker constant for JSON::decode(), used to flag stack state
+ */
+ const JSON_IN_OBJ = 8;
+
+ /**
+ * Marker constant for JSON::decode(), used to flag stack state
+ */
+ const JSON_IN_CMT = 16;
+
+ /**
+ * Encodes an arbitrary variable into JSON format
+ *
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
+ * If var is a string, it will be converted to UTF-8 format first before being encoded.
+ * @return string JSON string representation of input var
+ */
+ public static function encode($var)
+ {
+ switch (gettype($var)) {
+ case 'boolean':
+ return $var ? 'true' : 'false';
+
+ case 'NULL':
+ return 'null';
+
+ case 'integer':
+ return (int) $var;
+
+ case 'double':
+ case 'float':
+ return str_replace(',','.',(float)$var); // locale-independent representation
+
+ case 'string':
+ if (($enc=strtoupper(Yii::app()->charset))!=='UTF-8')
+ $var=iconv($enc, 'UTF-8', $var);
+
+ if(function_exists('json_encode'))
+ return json_encode($var);
+
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
+ $ascii = '';
+ $strlen_var = strlen($var);
+
+ /*
+ * Iterate over every character in the string,
+ * escaping with a slash or encoding to UTF-8 where necessary
+ */
+ for ($c = 0; $c < $strlen_var; ++$c) {
+
+ $ord_var_c = ord($var{$c});
+
+ switch (true) {
+ case $ord_var_c == 0x08:
+ $ascii .= '\b';
+ break;
+ case $ord_var_c == 0x09:
+ $ascii .= '\t';
+ break;
+ case $ord_var_c == 0x0A:
+ $ascii .= '\n';
+ break;
+ case $ord_var_c == 0x0C:
+ $ascii .= '\f';
+ break;
+ case $ord_var_c == 0x0D:
+ $ascii .= '\r';
+ break;
+
+ case $ord_var_c == 0x22:
+ case $ord_var_c == 0x2F:
+ case $ord_var_c == 0x5C:
+ // double quote, slash, slosh
+ $ascii .= '\\'.$var{$c};
+ break;
+
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
+ // characters U-00000000 - U-0000007F (same as ASCII)
+ $ascii .= $var{$c};
+ break;
+
+ case (($ord_var_c & 0xE0) == 0xC0):
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c, ord($var{$c+1}));
+ $c+=1;
+ $utf16 = self::utf8ToUTF16BE($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF0) == 0xE0):
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c+1}),
+ ord($var{$c+2}));
+ $c+=2;
+ $utf16 = self::utf8ToUTF16BE($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xF8) == 0xF0):
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c+1}),
+ ord($var{$c+2}),
+ ord($var{$c+3}));
+ $c+=3;
+ $utf16 = self::utf8ToUTF16BE($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFC) == 0xF8):
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c+1}),
+ ord($var{$c+2}),
+ ord($var{$c+3}),
+ ord($var{$c+4}));
+ $c+=4;
+ $utf16 = self::utf8ToUTF16BE($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+
+ case (($ord_var_c & 0xFE) == 0xFC):
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $char = pack('C*', $ord_var_c,
+ ord($var{$c+1}),
+ ord($var{$c+2}),
+ ord($var{$c+3}),
+ ord($var{$c+4}),
+ ord($var{$c+5}));
+ $c+=5;
+ $utf16 = self::utf8ToUTF16BE($char);
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
+ break;
+ }
+ }
+
+ return '"'.$ascii.'"';
+
+ case 'array':
+ /*
+ * As per JSON spec if any array key is not an integer
+ * we must treat the the whole array as an object. We
+ * also try to catch a sparsely populated associative
+ * array with numeric keys here because some JS engines
+ * will create an array with empty indexes up to
+ * max_index which can cause memory issues and because
+ * the keys, which may be relevant, will be remapped
+ * otherwise.
+ *
+ * As per the ECMA and JSON specification an object may
+ * have any string as a property. Unfortunately due to
+ * a hole in the ECMA specification if the key is a
+ * ECMA reserved word or starts with a digit the
+ * parameter is only accessible using ECMAScript's
+ * bracket notation.
+ */
+
+ // treat as a JSON object
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
+ return '{' .
+ join(',', array_map(array('CJSON', 'nameValue'),
+ array_keys($var),
+ array_values($var)))
+ . '}';
+ }
+
+ // treat it like a regular array
+ return '[' . join(',', array_map(array('CJSON', 'encode'), $var)) . ']';
+
+ case 'object':
+ if ($var instanceof Traversable)
+ {
+ $vars = array();
+ foreach ($var as $k=>$v)
+ $vars[$k] = $v;
+ }
+ else
+ $vars = get_object_vars($var);
+ return '{' .
+ join(',', array_map(array('CJSON', 'nameValue'),
+ array_keys($vars),
+ array_values($vars)))
+ . '}';
+
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * array-walking function for use in generating JSON-formatted name-value pairs
+ *
+ * @param string $name name of key to use
+ * @param mixed $value reference to an array element to be encoded
+ *
+ * @return string JSON-formatted name-value pair, like '"name":value'
+ * @access private
+ */
+ protected static function nameValue($name, $value)
+ {
+ return self::encode(strval($name)) . ':' . self::encode($value);
+ }
+
+ /**
+ * reduce a string by removing leading and trailing comments and whitespace
+ *
+ * @param string $str string value to strip of comments and whitespace
+ *
+ * @return string string value stripped of comments and whitespace
+ * @access private
+ */
+ protected static function reduceString($str)
+ {
+ $str = preg_replace(array(
+
+ // eliminate single line comments in '// ...' form
+ '#^\s*//(.+)$#m',
+
+ // eliminate multi-line comments in '/* ... */' form, at start of string
+ '#^\s*/\*(.+)\*/#Us',
+
+ // eliminate multi-line comments in '/* ... */' form, at end of string
+ '#/\*(.+)\*/\s*$#Us'
+
+ ), '', $str);
+
+ // eliminate extraneous space
+ return trim($str);
+ }
+
+ /**
+ * decodes a JSON string into appropriate variable
+ *
+ * @param string $str JSON-formatted string
+ * @param boolean $useArray whether to use associative array to represent object data
+ * @return mixed number, boolean, string, array, or object corresponding to given JSON input string.
+ * Note that decode() always returns strings in ASCII or UTF-8 format!
+ * @access public
+ */
+ public static function decode($str, $useArray=true)
+ {
+ if(function_exists('json_decode'))
+ return json_decode($str,$useArray);
+
+ $str = self::reduceString($str);
+
+ switch (strtolower($str)) {
+ case 'true':
+ return true;
+
+ case 'false':
+ return false;
+
+ case 'null':
+ return null;
+
+ default:
+ if (is_numeric($str)) {
+ // Lookie-loo, it's a number
+
+ // This would work on its own, but I'm trying to be
+ // good about returning integers where appropriate:
+ // return (float)$str;
+
+ // Return float or int, as appropriate
+ return ((float)$str == (integer)$str)
+ ? (integer)$str
+ : (float)$str;
+
+ } elseif (preg_match('/^("|\').+(\1)$/s', $str, $m) && $m[1] == $m[2]) {
+ // STRINGS RETURNED IN UTF-8 FORMAT
+ $delim = substr($str, 0, 1);
+ $chrs = substr($str, 1, -1);
+ $utf8 = '';
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
+
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+ $ord_chrs_c = ord($chrs{$c});
+
+ switch (true) {
+ case $substr_chrs_c_2 == '\b':
+ $utf8 .= chr(0x08);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\t':
+ $utf8 .= chr(0x09);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\n':
+ $utf8 .= chr(0x0A);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\f':
+ $utf8 .= chr(0x0C);
+ ++$c;
+ break;
+ case $substr_chrs_c_2 == '\r':
+ $utf8 .= chr(0x0D);
+ ++$c;
+ break;
+
+ case $substr_chrs_c_2 == '\\"':
+ case $substr_chrs_c_2 == '\\\'':
+ case $substr_chrs_c_2 == '\\\\':
+ case $substr_chrs_c_2 == '\\/':
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
+ $utf8 .= $chrs{++$c};
+ }
+ break;
+
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
+ // single, escaped unicode character
+ $utf16 = chr(hexdec(substr($chrs, ($c+2), 2)))
+ . chr(hexdec(substr($chrs, ($c+4), 2)));
+ $utf8 .= self::utf16beToUTF8($utf16);
+ $c+=5;
+ break;
+
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
+ $utf8 .= $chrs{$c};
+ break;
+
+ case ($ord_chrs_c & 0xE0) == 0xC0:
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 2);
+ ++$c;
+ break;
+
+ case ($ord_chrs_c & 0xF0) == 0xE0:
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 3);
+ $c += 2;
+ break;
+
+ case ($ord_chrs_c & 0xF8) == 0xF0:
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 4);
+ $c += 3;
+ break;
+
+ case ($ord_chrs_c & 0xFC) == 0xF8:
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 5);
+ $c += 4;
+ break;
+
+ case ($ord_chrs_c & 0xFE) == 0xFC:
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
+ $utf8 .= substr($chrs, $c, 6);
+ $c += 5;
+ break;
+
+ }
+
+ }
+
+ return $utf8;
+
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
+ // array, or object notation
+
+ if ($str{0} == '[') {
+ $stk = array(self::JSON_IN_ARR);
+ $arr = array();
+ } else {
+ if ($useArray) {
+ $stk = array(self::JSON_IN_OBJ);
+ $obj = array();
+ } else {
+ $stk = array(self::JSON_IN_OBJ);
+ $obj = new stdClass();
+ }
+ }
+
+ array_push($stk, array('what' => self::JSON_SLICE,
+ 'where' => 0,
+ 'delim' => false));
+
+ $chrs = substr($str, 1, -1);
+ $chrs = self::reduceString($chrs);
+
+ if ($chrs == '') {
+ if (reset($stk) == self::JSON_IN_ARR) {
+ return $arr;
+
+ } else {
+ return $obj;
+
+ }
+ }
+
+ //print("\nparsing {$chrs}\n");
+
+ $strlen_chrs = strlen($chrs);
+
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
+
+ $top = end($stk);
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
+
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == self::JSON_SLICE))) {
+ // found a comma that is not inside a string, array, etc.,
+ // OR we've reached the end of the character list
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
+ array_push($stk, array('what' => self::JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ if (reset($stk) == self::JSON_IN_ARR) {
+ // we are in an array, so just push an element onto the stack
+ array_push($arr, self::decode($slice,$useArray));
+
+ } elseif (reset($stk) == self::JSON_IN_OBJ) {
+ // we are in an object, so figure
+ // out the property name and set an
+ // element in an associative array,
+ // for now
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // "name":value pair
+ $key = self::decode($parts[1],$useArray);
+ $val = self::decode($parts[2],$useArray);
+
+ if ($useArray) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
+ // name:value pair, where name is unquoted
+ $key = $parts[1];
+ $val = self::decode($parts[2],$useArray);
+
+ if ($useArray) {
+ $obj[$key] = $val;
+ } else {
+ $obj->$key = $val;
+ }
+ }
+
+ }
+
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != self::JSON_IN_STR)) {
+ // found a quote, and we are not inside a string
+ array_push($stk, array('what' => self::JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
+ //print("Found start of string at {$c}\n");
+
+ } elseif (($chrs{$c} == $top['delim']) &&
+ ($top['what'] == self::JSON_IN_STR) &&
+ (($chrs{$c - 1} != "\\") ||
+ ($chrs{$c - 1} == "\\" && $chrs{$c - 2} == "\\"))) {
+ // found a quote, we're in a string, and it's not escaped
+ array_pop($stk);
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '[') &&
+ in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
+ // found a left-bracket, and we are in an array, object, or slice
+ array_push($stk, array('what' => self::JSON_IN_ARR, 'where' => $c, 'delim' => false));
+ //print("Found start of array at {$c}\n");
+
+ } elseif (($chrs{$c} == ']') && ($top['what'] == self::JSON_IN_ARR)) {
+ // found a right-bracket, and we're in an array
+ array_pop($stk);
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($chrs{$c} == '{') &&
+ in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
+ // found a left-brace, and we are in an array, object, or slice
+ array_push($stk, array('what' => self::JSON_IN_OBJ, 'where' => $c, 'delim' => false));
+ //print("Found start of object at {$c}\n");
+
+ } elseif (($chrs{$c} == '}') && ($top['what'] == self::JSON_IN_OBJ)) {
+ // found a right-brace, and we're in an object
+ array_pop($stk);
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ } elseif (($substr_chrs_c_2 == '/*') &&
+ in_array($top['what'], array(self::JSON_SLICE, self::JSON_IN_ARR, self::JSON_IN_OBJ))) {
+ // found a comment start, and we are in an array, object, or slice
+ array_push($stk, array('what' => self::JSON_IN_CMT, 'where' => $c, 'delim' => false));
+ $c++;
+ //print("Found start of comment at {$c}\n");
+
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == self::JSON_IN_CMT)) {
+ // found a comment end, and we're in one now
+ array_pop($stk);
+ $c++;
+
+ for ($i = $top['where']; $i <= $c; ++$i)
+ $chrs = substr_replace($chrs, ' ', $i, 1);
+
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
+
+ }
+
+ }
+
+ if (reset($stk) == self::JSON_IN_ARR) {
+ return $arr;
+
+ } elseif (reset($stk) == self::JSON_IN_OBJ) {
+ return $obj;
+
+ }
+
+ }
+ }
+ }
+
+ /**
+ * This function returns any UTF-8 encoded text as a list of
+ * Unicode values:
+ * @param string $str string to convert
+ * @return string
+ * @author Scott Michael Reynen <scott@randomchaos.com>
+ * @link http://www.randomchaos.com/document.php?source=php_and_unicode
+ * @see unicodeToUTF8()
+ */
+ protected static function utf8ToUnicode( &$str )
+ {
+ $unicode = array();
+ $values = array();
+ $lookingFor = 1;
+
+ for ($i = 0; $i < strlen( $str ); $i++ )
+ {
+ $thisValue = ord( $str[ $i ] );
+ if ( $thisValue < 128 )
+ $unicode[] = $thisValue;
+ else
+ {
+ if ( count( $values ) == 0 )
+ $lookingFor = ( $thisValue < 224 ) ? 2 : 3;
+ $values[] = $thisValue;
+ if ( count( $values ) == $lookingFor )
+ {
+ $number = ( $lookingFor == 3 ) ?
+ ( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ):
+ ( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 );
+ $unicode[] = $number;
+ $values = array();
+ $lookingFor = 1;
+ }
+ }
+ }
+ return $unicode;
+ }
+
+ /**
+ * This function converts a Unicode array back to its UTF-8 representation
+ * @param string $str string to convert
+ * @return string
+ * @author Scott Michael Reynen <scott@randomchaos.com>
+ * @link http://www.randomchaos.com/document.php?source=php_and_unicode
+ * @see utf8ToUnicode()
+ */
+ protected static function unicodeToUTF8( &$str )
+ {
+ $utf8 = '';
+ foreach( $str as $unicode )
+ {
+ if ( $unicode < 128 )
+ {
+ $utf8.= chr( $unicode );
+ }
+ elseif ( $unicode < 2048 )
+ {
+ $utf8.= chr( 192 + ( ( $unicode - ( $unicode % 64 ) ) / 64 ) );
+ $utf8.= chr( 128 + ( $unicode % 64 ) );
+ }
+ else
+ {
+ $utf8.= chr( 224 + ( ( $unicode - ( $unicode % 4096 ) ) / 4096 ) );
+ $utf8.= chr( 128 + ( ( ( $unicode % 4096 ) - ( $unicode % 64 ) ) / 64 ) );
+ $utf8.= chr( 128 + ( $unicode % 64 ) );
+ }
+ }
+ return $utf8;
+ }
+
+ /**
+ * UTF-8 to UTF-16BE conversion.
+ *
+ * Maybe really UCS-2 without mb_string due to utf8ToUnicode limits
+ * @param string $str string to convert
+ * @param boolean $bom whether to output BOM header
+ * @return string
+ */
+ protected static function utf8ToUTF16BE(&$str, $bom = false)
+ {
+ $out = $bom ? "\xFE\xFF" : '';
+ if(function_exists('mb_convert_encoding'))
+ return $out.mb_convert_encoding($str,'UTF-16BE','UTF-8');
+
+ $uni = self::utf8ToUnicode($str);
+ foreach($uni as $cp)
+ $out .= pack('n',$cp);
+ return $out;
+ }
+
+ /**
+ * UTF-8 to UTF-16BE conversion.
+ *
+ * Maybe really UCS-2 without mb_string due to utf8ToUnicode limits
+ * @param string $str string to convert
+ * @return string
+ */
+ protected static function utf16beToUTF8(&$str)
+ {
+ $uni = unpack('n*',$str);
+ return self::unicodeToUTF8($uni);
+ }
+}
diff --git a/framework/web/helpers/CJavaScript.php b/framework/web/helpers/CJavaScript.php
new file mode 100644
index 0000000..7454f17
--- /dev/null
+++ b/framework/web/helpers/CJavaScript.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * CJavaScript helper class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 2008-2011 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+/**
+ * CJavaScript is a helper class containing JavaScript-related handling functions.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CJavaScript.php 2799 2011-01-01 19:31:13Z qiang.xue $
+ * @package system.web.helpers
+ * @since 1.0
+ */
+class CJavaScript
+{
+ /**
+ * Quotes a javascript string.
+ * After processing, the string can be safely enclosed within a pair of
+ * quotation marks and serve as a javascript string.
+ * @param string $js string to be quoted
+ * @param boolean $forUrl whether this string is used as a URL
+ * @return string the quoted string
+ */
+ public static function quote($js,$forUrl=false)
+ {
+ if($forUrl)
+ return strtr($js,array('%'=>'%25',"\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\','</'=>'<\/'));
+ else
+ return strtr($js,array("\t"=>'\t',"\n"=>'\n',"\r"=>'\r','"'=>'\"','\''=>'\\\'','\\'=>'\\\\','</'=>'<\/'));
+ }
+
+ /**
+ * Encodes a PHP variable into javascript representation.
+ *
+ * Example:
+ * <pre>
+ * $options=array('key1'=>true,'key2'=>123,'key3'=>'value');
+ * echo CJavaScript::encode($options);
+ * // The following javascript code would be generated:
+ * // {'key1':true,'key2':123,'key3':'value'}
+ * </pre>
+ *
+ * For highly complex data structures use {@link jsonEncode} and {@link jsonDecode}
+ * to serialize and unserialize.
+ *
+ * @param mixed $value PHP variable to be encoded
+ * @return string the encoded string
+ */
+ public static function encode($value)
+ {
+ if(is_string($value))
+ {
+ if(strpos($value,'js:')===0)
+ return substr($value,3);
+ else
+ return "'".self::quote($value)."'";
+ }
+ else if($value===null)
+ return 'null';
+ else if(is_bool($value))
+ return $value?'true':'false';
+ else if(is_integer($value))
+ return "$value";
+ else if(is_float($value))
+ {
+ if($value===-INF)
+ return 'Number.NEGATIVE_INFINITY';
+ else if($value===INF)
+ return 'Number.POSITIVE_INFINITY';
+ else
+ return rtrim(sprintf('%.16F',$value),'0'); // locale-independent representation
+ }
+ else if(is_object($value))
+ return self::encode(get_object_vars($value));
+ else if(is_array($value))
+ {
+ $es=array();
+ if(($n=count($value))>0 && array_keys($value)!==range(0,$n-1))
+ {
+ foreach($value as $k=>$v)
+ $es[]="'".self::quote($k)."':".self::encode($v);
+ return '{'.implode(',',$es).'}';
+ }
+ else
+ {
+ foreach($value as $v)
+ $es[]=self::encode($v);
+ return '['.implode(',',$es).']';
+ }
+ }
+ else
+ return '';
+ }
+
+ /**
+ * Returns the JSON representation of the PHP data.
+ * @param mixed $data the data to be encoded
+ * @return string the JSON representation of the PHP data.
+ */
+ public static function jsonEncode($data)
+ {
+ return CJSON::encode($data);
+ }
+
+ /**
+ * Decodes a JSON string.
+ * @param string $data the data to be decoded
+ * @param boolean $useArray whether to use associative array to represent object data
+ * @return mixed the decoded PHP data
+ */
+ public static function jsonDecode($data,$useArray=true)
+ {
+ return CJSON::decode($data,$useArray);
+ }
+}