Skip to content

Instantly share code, notes, and snippets.

@Deele
Created January 7, 2015 22:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Deele/2d8a96070dfa87af4d1b to your computer and use it in GitHub Desktop.
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
/**
* Returns whether given variable is undefined
*
* @author Nils Lindentals <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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 <nils@dfworks.lv>
* @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