Created
January 7, 2015 22:24
-
-
Save Deele/2d8a96070dfa87af4d1b to your computer and use it in GitHub Desktop.
Port of Yii2 BaseHtml class that provides a set of static methods for generating commonly used HTML tags
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Returns whether given variable is undefined | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isUndefined(variable) { | |
return (typeof(variable) === typeof(undefined)); | |
} | |
/** | |
* Returns whether given variable is defined | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isDefined(variable) { | |
return !isUndefined(variable); | |
} | |
/** | |
* Returns whether given variable is a string | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isString(variable) { | |
return (typeof(variable) === 'string' || variable instanceof String); | |
} | |
/** | |
* Returns whether given variable is an array | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isArray(variable) { | |
return (Object.prototype.toString.call(variable) === '[object Array]'); | |
} | |
/** | |
* Returns whether given variable is function | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isFunction(variable) { | |
return Object.prototype.toString.call(variable) == '[object Function]'; | |
} | |
/** | |
* Returns whether given variable is boolean | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param variable | |
* @returns {boolean} | |
*/ | |
function isBool(variable) { | |
return (typeof(variable) === 'Boolean'); | |
} | |
/** | |
* Returns whether given variable is empty array or object | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param obj | |
* @returns {boolean} | |
*/ | |
function isEmpty(obj) { | |
if (obj == null) return true; | |
if (obj.length > 0) return false; | |
if (obj.length === 0) return true; | |
for (var key in obj) { | |
if (obj.hasOwnProperty(key)) return false; | |
} | |
return true; | |
} | |
/** | |
* Returns an object that is amalgamation of two given objects | |
* | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @param obj1 | |
* @param obj2 | |
* @returns {object} | |
*/ | |
function mergeOptions(obj1, obj2) { | |
var obj3 = {}, attrname; | |
for (attrname in obj1) { | |
if (obj1.hasOwnProperty(attrname)) { | |
obj3[attrname] = obj1[attrname]; | |
} | |
} | |
for (attrname in obj2) { | |
if (obj2.hasOwnProperty(attrname)) { | |
obj3[attrname] = obj2[attrname]; | |
} | |
} | |
return obj3; | |
} | |
/** | |
* BaseHtml provides a set of static methods for generating commonly used HTML tags | |
* | |
* Port of Yii2 BaseHtml class (http://www.yiiframework.com/doc-2.0/yii-helpers-basehtml.html) with small | |
* modifications, that require server-side capabilities (like CSRF) | |
* | |
* var h = BaseHtml(); | |
* h.tag('div', 'Foo bar', {'class': 'foo-bar-container'}) | |
* @author Nils Lindentals <[email protected]> | |
* @license GNU GPL v2.0 | |
* | |
* @class BaseHtml | |
* @constructor | |
* | |
* @requires {Function} isDefined() | |
* @requires {Function} isUndefined() | |
* @requires {Function} isBool() | |
* @requires {Function} isArray() | |
* @requires {Function} isEmpty() | |
* @requires {Function} mergeOptions() | |
* @requires {Function} htmlspecialchars() https://github.com/kvz/phpjs/blob/master/functions/strings/htmlspecialchars.js | |
* @requires {Function} htmlspecialchars_decode() https://github.com/kvz/phpjs/blob/master/functions/strings/htmlspecialchars_decode.js | |
* @requires {Function} strcasecmp() https://github.com/kvz/phpjs/blob/master/functions/strings/strcasecmp.js | |
* @requires {Function} urldecode() https://github.com/kvz/phpjs/blob/master/functions/url/urldecode.js | |
* @requires {Function} implode() https://github.com/kvz/phpjs/blob/master/functions/strings/implode.js | |
* @requires {Function} strpos() https://github.com/kvz/phpjs/blob/master/functions/strings/strpos.js | |
* @requires {Function} substr() https://github.com/kvz/phpjs/blob/master/functions/strings/substr.js | |
* @requires {Function} explode() https://github.com/kvz/phpjs/blob/master/functions/strings/explode.js | |
* | |
* @returns {{ | |
* encode: encode, | |
* decode: decode, | |
* renderTagAttributes: renderTagAttributes, | |
* renderTagAttributes: renderTagAttributes, | |
* tag: tag, | |
* beginTag: beginTag, | |
* endTag: endTag, | |
* style: style, | |
* script: script, | |
* cssFile: cssFile, | |
* jsFile: jsFile, | |
* hiddenInput: hiddenInput, | |
* beginForm: beginForm, | |
* endForm: endForm, | |
* a: a, | |
* mailto: mailto, | |
* img: img, | |
* label: label, | |
* button: button, | |
* submitButton: submitButton, | |
* resetButton: resetButton, | |
* input: input, | |
* buttonInput: buttonInput, | |
* submitInput: submitInput, | |
* resetInput: resetInput, | |
* textInput: textInput, | |
* passwordInput: passwordInput, | |
* fileInput: fileInput, | |
* textarea: textarea, | |
* radio: radio, | |
* radio: radio, | |
* }} | |
* | |
* @todo Document every function | |
* @todo Implement intialization options for BaseHtml() function | |
* @todo Think of "Model" class implementation for all of those "active*" fields | |
* @todo checkbox | |
* @todo dropDownList | |
* @todo listBox | |
* @todo checkboxList | |
* @todo radioList | |
* @todo ul | |
* @todo ol | |
* @todo ol | |
* @todo activeLabel | |
* @todo errorSummary | |
* @todo error | |
* @todo activeInput | |
* @todo activeTextInput | |
* @todo activeHiddenInput | |
* @todo activePasswordInput | |
* @todo activeFileInput | |
* @todo activeTextarea | |
* @todo activeRadio | |
* @todo activeCheckbox | |
* @todo activeDropDownList | |
* @todo activeListBox | |
* @todo activeCheckboxList | |
* @todo activeRadioList | |
* @todo activeListInput | |
* @todo renderSelectOptions | |
* @todo addCssClass | |
* @todo removeCssClass | |
* @todo addCssStyle | |
* @todo removeCssStyle | |
* @todo removeCssStyle | |
* @todo cssStyleFromArray | |
* @todo cssStyleToArray | |
* @todo getAttributeName | |
* @todo getAttributeValue | |
* @todo getInputName | |
* @todo getInputId | |
*/ | |
function BaseHtml() { | |
var settings = { | |
charset: 'UTF-8' | |
}, | |
voidElements = { | |
'area': 1, | |
'base': 1, | |
'br': 1, | |
'col': 1, | |
'command': 1, | |
'embed': 1, | |
'hr': 1, | |
'img': 1, | |
'input': 1, | |
'keygen': 1, | |
'link': 1, | |
'meta': 1, | |
'param': 1, | |
'source': 1, | |
'track': 1, | |
'wbr': 1 | |
}, | |
attributeOrder = [ | |
'type', | |
'id', | |
'class', | |
'name', | |
'value', | |
'href', | |
'src', | |
'action', | |
'method', | |
'selected', | |
'checked', | |
'readonly', | |
'disabled', | |
'multiple', | |
'size', | |
'maxlength', | |
'width', | |
'height', | |
'rows', | |
'cols', | |
'alt', | |
'title', | |
'rel', | |
'media' | |
], | |
functions = {}; | |
functions.encode = function(content, doubleEncode) { | |
if (isUndefined(doubleEncode)) { | |
doubleEncode = true; | |
} | |
return htmlspecialchars(content, ['ENT_QUOTES', 'ENT_SUBSTITUTE'], settings.charset, doubleEncode); | |
}; | |
functions.decode = function(content) { | |
return htmlspecialchars_decode(content, 'ENT_QUOTES'); | |
}; | |
functions.renderTagAttributes = function(attributes) { | |
var name, i; | |
if (attributes.length > 1) { | |
var sorted = []; | |
for (i in attributeOrder) { | |
if (attributeOrder.hasOwnProperty(i)) { | |
name = attributeOrder[i]; | |
if (isDefined(attributes[name])) { | |
sorted[name] = attributes[name]; | |
} | |
} | |
} | |
attributes = mergeOptions(sorted, attributes); | |
} | |
var html = ''; | |
for (name in attributes) { | |
if (attributes.hasOwnProperty(name)) { | |
var value = attributes[name]; | |
if (isBool(value)) { | |
if (value) { | |
html += ' $name'; | |
} | |
} | |
else if (isArray(value) && name === 'data') { | |
for (var n in value) { | |
if (value.hasOwnProperty(n)) { | |
var v = value[n]; | |
if (isArray(v)) { | |
//html += ' '+name+'-'+n+'=\''+Json::encode($v, JSON_HEX_APOS)+'\''; | |
} | |
else { | |
html += ' '+name+'-'+n+'="'+functions.encode(v)+'"'; | |
} | |
} | |
} | |
} | |
else if (value !== null) { | |
html += ' '+name+'="'+functions.encode(value)+'"'; | |
} | |
} | |
} | |
return html; | |
}; | |
functions.tag = function(name, content, options) { | |
if (isUndefined(content)) { | |
content = ''; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
var html = '<'+name+functions.renderTagAttributes(options)+'>'; | |
return isDefined(voidElements[name.toLowerCase()]) ? html : html+content+'</'+name+'>'; | |
}; | |
functions.beginTag = function(name, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
return '<'+name+functions.renderTagAttributes(options)+'>'; | |
}; | |
functions.endTag = function(name) { | |
return '</'+name+'>'; | |
}; | |
functions.style = function(content, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
return functions.tag('style', content, options); | |
}; | |
functions.script = function(content, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
return functions.tag('script', content, options); | |
}; | |
functions.cssFile = function(url, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
if (isUndefined(options['rel'])) { | |
options['rel'] = 'stylesheet'; | |
} | |
options['href'] = url; | |
if (isDefined(options['condition'])) { | |
var condition = options['condition']; | |
delete options['condition']; | |
return '<!--[if '+condition+']>'+"\n"+functions.tag('link', '', options)+"\n<![endif]-->"; | |
} | |
else if (isDefined(options['noscript']) && options['noscript'] === true) { | |
delete options['noscript']; | |
return "<noscript>"+functions.tag('link', '', options)+"</noscript>"; | |
} | |
else { | |
return functions.tag('link', '', options); | |
} | |
}; | |
functions.jsFile = function(url, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['src'] = url; | |
if (isDefined(options['condition'])) { | |
var condition = options['condition']; | |
delete options['condition']; | |
return '<!--[if '+condition+']>'+"\n"+functions.tag('script', '', options)+"\n<![endif]-->"; | |
} | |
else { | |
return functions.tag('script', '', options); | |
} | |
}; | |
functions.hiddenInput = function(name, value, options) { | |
return functions.input('hidden', name, value, options); | |
}; | |
functions.beginForm = function(action, method, options) { | |
if (isUndefined(action)) { | |
action = ''; | |
} | |
if (isUndefined(method)) { | |
method = 'post'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
var hiddenInputs = [], | |
pos = strpos(action, '?'); | |
if (!strcasecmp(method, 'get') && pos !== false) { | |
// query parameters in the action are ignored for GET method | |
// we use hidden fields to add them back | |
var explodedAction = explode('&', substr(action, pos + 1)); | |
for (var i in explodedAction) { | |
if (explodedAction.hasOwnProperty(i)) { | |
var pair = explodedAction[i], | |
pos1 = strpos(pair, '='); | |
if (pos1 !== false) { | |
hiddenInputs.push(functions.hiddenInput( | |
urldecode(substr(pair, 0, pos1)), | |
urldecode(substr(pair, pos1 + 1)) | |
)); | |
} | |
else { | |
hiddenInputs.push(functions.hiddenInput(urldecode(pair), '')); | |
} | |
} | |
} | |
action = substr(action, 0, pos); | |
} | |
options['action'] = action; | |
options['method'] = method; | |
var form = functions.beginTag('form', options); | |
if (!isEmpty(hiddenInputs)) { | |
form += "\n"+implode("\n", hiddenInputs); | |
} | |
return form; | |
}; | |
functions.endForm = function() { | |
return '</form>'; | |
}; | |
functions.a = function(text, url, options) { | |
if (isUndefined(url)) { | |
url = null; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
if (url !== null) { | |
options['href'] = url; | |
} | |
return functions.tag('a', text, options); | |
}; | |
functions.mailto = function(text, email, options) { | |
if (isUndefined(email)) { | |
email = null; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['href'] = 'mailto:'+(email === null ? text : email); | |
return functions.tag('a', text, options); | |
}; | |
functions.img = function(src, options) { | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['src'] = src; | |
if (isUndefined(options['alt'])) { | |
options['alt'] = ''; | |
} | |
return functions.tag('img', '', options); | |
}; | |
functions.label = function(content, _for, options) { | |
if (isUndefined(_for)) { | |
_for = null; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['for'] = _for; | |
return functions.tag('label', content, options); | |
}; | |
functions.button = function(content, options) { | |
if (isUndefined(content)) { | |
content = 'Button'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
if (isUndefined(options['type'])) { | |
options['type'] = 'button'; | |
} | |
return functions.tag('button', content, options); | |
}; | |
functions.submitButton = function(content, options) { | |
if (isUndefined(content)) { | |
content = 'Submit'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = 'submit'; | |
return functions.button(content, options); | |
}; | |
functions.resetButton = function(content, options) { | |
if (isUndefined(content)) { | |
content = 'Reset'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = 'reset'; | |
return functions.button(content, options); | |
}; | |
functions.input = function(type, name, value, options) { | |
if (isUndefined(name)) { | |
name = null; | |
} | |
if (isUndefined(value)) { | |
value = null; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = type; | |
options['name'] = name; | |
options['value'] = value === null ? null : value; | |
return functions.tag('input', '', options); | |
}; | |
functions.buttonInput = function(label, options) { | |
if (isUndefined(label)) { | |
label = 'Button'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = 'button'; | |
options['value'] = label; | |
return functions.tag('input', '', options); | |
}; | |
functions.submitInput = function(label, options) { | |
if (isUndefined(label)) { | |
label = 'Submit'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = 'submit'; | |
options['value'] = label; | |
return functions.tag('input', '', options); | |
}; | |
functions.resetInput = function(label, options) { | |
if (isUndefined(label)) { | |
label = 'Reset'; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['type'] = 'reset'; | |
options['value'] = label; | |
return functions.tag('input', '', options); | |
}; | |
functions.textInput = function(name, value, options) { | |
return functions.input('text', name, value, options); | |
}; | |
functions.passwordInput = function(name, value, options) { | |
return functions.input('password', name, value, options); | |
}; | |
functions.fileInput = function(name, value, options) { | |
return functions.input('file', name, value, options); | |
}; | |
functions.textarea = function(name, value, options) { | |
if (isUndefined(name)) { | |
name = null; | |
} | |
if (isUndefined(value)) { | |
value = ''; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['name'] = name; | |
return functions.tag('textarea', functions.encode(value), options); | |
}; | |
functions.radio = function(name, checked , options) { | |
if (isUndefined(name)) { | |
name = null; | |
} | |
if (isUndefined(checked )) { | |
checked = false; | |
} | |
if (isUndefined(options)) { | |
options = {}; | |
} | |
options['checked'] = Boolean(checked); | |
var value = isDefined(options['value']) ? options['value'] : '1', hidden; | |
if (isDefined(options['uncheck'])) { | |
// add a hidden field so that if the radio button is not selected, it still submits a value | |
hidden = functions.hiddenInput(name, options['uncheck']); | |
delete options['uncheck']; | |
} | |
else { | |
hidden = ''; | |
} | |
if (isDefined(options['label'])) { | |
var label = options['label'], | |
labelOptions = isDefined(options['labelOptions']) ? options['labelOptions'] : {}; | |
delete options['label']; | |
delete options['labelOptions']; | |
var content = functions.label( | |
functions.input('radio', name, value, options)+' '+label, | |
null, | |
labelOptions | |
); | |
return hidden+content; | |
} | |
else { | |
return hidden+functions.input('radio', name, value, options); | |
} | |
}; | |
return functions; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment