UNPKG

wns-mvc-package

Version:

MVC package/bundle for WNS Middleware

1,271 lines (1,190 loc) 109 kB
/** * WNS's HTTP Package * @copyright &copy; 2013- Pedro Nasser &reg; * @license: MIT * @page http://github.com/pedronasser/wns-http-package */ /** * @class wnHtml * @desc html helpers * @author Pedro Nasser */ module.exports = { /** * WNS Class dependencies */ extend: ['wnComponent'], /** * NPM dependencies */ dependencies: ['url'], /** * PRIVATE */ private: {}, /** * Public Variables */ public: { ID_PREFIX: 'wn', /** * @var string the CSS class for displaying error summaries (see {@link errorSummary}). */ errorSummaryCss:'errorSummary', /** * @var string the CSS class for displaying error messages (see {@link error}). */ 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. */ errorCss:'error', /** * @var string the tag name for the error container tag. Defaults to 'div'. * @since 1.1.13 */ errorContainerTag:'div', /** * @var string the CSS class for required labels. Defaults to 'required'. * @see label */ requiredCss:'required', /** * @var string the HTML code to be prepended to the required label. * @see label */ beforeRequiredLabel:'', /** * @var string the HTML code to be appended to the required label. * @see label */ afterRequiredLabel:' <span class:"required">*</span>', /** * @var integer the counter for generating automatic input field names. */ count:0, /** * Sets the default style for attaching jQuery event handlers. * * If set to true (default), event handlers are delegated. * 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, event handlers are directly bound. * 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 delegate 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 */ liveEvents:true, /** * @var boolean whether to close single tags. Defaults to true. Can be set to false for HTML5. * @since 1.1.13 */ closeSingleTags:true, /** * @var boolean whether to render special attributes value. Defaults to true. Can be set to false for HTML5. * @since 1.1.13 */ renderSpecialAttributesValue:true, }, /** * Extended methods */ methods: { /** * 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 */ encode: function (text) { return this.getParent().html.encoder.htmlspecialchars(text,'ENT_QUOTES',this.getParent().charset); }, /** * Replace an array of strings with other array * of string on the target string. * @param array $find find * @param array $replace replace * @param string $string target string * @return string result */ replaceArray: function(find, replace, string) { var replaceString = string+''; for (var i = 0; i < find.length; i++) { replaceString = replaceString.replace(find[i], replace[i]); } return replaceString; }, /** * PHP's strstr() function in Javascript. * @param string $haystack haystack * @param string $haystack needle * @param boolean $bool bool * @return string result */ strstr: function(haystack, needle, bool) { var pos = 0; haystack += ''; pos = haystack.indexOf(needle); if (pos == -1) { return false; } else { if (bool) { return haystack.substr(0, pos); } else { return haystack.slice(pos); } } }, /** * 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 */ decode: function (text) { return this.getParent().html.encoder.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 */ encodeArray: function (data) { d=[]; for (d in data) { var key = d, value = data[d]; if('string' === typeof (key)) key=this.getParent().html.encoder.htmlspecialchars(key,'ENT_QUOTES',this.getParent().charset); if('string' === typeof (value)) value=this.getParent().html.encoder.htmlspecialchars(value,'ENT_QUOTES',this.getParent().charset); else if(Array.isArray(value)) value=self.encodeArray(value); d[key]=value; } return d; }, /** * Generates an HTML element. * @param string $tag the tag name * @param object $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 */ tag: function (tag,htmlOptions,content,closeTag) { var tag = tag!=undefined ? tag : '', content = content!=undefined ? content : false, closeTag = closeTag!=undefined ? closeTag : true; html='<' + tag + self.renderAttributes(htmlOptions||{}); if(content===false) return closeTag && self.closeSingleTags ? html+' />' : html+'>'; else return closeTag ? html+'>'+content+'</'+tag+'>' : html+'>'+content; }, /** * Generates an open HTML element. * @param string $tag the tag name * @param object $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 */ openTag: function (tag,htmlOptions) { return '<' + tag + self.renderAttributes(htmlOptions) + '>'; }, /** * Generates a close HTML element. * @param string $tag the tag name * @return string the generated HTML element tag */ closeTag: function (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. */ cdata: function (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 object $options other options in name-value pairs (e.g. 'scheme', 'lang') * @return string the generated meta tag */ metaTag: function (content,name,httpEquiv,options) { var options = options || {}; 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 */ linkTag: function (relation,type,href,media,options) { var options = options || {}; 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 */ css: function (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 */ refresh: function (seconds,url) { // content=seconds+""; // if(url&&url!=='') // content+=';url='+self.normalizeUrl(url+''); // this.getParent().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. */ cssFile: function (url,media) { return self.linkTag('stylesheet','text/css',url,media!=='' ? media : null); }, /** * Encloses the given JavaScript within a script tag. * @param string $text the JavaScript to be enclosed * @param object $htmlOptions additional HTML attributes (see {@link tag}) * @return string the enclosed JavaScript */ script: function (text,htmlOptions) { defaultHtmlOptions={ 'type':'text/javascript', }; htmlOptions=_.merge(htmlOptions,defaultHtmlOptions); return self.tag('script',htmlOptions,"\n/*<![CDATA[*/\n"+(text||'')+"\n/*]]>*/\n"); }, /** * Includes a JavaScript file. * @param string $url URL for the JavaScript file * @param object $htmlOptions additional HTML attributes (see {@link tag}) * @return string the JavaScript file tag */ scriptFile: function (url,htmlOptions) { defaultHtmlOptions={ 'type':'text/javascript', 'src':url||'' }; htmlOptions=_.merge(defaultHtmlOptions,htmlOptions); return self.tag('script',htmlOptions,''); }, /** * 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 object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. */ form: function (action,method,htmlOptions) { var method = method || 'post', action = action || ''; 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 object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. * @see endForm */ beginForm: function (action,method,htmlOptions) { var method = method || 'post', action = action || '', htmlOptions = htmlOptions || {}; htmlOptions['action']=_url=self.normalizeUrl(action)+''; htmlOptions['method']=method; form=self.tag('form',htmlOptions,false,false); hiddens=[]; if('get'!==method.toLowerCase() && (pos=_url.indexOf('?'))!==-1) { var explode = _url.substr(pos+1).split('&'); for (e in explode) { var pair = explode[e]; if((pos=pair.indexOf('='))!==-1) hiddens.push(self.hiddenField(decodeURIComponent(pair.substr(0,pos).replace(/\+/g, ' ')),decodeURIComponent(url.parse(pair.substr(pos+1), true).path.replace(/\+/g, ' ')),{'id':false})); else hiddens.push(self.hiddenField(decodeURIComponent(pair.replace(/\+/g, ' ')),'',{'id':false})); } } request=this.getParent().request; // if(request.enableCsrfValidation && !strcasecmp(method,'post')) // hiddens.push(self.hiddenField(request.csrfTokenName,request.getCsrfToken(),{'id':false})); // if(hiddens!==[]) // form+="\n"+self.tag('div',{'style':'display:none'},hiddens.join("\n")); return form; }, /** * Generates a closing form tag. * @return string the generated tag * @see beginForm */ endForm: function () { 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 object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated form tag. */ statefulForm: function (action,method,htmlOptions) { var method = method || 'post', action = action || ''; return self.form(action,method,htmlOptions)+"\n"+ self.tag('div',{'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 pageStateField: function (value) { return '<input type="hidden" name="'+this.getParent().controller.stateInputName+'" 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 object $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 */ link: function (text,url,htmlOptions) { var url = url || '#'; 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 object $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 */ mailto: function (text,email,htmlOptions) { if(email==='') email=text; return self.link(text,'mailto:'+email,htmlOptions,true); }, /** * Generates an image tag. * @param string $src the image URL * @param string $alt the alternative text display * @param object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated image tag */ image: function (src,alt,htmlOptions) { htmlOptions['src']=src; htmlOptions['alt']=alt||''; return self.tag('img',htmlOptions); }, /** * Generates a button. * @param string $label the button label * @param object $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 */ button: function (label,htmlOptions) { var label = label||'button'; if(undefined === (htmlOptions['name'])) { if(htmlOptions.name !== undefined) htmlOptions.name=self.ID_PREFIX+self.count++; } if(undefined === (htmlOptions['type'])) htmlOptions['type']='button'; if(undefined === (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 object $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 */ htmlButton: function (label,htmlOptions) { var label = label||'button'; if(undefined === (htmlOptions['name'])) htmlOptions['name']=self.ID_PREFIX+self.count++; if(undefined === (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 object $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 */ submitButton: function (label,htmlOptions) { var label = label||'submit'; htmlOptions['type']='submit'; return self.button(label,htmlOptions); }, /** * Generates a reset button. * @param string $label the button label * @param object $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 */ resetButton: function (label,htmlOptions) { var label = label||'reset'; htmlOptions['type']='reset'; return self.button(label,htmlOptions); }, /** * Generates an image submit button. * @param string $src the image URL * @param object $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 */ imageButton: function (src,htmlOptions) { htmlOptions['src']=src||''; htmlOptions['type']='image'; return self.button('submit',htmlOptions); }, /** * Generates a link submit button. * @param string $label the button label * @param object $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 */ linkButton: function (label,htmlOptions) { var label = label||'submit'; if(undefined === (htmlOptions['submit'])) htmlOptions['submit']=(undefined !== 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 object $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 self.$requiredCss), * and be decorated with {@link self.beforeRequiredLabel} and * {@link self.afterRequiredLabel}.</li> * </ul> * @return string the generated label tag */ label: function (label,_for,htmlOptions) { if(_for===false) delete (htmlOptions['for']); else htmlOptions['for']=_for; if('undefined' === typeof (htmlOptions['required'])) { if(htmlOptions['required']) { if('undefined' === typeof (htmlOptions['class'])) htmlOptions['class']+=' '+self.requiredCss; else htmlOptions['class']=self.requiredCss; label=self.beforeRequiredLabel.label.self.afterRequiredLabel; } delete (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 object $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 */ textField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('text',name,value,htmlOptions); }, /** * Generates a number field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ numberField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('number',name,value,htmlOptions); }, /** * Generates a range field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ rangeField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('range',name,value,htmlOptions); }, /** * Generates a date field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ dateField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('date',name,value,htmlOptions); }, /** * Generates a time field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ timeField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('time',name,value,htmlOptions); }, /** * Generates an email field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ emailField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('email',name,value,htmlOptions); }, /** * Generates a telephone field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ telField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('tel',name,value,htmlOptions); }, /** * Generates a URL field input. * @param string $name the input name * @param string $value the input value * @param object $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 * @since 1.1.14 */ urlField: function (name,value,htmlOptions) { self.clientChange('change',htmlOptions); return self.inputField('url',name,value,htmlOptions); }, /** * Generates a hidden input. * @param string $name the input name * @param string $value the input value * @param object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated input field * @see inputField */ hiddenField: function (name,value,htmlOptions) { 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 object $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 */ passwordField: function (name,value,htmlOptions) { 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 object $htmlOptions additional HTML attributes (see {@link tag}). * @return string the generated input field * @see inputField */ fileField: function (name,value,htmlOptions) { 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 object $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 */ textArea: function (name,value,htmlOptions) { var name = arguments[0] || ''; var value = (typeof arguments[1]=='string'?arguments[1]:''); var htmlOptions = (typeof arguments[1]=='object'?arguments[1]:arguments[2]) || {};3 htmlOptions['name']=name||''; if(undefined === (htmlOptions['id'])) htmlOptions['id']=self.getIdByName(name); else if(htmlOptions['id']===false) delete (htmlOptions['id']); self.clientChange('change',htmlOptions); var tag = self.tag('textarea',htmlOptions,(undefined !== htmlOptions['encode'] && !htmlOptions['encode']) ? value : self.encode(value)); return tag; }, /** * Generates a radio button. * @param string $name the input name * @param boolean $checked whether the radio button is checked * @param object $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 */ radioButton: function (name,checked,htmlOptions) { if(checked) htmlOptions['checked']='checked'; else delete (htmlOptions['checked']); value=(undefined !== htmlOptions['value']) ? htmlOptions['value'] : 1; self.clientChange('click',htmlOptions); if(htmlOptions.uncheckedValue!==undefined) { uncheck=htmlOptions['uncheckValue']; delete (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('undefined' === typeof (htmlOptions['id']) && htmlOptions['id']!==false) uncheckOptions={'id':self.ID_PREFIX+htmlOptions['id']}; else uncheckOptions={'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 object $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 */ checkBox: function (name,checked,htmlOptions) { if(checked) htmlOptions['checked']='checked'; else delete (htmlOptions['checked']); value=(undefined !== htmlOptions['value']) ? htmlOptions['value'] : 1; self.clientChange('click',htmlOptions); if(htmlOptions.uncheckValue!==undefined) { uncheck=htmlOptions['uncheckValue']; delete (htmlOptions['uncheckValue']); } else uncheck=null; if(uncheck!==null) { // add a hidden field so that if the check box is not checked, it still submits a value if('undefined' === typeof (htmlOptions['id']) && htmlOptions['id']!==false) uncheckOptions={'id':self.ID_PREFIX+htmlOptions['id']}; else uncheckOptions={'id':false}; hidden=self.hiddenField(name,uncheck,uncheckOptions); } else hidden=''; // add a hidden field so that if the check box is not checked, 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 object $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> * Since 1.1.13, a special option named 'unselectValue' is available. It can be used to set the value * that will be returned when no option is selected in multiple mode. When set, a hidden field is * rendered so that if no option is selected in multiple mode, we can still obtain the posted * unselect value. If 'unselectValue' is not set or set to NULL, the hidden field will not be rendered. * @return string the generated drop down list * @see clientChange * @see inputField * @see listData */ dropDownList: function (name,select,data,htmlOptions) { htmlOptions['name']=name; if(undefined === (htmlOptions['id'])) htmlOptions['id']=self.getIdByName(name); else if(htmlOptions['id']===false) delete (htmlOptions['id']); self.clientChange('change',htmlOptions); options="\n"+self.listOptions(select,data,htmlOptions); hidden=''; if(!self.getParent().html.encoder.isEmpty(htmlOptions['multiple'])) { if(substr(htmlOptions['name'],-2)!=='[]') htmlOptions['name']+='[]'; if('undefined' === typeof (htmlOptions['unselectValue'])) { hiddenOptions=(undefined !== htmlOptions['id']) ? {'id':self.ID_PREFIX+htmlOptions['id']} : {'id':false}; hidden=self.hiddenField(substr(htmlOptions['name'],0,-2),htmlOptions['unselectValue'],hiddenOptions); delete (htmlOptions['unselectValue']); } } // add a hidden field so that if the option is not selected, it still submits a value return hidden + 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 object $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 */ listBox: function (name,select,data,htmlOptions) { if(undefined === (htmlOptions['size'])) htmlOptions['size']=4; if(!self.getParent().html.encoder.isEmpty(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 object $htmlOptions additional 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, * {beginLabel} will be replaced by &lt;label&gt; with labelOptions, {labelTitle} will be replaced * by the corresponding check box label title and {endLabel} will be replaced by &lt;/label&gt;</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> * <li>container: string, specifies the checkboxes enclosing tag. Defaults to 'span'. * If the value is an empty string, no enclosing tag will be generated</li> * <li>baseID: string, specifies the base ID prefix to be used for checkboxes in the list. * This option is available since version 1.1.13.</li> * </ul> * @return string the generated check box list */ checkBoxList: function (name,select,data,htmlOptions) { template=(undefined !== htmlOptions['template'])?htmlOptions['template']:'{input} {label}'; separator=(undefined !== htmlOptions['separator'])?htmlOptions['separator']:"<br/>\n"; container=(undefined !== htmlOptions['container'])?htmlOptions['container']:'span'; delete (htmlOptions['template'],htmlOptions['separator'],htmlOptions['container']); if(substr(name,-2)!=='[]') name+='[]'; if('undefined' === typeof (htmlOptions['checkAll'])) { checkAllLabel=htmlOptions['checkAll']; checkAllLast=(undefined !== htmlOptions['checkAllLast']) && htmlOptions['checkAllLast']; } delete (htmlOptions['checkAll'],htmlOptions['checkAllLast']); labelOptions=(undefined !== htmlOptions['labelOptions'])?htmlOptions['labelOptions']:{}; delete (htmlOptions['labelOptions']); items=[]; baseID=(undefined !== htmlOptions['baseID']) ? htmlOptions['baseID'] : self.getIdByName(name); delete (htmlOptions['baseID']); id=0; checkAll=true; for(d in data) { var value=d, labelTitle=data[d] checked=!Array.isArray(select) && !strcmp(value,select) || Array.isArray(select) && in_array(value,select); checkAll=checkAll && checked; htmlOptions['value']=value; htmlOptions['id']=baseID+'_'+id++; option=self.checkBox(name,checked,htmlOptions); beginLabel=self.openTag('label',labelOptions); label=self.label(labelTitle,htmlOptions['id'],labelOptions); endLabel=self.closeTag('label'); items.push(self.strtr(template,{ '{input}':option, '{beginLabel}':beginLabel, '{label}':label, '{labelTitle}':labelTitle, '{endLabel}':endLabel, })); } if('undefined' === typeof (checkAllLabel)) { htmlOptions['value']=1; htmlOptions['id']=id=baseID+'_all'; option=self.checkBox(id,checkAll,htmlOptions); beginLabel=self.openTag('label',labelOptions); label=self.label(checkAllLabel,id,labelOptions); endLabel=self.closeTag('label'); item=self.strtr(template,{ '{input}':option, '{beginLabel}':beginLabel, '{label}':label, '{labelTitle}':checkAllLabel, '{endLabel}':endLabel, }); if(checkAllLast) items.push(item); else items.unshift(item) name=self.strtr(name,{'[':'\\[',']':'\\]'}); var js="\n"; js+=" jQuery('#$id').click(function() {\n"; js+=" jQuery(\"input[name='$name']\").prop('checked', this.checked);\n"; js+=" });\n"; js+=" jQuery(\"input[name='$name']\").click(function() {\n"; js+=" jQuery('#$id').prop('checked', !jQuery(\"input[name='$name']:not(:checked)\").length);\n"; js+=" });\n"; js+=" jQuery('#$id').prop('checked', !jQuery(\"input[name='$name']:not(:checked)\").length);"; cs=this.getParent().getClientScript(); cs.registerCoreScript('jquery'); cs.registerScript(id,js); } if(self.getParent().html.encoder.isEmpty(container)) return implode(separator,items); else return self.tag(container,{'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 object $htmlOptions additional 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, * {beginLabel} will be replaced by &lt;label&gt; with labelOptions, {labelTitle} will be replaced * by the corresponding radio button label title and {endLabel} will be replaced by &lt;/label&gt;</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> * <li>container: string, specifies the radio buttons enclosing tag. Defaults to 'span'. * If the value is an empty string, no enclosing tag will be generated</li> * <li>baseID: string, specifies the base ID prefix to be used for radio buttons in the list. * This option is available since version 1.1.13.</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 radio button at the beginning. Note, the text label will NOT be HTML-encoded. * This option is available since version 1.1.14.</li> * </ul> * @return string the generated radio button list */ radioButtonList: function (name,select,data,htmlOptions) { template=(undefined !== htmlOptions['template'])?htmlOptions['template']:'{input} {label}'; separator=(undefined !== htmlOptions['separator'])?htmlOptions['separator']:"<br/>\n"; container=(undefined !== htmlOptions['container'])?htmlOptions['container']:'span'; delete (htmlOptions['template'],htmlOptions['separator'],htmlOptions['container']); labelOptions=(undefined !== htmlOptions['labelOptions'])?htmlOptions['labelOptions']:{}; delete (htmlOptions['labelOptions']); if('undefined' === typeof (htmlOptions['empty'])) { if(!Array.isArray(htmlOptions['empty'])) htmlOptions['empty']={'':htmlOptions['empty']}; data=_.merge(htmlOptions['empty'],data); delete (htmlOptions['empty']); } items=[]; baseID=(undefined !== htmlOptions['baseID']) ? htmlOptions['baseID'] : self.getIdByName(name); delete (htmlOptions['baseID']); id=0; for(d in data) { var value=d, labelTitle=data[d] checked=!strcmp(value,select); htmlOptions['value']=value; htmlOptions['id']=baseID+'_'+id++; option=self.radioButton(name,checked,htmlOptions); beginLabel=self.openTag('label',labelOptions); label=self.label(labelTitle,htmlOptions['id'],labelOptions); endLabel=self.closeTag('label'); items.push(self.strtr(template,{ '{input}':option, '{beginLabel}':beginLabel, '{label}':label, '{labelTitle}':labelTitle, '{endLabel}':endLabel, })); } if(self.getParent().html.encoder.isEmpty(container)) return implode(separator,items); else return self.tag(container,{'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 object $ajaxOptions AJAX options (see {@link a