/*!
 * Snakeskin v7.5.1 (live)
 * https://github.com/SnakeskinTpl/Snakeskin
 *
 * Released under the MIT license
 * https://github.com/SnakeskinTpl/Snakeskin/blob/master/LICENSE
 *
 * Date: 'Fri, 21 Sep 2018 15:57:44 GMT
 */

(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
	typeof define === 'function' && define.amd ? define('Snakeskin', factory) :
	(global.Snakeskin = factory());
}(this, (function () { 'use strict';

var Snakeskin = void 0;
var Snakeskin$1 = Snakeskin = {
  VERSION: [7, 5, 1]
};

/**
 * The operation UID
 * @type {?string}
 */
Snakeskin.UID = null;

/**
 * The namespace for directives
 * @const
 */
Snakeskin.Directives = {};

/**
 * The namespace for filters
 * @const
 */
Snakeskin.Filters = {};

/**
 * The namespace for super-global variables
 * @const
 */
Snakeskin.Vars = {
  /**
   * Decorator for template overriding
   *
   * @param {string} name
   * @return {!Function}
   */
  override: function override(name) {
    return function (fn, ctx) {
      return ctx[name] = fn;
    };
  },


  /**
   * Decorator for template ignoring
   * @param {!Function} fn
   */
  ignore: function ignore(fn) {
    fn.ignore = true;
  }
};

/**
 * The namespace for local variables
 * @const
 */
Snakeskin.LocalVars = {};

/**
 * The cache of templates
 * @const
 */
Snakeskin.cache = {};

Array.isArray = Array.isArray || function (obj) {
	return {}.call(obj) === '[object Array]';
};

String.prototype.trim = String.prototype.trim || function () {
	var str = this.replace(/^\s\s*/, '');

	var i = str.length;

	for (var rgxp = /\s/; rgxp.test(str.charAt(--i));) {
		// Do nothing
	}

	return str.substring(0, i + 1);
};

/**
 * Returns true if the specified value is a function
 *
 * @param {?} obj - source value
 * @return {boolean}
 */
function isFunction(obj) {
  return typeof obj === 'function';
}

/**
 * Returns true if the specified value is a number
 *
 * @param {?} obj - source value
 * @return {boolean}
 */


/**
 * Returns true if the specified value is a string
 *
 * @param {?} obj - source value
 * @return {boolean}
 */
function isString(obj) {
  return typeof obj === 'string';
}

/**
 * Returns true if the specified value is a boolean
 *
 * @param {?} obj - source value
 * @return {boolean}
 */


/**
 * Returns true if the specified value is an array
 *
 * @param {?} obj - source value
 * @return {boolean}
 */
function isArray(obj) {
  return Array.isArray(obj);
}

/**
 * Returns true if the specified value is a plain object
 *
 * @param {?} obj - source value
 * @return {boolean}
 */
function isObject(obj) {
  return Boolean(obj) && obj.constructor === Object;
}

/**
 * Special Snakeskin class for escaping HTML entities from an object
 *
 * @constructor
 * @param {?} obj - source object
 * @param {?string=} [opt_attr] - type of attribute declaration
 */
Snakeskin$1.HTMLObject = function (obj, opt_attr) {
	this.value = obj;
	this.attr = opt_attr;
};

/**
 * StringBuffer constructor
 *
 * @constructor
 * @return {!Array}
 */
Snakeskin$1.StringBuffer = function () {
	return [];
};

/**
 * @param {!Function} child
 * @param {!Function} parent
 */
function inherit(child, parent) {
	/** @constructor */
	var F = function F() {
		this.constructor = child;
	};

	F.prototype = parent.prototype;
	child.prototype = new F();
}

/**
 * Node constructor
 * @constructor
 */
Snakeskin$1.Node = function () {};

/**
 * Returns the number of child elements
 * @return {number}
 */
Snakeskin$1.Node.prototype.length = function () {
	return this.value.childNodes.length;
};

/**
 * Returns text content
 * @return {string}
 */
Snakeskin$1.Node.prototype.textContent = function () {
	return this.value.textContent;
};

/**
 * DocumentFragment constructor
 *
 * @constructor
 * @extends {Snakeskin.Node}
 * @param {string} renderMode - rendering mode of templates
 */
Snakeskin$1.DocumentFragment = function (renderMode) {
	this.renderMode = renderMode;
	this.value = document.createDocumentFragment();
};

inherit(Snakeskin$1.DocumentFragment, Snakeskin$1.Node);

/**
 * Appends a child to the document fragment
 * @param {?} el - element for appending
 */
Snakeskin$1.DocumentFragment.prototype.appendChild = function (el) {
	this.value.appendChild(el);
};

/**
 * Returns text content
 * @return {string}
 */
Snakeskin$1.DocumentFragment.prototype.textContent = function () {
	var children = this.value.childNodes;

	var res = '';
	for (var i = 0; i < children.length; i++) {
		res += children[i].outerHTML || children[i].textContent;
	}

	return res;
};

/**
 * Element constructor
 *
 * @constructor
 * @extends {Snakeskin.Node}
 *
 * @param {string} name - element name
 * @param {string} renderMode - rendering mode of templates
 */
Snakeskin$1.Element = function (name, renderMode) {
	this.renderMode = renderMode;
	this.value = document.createElement(name);
};

inherit(Snakeskin$1.Element, Snakeskin$1.Node);

/**
 * Appends a child to the element
 * @param {?} el - element for appending
 */
Snakeskin$1.Element.prototype.appendChild = function (el) {
	this.value.appendChild(el);
};

/**
 * Sets an attribute to the element
 *
 * @param {string} name - attribute name
 * @param {string} val - attribute value
 */
Snakeskin$1.Element.prototype.setAttribute = function (name, val) {
	this.value.setAttribute(name, val);
};

/**
 * Returns text content
 * @return {string}
 */
Snakeskin$1.Element.prototype.textContent = function () {
	return this.value.outerHTML;
};

/**
 * Comment constructor
 *
 * @constructor
 * @extends {Snakeskin.Node}
 *
 * @param {string} text - comment text
 * @param {string} renderMode - rendering mode of templates
 */
Snakeskin$1.Comment = function (text, renderMode) {
	this.renderMode = renderMode;
	this.value = document.createComment(text);
};

inherit(Snakeskin$1.Comment, Snakeskin$1.Node);

/**
 * Text constructor
 *
 * @constructor
 * @extends {Snakeskin.Node}
 *
 * @param {string} text
 * @param {string} renderMode - rendering mode of templates
 */
Snakeskin$1.Text = function (text, renderMode) {
	this.renderMode = renderMode;
	this.value = document.createTextNode(text);
};

inherit(Snakeskin$1.Text, Snakeskin$1.Node);

/**
 * Map of inline tag names
 * @const
 */
Snakeskin$1.inlineTags = {
	'html': {
		'area': 'href',
		'base': 'href',
		'br': true,
		'col': true,
		'embed': 'src',
		'hr': true,
		'img': 'src',
		'input': 'value',
		'link': 'href',
		'meta': 'content',
		'param': 'value',
		'source': 'src',
		'track': 'src',
		'wbr': true
	},

	'xml': {}
};

/**
 * Appends a value to the specified element
 *
 * @param {(!Snakeskin.DocumentFragment|!Snakeskin.Element)} el - base element
 * @param {?} val - value for appending
 * @param {string} renderMode - rendering mode of templates
 * @return {(!Snakeskin.Element|!Snakeskin.Comment|!Snakeskin.Text)}
 */
Snakeskin$1.appendChild = function (el, val, renderMode) {
	if (val instanceof Snakeskin$1.Node === false) {
		val = new Snakeskin$1.Text(String(val), renderMode);
	}

	if (el) {
		el.appendChild(val.value);
	}

	return val;
};

/**
 * Sets an attribute to the specified element
 *
 * @param {!Snakeskin.Node} node - source element
 * @param {string} name - attribute name
 * @param {?} val - attribute value
 */
Snakeskin$1.setAttribute = function (node, name, val) {
	node.setAttribute(name, val instanceof Snakeskin$1.Node ? val.textContent() : String(val));
};

var keys = function () {
	return (/\[native code]/.test(Object.keys && Object.keys.toString()) && Object.keys
	);
}();

/**
 * Common iterator
 * (with hasOwnProperty for objects)
 *
 * @param {(Array|Object|undefined)} obj - source object
 * @param {(
 *   function(?, ?, !Array, {isFirst: boolean, isLast: boolean, length: number})|
 *   function(?, ?, !Object, {i: number, isFirst: boolean, isLast: boolean, length: number})
 * )} callback - callback function
 */
Snakeskin$1.forEach = function (obj, callback) {
	if (!obj) {
		return;
	}

	var length = 0;

	if (isArray(obj)) {
		length = obj.length;
		for (var i = 0; i < length; i++) {
			if (callback(obj[i], i, obj, { isFirst: i === 0, isLast: i === length - 1, length: length }) === false) {
				break;
			}
		}
	} else if (keys) {
		var arr = keys(obj);

		length = arr.length;
		for (var _i = 0; _i < length; _i++) {
			if (callback(obj[arr[_i]], arr[_i], obj, { i: _i, isFirst: _i === 0, isLast: _i === length - 1, length: length }) === false) {
				break;
			}
		}
	} else {
		if (callback.length >= 4) {
			for (var key in obj) {
				if (!obj.hasOwnProperty(key)) {
					break;
				}

				length++;
			}
		}

		var _i2 = 0;
		for (var _key in obj) {
			if (!obj.hasOwnProperty(_key)) {
				break;
			}

			if (callback(obj[_key], _key, obj, { i: _i2, isFirst: _i2 === 0, isLast: _i2 === length - 1, length: length }) === false) {
				break;
			}

			_i2++;
		}
	}
};

/**
 * Object iterator
 * (without hasOwnProperty)
 *
 * @param {(Object|undefined)} obj - source object
 * @param {function(?, string, !Object, {i: number, isFirst: boolean, isLast: boolean, length: number})} callback - callback function
 */
Snakeskin$1.forIn = function (obj, callback) {
	if (!obj) {
		return;
	}

	var length = 0,
	    i = 0;

	if (callback.length >= 4) {
		/* eslint-disable guard-for-in */
		for (var ignore in obj) {
			length++;
		}
		/* eslint-enable guard-for-in */
	}

	for (var key in obj) {
		if (callback(obj[key], key, obj, { i: i, isFirst: i === 0, isLast: i === length - 1, length: length }) === false) {
			break;
		}

		i++;
	}
};

/**
 * Decorates a function by another functions
 *
 * @param {!Array<!Function>} decorators - array of decorator functions
 * @param {!Function} fn - source function
 * @param {!Object} nms - source namespace
 * @return {!Function}
 */
Snakeskin$1.decorate = function (decorators, nms, fn) {
	Snakeskin$1.forEach(decorators, function (decorator) {
		return fn = decorator(fn, nms) || fn;
	});
	fn.decorators = decorators;
	return fn;
};

/**
 * Gets an object with an undefined type
 * (for the GCC)
 *
 * @param {?} val - source object
 * @return {?}
 */
function any(val) {
  return val;
}

/**
 * String tag (for ES6 string templates) for truncate starting whitespaces and eol-s
 *
 * @param {!Array<string>} strings
 * @param {...?} expr
 * @return {string}
 */


var rRgxp = /([\\/'*+?|()[\]{}.^$-])/g;

/**
 * Escapes RegExp characters in a string
 *
 * @param {string} str - source string
 * @return {string}
 */
function r(str) {
  return str.replace(rRgxp, '\\$1');
}

var isNotPrimitiveRgxp = /^\(*\s*(.*?)\s*\)*$/;
var isNotPrimitiveMap = { 'false': true, 'null': true, 'true': true, 'undefined': true };

/**
 * Returns true if the specified string can't be parse as a primitive value
 *
 * @param {string} str - source string
 * @param {Object<string, boolean>=} opt_map - map of primitives
 * @return {boolean}
 */
function isNotPrimitive(str, opt_map) {
  str = ((isNotPrimitiveRgxp.exec(str) || [])[1] || '').trim();
  return Boolean(str && isNaN(Number(str)) && !(opt_map || isNotPrimitiveMap)[str]);
}



var stringRender = {
  'stringBuffer': true,
  'stringConcat': true
};

var attrSeparators = {
  '-': true,
  ':': true,
  '_': true
};

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
  return typeof obj;
} : function (obj) {
  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};





var asyncGenerator = function () {
  function AwaitValue(value) {
    this.value = value;
  }

  function AsyncGenerator(gen) {
    var front, back;

    function send(key, arg) {
      return new Promise(function (resolve, reject) {
        var request = {
          key: key,
          arg: arg,
          resolve: resolve,
          reject: reject,
          next: null
        };

        if (back) {
          back = back.next = request;
        } else {
          front = back = request;
          resume(key, arg);
        }
      });
    }

    function resume(key, arg) {
      try {
        var result = gen[key](arg);
        var value = result.value;

        if (value instanceof AwaitValue) {
          Promise.resolve(value.value).then(function (arg) {
            resume("next", arg);
          }, function (arg) {
            resume("throw", arg);
          });
        } else {
          settle(result.done ? "return" : "normal", result.value);
        }
      } catch (err) {
        settle("throw", err);
      }
    }

    function settle(type, value) {
      switch (type) {
        case "return":
          front.resolve({
            value: value,
            done: true
          });
          break;

        case "throw":
          front.reject(value);
          break;

        default:
          front.resolve({
            value: value,
            done: false
          });
          break;
      }

      front = front.next;

      if (front) {
        resume(front.key, front.arg);
      } else {
        back = null;
      }
    }

    this._invoke = send;

    if (typeof gen.return !== "function") {
      this.return = undefined;
    }
  }

  if (typeof Symbol === "function" && Symbol.asyncIterator) {
    AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
      return this;
    };
  }

  AsyncGenerator.prototype.next = function (arg) {
    return this._invoke("next", arg);
  };

  AsyncGenerator.prototype.throw = function (arg) {
    return this._invoke("throw", arg);
  };

  AsyncGenerator.prototype.return = function (arg) {
    return this._invoke("return", arg);
  };

  return {
    wrap: function (fn) {
      return function () {
        return new AsyncGenerator(fn.apply(this, arguments));
      };
    },
    await: function (value) {
      return new AwaitValue(value);
    }
  };
}();













var defineProperty = function (obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }

  return obj;
};

var _COMMENTS;
var _BASE_SYS_ESCAPES;
var _SYS_ESCAPES;
var _STRONG_SYS_ESCAPES;

// The base directive separators
// >>>

var LEFT_BOUND = '{';
var ADV_LEFT_BOUND = '#';

// <<<
// The additional directive separators
// >>>

var I18N = '`';

var SINGLE_COMMENT = '///';
var MULT_COMMENT_START = '/*';
var MULT_COMMENT_END = '*/';

var COMMENTS = (_COMMENTS = {}, defineProperty(_COMMENTS, SINGLE_COMMENT, SINGLE_COMMENT), defineProperty(_COMMENTS, MULT_COMMENT_START, MULT_COMMENT_START), defineProperty(_COMMENTS, MULT_COMMENT_END, MULT_COMMENT_END), _COMMENTS);

var MICRO_TEMPLATE = '${';

var BASE_SHORTS = defineProperty({
	'-': true
}, ADV_LEFT_BOUND, true);

var SHORTS = {};

Snakeskin$1.forEach(BASE_SHORTS, function (el, key) {
	return SHORTS[key] = true;
});

// <<<
// The context modifiers
// >>>



// <<<
// Jade-Like
// >>>

var CONCAT = '&';
var CONCAT_END = '.';
var IGNORE = '|';
var INLINE = ' :: ';

// <<<
// The filter modifiers
// >>>



// <<<
// Escaping
// >>>

var BASE_SYS_ESCAPES = (_BASE_SYS_ESCAPES = {
	'\\': true,
	'"': true,
	'\'': true,
	'/': true
}, defineProperty(_BASE_SYS_ESCAPES, I18N, true), defineProperty(_BASE_SYS_ESCAPES, LEFT_BOUND, true), defineProperty(_BASE_SYS_ESCAPES, SINGLE_COMMENT.charAt(0), true), defineProperty(_BASE_SYS_ESCAPES, MULT_COMMENT_START.charAt(0), true), _BASE_SYS_ESCAPES);

var SYS_ESCAPES = (_SYS_ESCAPES = {
	'\\': true
}, defineProperty(_SYS_ESCAPES, I18N, true), defineProperty(_SYS_ESCAPES, LEFT_BOUND, true), defineProperty(_SYS_ESCAPES, ADV_LEFT_BOUND, true), defineProperty(_SYS_ESCAPES, SINGLE_COMMENT.charAt(0), true), defineProperty(_SYS_ESCAPES, MULT_COMMENT_START.charAt(0), true), defineProperty(_SYS_ESCAPES, CONCAT, true), defineProperty(_SYS_ESCAPES, CONCAT_END, true), defineProperty(_SYS_ESCAPES, IGNORE, true), defineProperty(_SYS_ESCAPES, INLINE.trim().charAt(0), true), _SYS_ESCAPES);

Snakeskin$1.forEach(BASE_SHORTS, function (el, key) {
	return SYS_ESCAPES[key.charAt(0)] = true;
});

var STRONG_SYS_ESCAPES = (_STRONG_SYS_ESCAPES = {
	'\\': true
}, defineProperty(_STRONG_SYS_ESCAPES, I18N, true), defineProperty(_STRONG_SYS_ESCAPES, SINGLE_COMMENT.charAt(0), true), defineProperty(_STRONG_SYS_ESCAPES, MULT_COMMENT_START.charAt(0), true), _STRONG_SYS_ESCAPES);

var MICRO_TEMPLATE_ESCAPES = defineProperty({
	'\\': true
}, MICRO_TEMPLATE.charAt(0), true);















// <<<
// The reserved names
// >>>



var tmpSep = [];

Snakeskin$1.forEach(attrSeparators, function (el, key) {
	tmpSep.push(r(key));
});





var attrKey = /([^\s=]+)/;

var Filters = Snakeskin$1.Filters;

/**
 * Imports an object to Filters
 *
 * @param {!Object} filters - import object
 * @param {?string=} [opt_namespace] - namespace for saving, for example foo.bar
 * @return {!Object}
 */

Snakeskin$1.importFilters = function (filters, opt_namespace) {
	var obj = Filters;

	if (opt_namespace) {
		Snakeskin$1.forEach(opt_namespace.split('.'), function (el) {
			obj[el] = obj[el] || {};
			obj = obj[el];
		});
	}

	Snakeskin$1.forEach(filters, function (el, key) {
		return obj[key] = el;
	});

	return this;
};

/**
 * Sets parameters to the specified Snakeskin filter
 *
 * @param {(string|!Function)} filter - filter name or the filter function
 * @param {Object} params - parameters
 * @return {!Function}
 */
Snakeskin$1.setFilterParams = function (filter, params) {
	var safe = params['safe'];

	if (safe) {
		params['bind'] = ['Unsafe'].concat(params['bind'] || []);
	}

	var tmp = void 0;
	function wrapper(val, Unsafe) {
		var _tmp2;

		for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
			args[_key - 2] = arguments[_key];
		}

		if (val && isFunction(Unsafe) && val instanceof Unsafe) {
			var _tmp;

			val.value = (_tmp = tmp).call.apply(_tmp, [this, val.value].concat(args));
			return val;
		}

		return (_tmp2 = tmp).call.apply(_tmp2, [this, val].concat(args));
	}

	if (isString(filter)) {
		if (safe) {
			tmp = Filters[filter];
			Filters[filter] = wrapper;
		}

		Filters[filter] = Filters[filter] || function (str) {
			return str;
		};
		Filters[filter]['ssFilterParams'] = params;
		return Filters[filter];
	}

	if (safe) {
		tmp = filter;
		filter = wrapper;
	}

	filter['ssFilterParams'] = params;
	return any(filter);
};

/**
 * Console API
 * @const
 */
Filters['console'] = {
	/**
  * @param {?} val
  * @return {?}
  */
	'dir': function dir(val) {
		var _console;

		(_console = console).dir.apply(_console, arguments);
		return val;
	},


	/**
  * @param {?} val
  * @return {?}
  */
	'error': function error(val) {
		var _console2;

		(_console2 = console).error.apply(_console2, arguments);
		return val;
	},


	/**
  * @param {?} val
  * @return {?}
  */
	'info': function info(val) {
		var _console3;

		(_console3 = console).info.apply(_console3, arguments);
		return val;
	},


	/**
  * @param {?} val
  * @return {?}
  */
	'log': function log(val) {
		var _console4;

		(_console4 = console).log.apply(_console4, arguments);
		return val;
	},


	/**
  * @param {?} val
  * @return {?}
  */
	'table': function table(val) {
		var _console5;

		(_console5 = console).table.apply(_console5, arguments);
		return val;
	},


	/**
  * @param {?} val
  * @return {?}
  */
	'warn': function warn(val) {
		var _console6;

		(_console6 = console).warn.apply(_console6, arguments);
		return val;
	}
};

var entityMap = {
	'"': '&quot;',
	'&': '&amp;',
	'\'': '&#39;',
	'<': '&lt;',
	'>': '&gt;'
};

var escapeHTMLRgxp = /[<>"'/]|&(?!#|[a-z]+;)/g;
var escapeHTML = function escapeHTML(s) {
	return entityMap[s] || s;
};

var uentityMap = {
	'&#39;': '\'',
	'&#x2F;': '/',
	'&amp;': '&',
	'&gt;': '>',
	'&lt;': '<',
	'&quot;': '"'
};

var uescapeHTMLRgxp = /&amp;|&lt;|&gt;|&quot;|&#39;|&#x2F;/g;
var uescapeHTML = function uescapeHTML(s) {
	return uentityMap[s];
};

/**
 * Escapes HTML entities from a string
 *
 * @param {?} val - source value
 * @param {?=} [opt_Unsafe] - unsafe class constructor
 * @param {?string=} [opt_attr] - type of attribute declaration
 * @param {Object=} [opt_attrCache] - attribute cache object
 * @param {?=} [opt_true] - true value
 * @return {(string|!Snakeskin.HTMLObject|!Snakeskin.Node)}
 */
Filters['html'] = function (val, opt_Unsafe, opt_attr, opt_attrCache, opt_true) {
	if (!val || val instanceof Snakeskin$1.Node) {
		return val;
	}

	if (val instanceof Snakeskin$1.HTMLObject) {
		Snakeskin$1.forEach(val.value, function (el, key, data) {
			if (val.attr) {
				opt_attrCache[key] = data[key] = el[0] !== opt_true ? [Filters['html'](el[0], opt_Unsafe, val.attr, opt_attrCache, opt_true)] : el;
			} else {
				data[key] = Filters['html'](el, opt_Unsafe);
			}
		});

		return val;
	}

	if (isFunction(opt_Unsafe) && val instanceof opt_Unsafe) {
		return val.value;
	}

	return String(opt_attr ? Filters[opt_attr](val) : val).replace(escapeHTMLRgxp, escapeHTML);
};

Snakeskin$1.setFilterParams('html', {
	bind: ['Unsafe', '$attrType', function (o) {
		return o.getVar('$attrs');
	}, 'TRUE'],
	test: function test(val) {
		return isNotPrimitive(val);
	}
});

Filters['htmlObject'] = function (val) {
	if (val instanceof Snakeskin$1.HTMLObject) {
		return '';
	}

	return val;
};

Snakeskin$1.setFilterParams('htmlObject', {
	test: function test(val) {
		return isNotPrimitive(val);
	}
});

/**
 * Replaces undefined to ''
 *
 * @param {?} val - source value
 * @return {?}
 */
Filters['undef'] = function (val) {
	return val !== undefined ? val : '';
};

Snakeskin$1.setFilterParams('undef', {
	test: function test(val) {
		return isNotPrimitive(val, { 'false': true, 'null': true, 'true': true });
	}
});

/**
 * Replaces escaped HTML entities to real content
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['uhtml'] = function (val) {
	return String(val).replace(uescapeHTMLRgxp, uescapeHTML);
};

var stripTagsRgxp = /<\/?[^>]+>/g;

/**
 * Removes < > from a string
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['stripTags'] = function (val) {
	return String(val).replace(stripTagsRgxp, '');
};

var uriO = /%5B/g;
var uriC = /%5D/g;

/**
 * Encodes URL
 *
 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURI
 * @param {?} val - source value
 * @return {string}
 */
Filters['uri'] = function (val) {
	return encodeURI(String(val)).replace(uriO, '[').replace(uriC, ']');
};

Snakeskin$1.setFilterParams('uri', {
	safe: true
});

/**
 * Converts a string to uppercase
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['upper'] = function (val) {
	return String(val).toUpperCase();
};

Snakeskin$1.setFilterParams('upper', {
	safe: true
});

/**
 * Converts the first letter of a string to uppercase
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['ucfirst'] = function (val) {
	val = String(val);
	return val.charAt(0).toUpperCase() + val.slice(1);
};

Snakeskin$1.setFilterParams('ucfirst', {
	safe: true
});

/**
 * Converts a string to lowercase
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['lower'] = function (val) {
	return String(val).toLowerCase();
};

Snakeskin$1.setFilterParams('lower', {
	safe: true
});

/**
 * Converts the first letter of a string to lowercase
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['lcfirst'] = function (val) {
	val = String(val);
	return val.charAt(0).toLowerCase() + val.slice(1);
};

Snakeskin$1.setFilterParams('lcfirst', {
	safe: true
});

/**
 * Removes whitespace from both ends of a string
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['trim'] = function (val) {
	return String(val).trim();
};

Snakeskin$1.setFilterParams('trim', {
	safe: true
});

var spaceCollapseRgxp = /\s{2,}/g;

/**
 * Removes whitespace from both ends of a string
 * and collapses whitespace
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['collapse'] = function (val) {
	return String(val).replace(spaceCollapseRgxp, ' ').trim();
};

Snakeskin$1.setFilterParams('collapse', {
	safe: true
});

/**
 * Truncates a string to the specified length
 * (at the end puts three dots)
 *
 * @param {?} val - source value
 * @param {number} length - maximum length
 * @param {?boolean=} opt_wordOnly - if is false, then the string will be truncated without
 *   taking into account the integrity of the words
 *
 * @param {?boolean=} opt_html - if is true, then the dots will be inserted as HTML-mnemonic
 * @return {string}
 */
Filters['truncate'] = function (val, length, opt_wordOnly, opt_html) {
	val = String(val);
	if (!val || val.length <= length) {
		return val;
	}

	var tmp = val.slice(0, length - 1);

	var i = tmp.length,
	    lastInd = void 0;

	while (i-- && opt_wordOnly) {
		if (tmp.charAt(i) === ' ') {
			lastInd = i;
		} else if (lastInd !== undefined) {
			break;
		}
	}

	return (lastInd !== undefined ? tmp.slice(0, lastInd) : tmp) + (opt_html ? '&#8230;' : '…');
};

/**
 * Returns a new string of repetitions of a string
 *
 * @param {?} val - source value
 * @param {?number=} opt_num - number of repetitions
 * @return {string}
 */
Filters['repeat'] = function (val, opt_num) {
	return new Array(opt_num != null ? opt_num + 1 : 3).join(val);
};

Snakeskin$1.setFilterParams('repeat', {
	safe: true
});

/**
 * Removes a slice from a string
 *
 * @param {?} val - source value
 * @param {(string|RegExp)} search - searching slice
 * @return {string}
 */
Filters['remove'] = function (val, search) {
	return String(val).replace(search, '');
};

/**
 * Replaces a slice from a string to a new string
 *
 * @param {?} val - source value
 * @param {(string|!RegExp)} search - searching slice
 * @param {string} replace - string for replacing
 * @return {string}
 */
Filters['replace'] = function (val, search, replace) {
	return String(val).replace(search, replace);
};

var tplRgxp = /\${(.*?)}/g;

/**
 * Returns a string result of the specified template
 *
 * @example
 * 'hello ${name}' |tpl {name: 'Kobezzza'}
 *
 * @param {?} tpl - source template
 * @param {!Object} map - map of values
 * @return {string}
 */
Filters['tpl'] = function (tpl, map) {
	return String(tpl).replace(tplRgxp, function (str, $0) {
		return $0 in map ? map[$0] : '';
	});
};

/**
 * Converts a value to JSON
 *
 * @param {(Object|Array|string|number|boolean)} val - source value
 * @return {string}
 */
Filters['json'] = function (val) {
	return JSON.stringify(val);
};

/**
 * Converts a value to a string
 *
 * @param {(Object|Array|string|number|boolean)} val - source value
 * @return {string}
 */
Filters['string'] = function (val) {
	if ((typeof val === 'undefined' ? 'undefined' : _typeof(val)) === 'object' && val instanceof String === false) {
		return JSON.stringify(val);
	}

	return String(val);
};

/**
 * Parses a string as JSON
 *
 * @param {?} val - source value
 * @return {?}
 */
Filters['parse'] = function (val) {
	if (!isString(val)) {
		return val;
	}

	return JSON.parse(val);
};

/**
 * Sets a default value for the specified parameter
 *
 * @param {?} val - source value
 * @param {?} def - value
 * @return {?}
 */
Filters['default'] = function (val, def) {
	return val === undefined ? def : val;
};

Snakeskin$1.setFilterParams('default', {
	'!undef': true
});

var nl2brRgxp = /\r?\n|\n/g;

/**
 * Replaces EOL symbols from a string to <br>
 *
 * @param {?} val - source value
 * @param {(!Snakeskin.DocumentFragment|!Snakeskin.Element|undefined)} node - source node
 * @param {string} renderMode - rendering mode of templates
 * @param {boolean} stringResult - if is true, then the output will be saved to __STRING_RESULT__ as a string
 * @param {string} doctype - document type
 * @return {?}
 */
Filters['nl2br'] = function (val, node, renderMode, stringResult, doctype) {
	var arr = val.split(nl2brRgxp);

	var res = '';
	for (var i = 0; i < arr.length; i++) {
		var el = arr[i],
		    last = i === arr.length - 1;

		if (!stringResult && !stringRender[renderMode]) {
			Snakeskin$1.appendChild(any(node), el, renderMode);
			if (!last) {
				Snakeskin$1.appendChild(any(node), new Snakeskin$1.Element('br', renderMode), renderMode);
			}
		} else {
			res += Filters['html'](el);
			if (!last) {
				res += '<br' + (doctype === 'xml' ? '/' : '') + '>';
			}
		}
	}

	return res;
};

Snakeskin$1.setFilterParams('nl2br', {
	'!html': true,
	bind: ['$0', function (o) {
		return '\'' + o.renderMode + '\'';
	}, function (o) {
		return o.stringResult;
	}, '$0', function (o) {
		return '\'' + o.doctype + '\'';
	}]
});

/**
 * @param str
 * @return {string}
 */
function dasherize(str) {
	var res = str[0].toLowerCase();

	for (var i = 1; i < str.length; i++) {
		var el = str.charAt(i),
		    up = el.toUpperCase();

		if (up === el && up !== el.toLowerCase()) {
			res += '-' + el;
		} else {
			res += el;
		}
	}

	return res.toLowerCase();
}

/**
 * Escapes HTML entities from an attribute name
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['attrKey'] = function (val) {
	var tmp = attrKey.exec(String(val));
	return tmp && tmp[1] || 'undefined';
};

/**
 * Escapes HTML entities from an attribute group
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['attrKeyGroup'] = function (val) {
	var tmp = attrKey.exec(String(val));
	return tmp && tmp[1] || '';
};

var attrValRgxp = /(javascript)(:|;)/g;

/**
 * Escapes HTML entities from an attribute value
 *
 * @param {?} val - source value
 * @return {string}
 */
Filters['attrValue'] = function (val) {
	return String(val).replace(attrValRgxp, '$1&#31;$2');
};

/**
 * Sets attributes to a node
 *
 * @param {?} val - source value
 * @param {?} Unsafe - unsafe class constructor
 * @param {string} doctype - document type
 * @param {string} type - type of attribute declaration
 * @param {!Object} cache - attribute cache object
 * @param {!Boolean} TRUE - true value
 * @param {!Boolean} FALSE - false value
 * @return {(string|Unsafe|Snakeskin.HTMLObject)}
 */
Filters['attr'] = function (val, Unsafe, doctype, type, cache, TRUE, FALSE) {
	if (type !== 'attrKey' || !isObject(val)) {
		if (isFunction(Unsafe) && val instanceof Unsafe) {
			return val;
		}

		return String(val);
	}

	var localCache = {};

	/**
  * @param {Object} obj
  * @param {?string=} opt_prfx
  * @return {Snakeskin.HTMLObject}
  */
	function convert(obj, opt_prfx) {
		opt_prfx = opt_prfx || '';
		Snakeskin$1.forEach(obj, function (el, key) {
			if (el === FALSE) {
				return;
			}

			if (isObject(el)) {
				var group = Filters['attrKeyGroup'](key);
				return convert(el, opt_prfx + (!group.length || attrSeparators[group.slice(-1)] ? group : group + '-'));
			}

			var tmp = dasherize(opt_prfx + key);
			cache[tmp] = localCache[tmp] = [el];
		});

		return new Snakeskin$1.HTMLObject(localCache, 'attrValue');
	}

	return convert(val);
};

Snakeskin$1.setFilterParams('attr', {
	'!html': true,
	bind: ['Unsafe', function (o) {
		return '\'' + o.doctype + '\'';
	}, '$attrType', function (o) {
		return o.getVar('$attrs');
	}, 'TRUE', 'FALSE'],
	test: function test(val) {
		return isNotPrimitive(val);
	}
});

/**
 * Returns a valid template name for overriding
 *
 * @param {string} name - base name
 * @param {!Object} store - link to a store object
 * @param {string} scope - scope string
 * @param {boolean} init - true, if a template already defined
 * @return {string}
 */
Filters['super'] = function (name, store, scope, init) {
	var cache = store.templates = store.templates || {},
	    nm = scope + name;

	cache[nm] = cache[nm] || [name];

	if (init) {
		return cache[nm].slice(-2)[0] || name;
	}

	name = name + Math.random().toString().slice(2);
	cache[nm].push(name);

	return name;
};

Snakeskin$1.setFilterParams('super', {
	'!html': true,
	bind: [function (o) {
		return o.getVar('__STORE__');
	}, function (o) {
		return JSON.stringify(o.scope[0]);
	}, function (o) {
		return Boolean(o.vars[o.tplName]);
	}]
});

return Snakeskin$1;

})));
