Skip to content

Instantly share code, notes, and snippets.

@tadatuta
Created November 10, 2015 15:59
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 tadatuta/bbf67d79e94693ebdcda to your computer and use it in GitHub Desktop.
Save tadatuta/bbf67d79e94693ebdcda to your computer and use it in GitHub Desktop.
bem-xjst@4 basic templates
/// -------------------------------------
/// --------- BEM-XJST Runtime Start ----
/// -------------------------------------
var BEMHTML = function(module, exports) {
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.bemhtml = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = require('../../bemxjst/runtime');
},{"../../bemxjst/runtime":5}],2:[function(require,module,exports){
function ClassBuilder(options) {
this.modDelim = options.mod || '_';
this.elemDelim = options.elem || '__';
}
exports.ClassBuilder = ClassBuilder;
ClassBuilder.prototype.build = function build(block, elem) {
if (elem === undefined)
return block;
else
return block + this.elemDelim + elem;
};
ClassBuilder.prototype.buildModPostfix = function buildModPostfix(modName,
modVal) {
var res = this.modDelim + modName;
if (modVal !== true) res += this.modDelim + modVal;
return res;
};
ClassBuilder.prototype.buildBlockClass = function buildBlockClass(name,
modName,
modVal) {
var res = name;
if (modVal) res += this.buildModPostfix(modName, modVal);
return res;
};
ClassBuilder.prototype.buildElemClass = function buildElemClass(block,
name,
modName,
modVal) {
var res = this.buildBlockClass(block) + this.elemDelim + name;
if (modVal) res += this.buildModPostfix(modName, modVal);
return res;
};
ClassBuilder.prototype.split = function split(key) {
return key.split(this.elemDelim, 2);
};
},{}],3:[function(require,module,exports){
var utils = require('./utils');
function Context(bemxjst) {
this._bemxjst = bemxjst;
this.ctx = null;
this.block = '';
// Save current block until the next BEM entity
this._currBlock = '';
this.elem = null;
this.mods = {};
this.elemMods = {};
this.position = 0;
this._listLength = 0;
this._notNewList = false;
this._uniq = null;
// Used in `OnceMatch` check to detect context change
this._onceRef = {};
}
exports.Context = Context;
Context.prototype._flush = null;
Context.prototype.isArray = utils.isArray;
Context.prototype.isSimple = utils.isSimple;
Context.prototype.isShortTag = utils.isShortTag;
Context.prototype.extend = utils.extend;
Context.prototype.identify = utils.identify;
Context.prototype.xmlEscape = utils.xmlEscape;
Context.prototype.attrEscape = utils.attrEscape;
Context.prototype.jsAttrEscape = utils.jsAttrEscape;
Context.prototype.BEM = {};
Context.prototype.isFirst = function isFirst() {
return this.position === 1;
};
Context.prototype.isLast = function isLast() {
return this.position === this._listLength;
};
Context.prototype.generateId = function generateId() {
if (this._uniq === null)
this._uniq = utils.getUniq();
return this._uniq;
};
Context.prototype.reapply = function reapply(ctx) {
return this._bemxjst.run(ctx);
};
},{"./utils":8}],4:[function(require,module,exports){
var utils = require('./utils');
var Template = require('./tree').Template;
var PropertyMatch = require('./tree').PropertyMatch;
var CompilerOptions = require('./tree').CompilerOptions;
var Match = require('./match').Match;
function Entity(bemxjst, block, elem, templates) {
this.bemxjst = bemxjst;
this.block = null;
this.elem = null;
this.jsClass = null;
// `true` if entity has just a default renderer for `def()` mode
this.canFlush = true;
// Compiler options via `xjstOptions()`
this.options = {};
// "Fast modes"
this.def = new Match(this);
this.tag = new Match(this);
this.attrs = new Match(this);
this.mod = new Match(this);
this.js = new Match(this);
this.mix = new Match(this);
this.bem = new Match(this);
this.cls = new Match(this);
this.content = new Match(this);
// "Slow modes"
this.rest = {};
// Initialize
this.init(block, elem);
this.initModes(templates);
}
exports.Entity = Entity;
Entity.prototype.init = function init(block, elem) {
this.block = block;
this.elem = elem;
// Class for jsParams
this.jsClass = this.bemxjst.classBuilder.build(this.block, this.elem);
};
function contentMode() {
return this.ctx.content;
}
Entity.prototype.initModes = function initModes(templates) {
/* jshint maxdepth : false */
for (var i = 0; i < templates.length; i++) {
var template = templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
if (!(pred instanceof PropertyMatch))
continue;
if (pred.key !== '_mode')
continue;
template.predicates.splice(j, 1);
this._initRest(pred.value);
// All templates should go there anyway
this.rest[pred.value].push(template);
break;
}
if (j === -1)
this.def.push(template);
// Merge compiler options
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
if (!(pred instanceof CompilerOptions))
continue;
this.options = utils.extend(this.options, pred.options);
}
}
};
Entity.prototype._initRest = function _initRest(key) {
if (key === 'tag' ||
key === 'attrs' ||
key === 'js' ||
key === 'mix' ||
key === 'bem' ||
key === 'cls' ||
key === 'content' ||
key === 'default') {
if (key === 'default')
this.rest[key] = this.def;
else
this.rest[key] = this[key];
} else {
if (!this.rest.hasOwnProperty(key))
this.rest[key] = new Match(this);
}
};
Entity.prototype.setDefaults = function setDefaults() {
// Default .content() template for applyNext()
if (this.content.count !== 0)
this.content.push(new Template([], contentMode));
// .def() default
if (this.def.count !== 0) {
// `.xjstOptions({ flush: true })` will override this
this.canFlush = this.options.flush || false;
var self = this;
this.def.push(new Template([], function defaultBodyProxy() {
return self.defaultBody(this);
}));
}
};
Entity.prototype.prepend = function prepend(other) {
// Prepend to the slow modes, fast modes are in this hashmap too anyway
var keys = Object.keys(this.rest);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!other.rest[key])
continue;
this.rest[key].prepend(other.rest[key]);
}
// Add new slow modes
keys = Object.keys(other.rest);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (this.rest[key])
continue;
this._initRest(key);
this.rest[key].prepend(other.rest[key]);
}
};
// NOTE: This could be potentially compiled into inlined invokations
Entity.prototype.run = function run(context) {
if (this.def.count !== 0)
return this.def.exec(context);
return this.defaultBody(context);
};
Entity.prototype.defaultBody = function defaultBody(context) {
var tag = context.ctx.tag;
if (tag === undefined)
tag = this.tag.exec(context);
var js;
if (context.ctx.js !== false)
js = this.js.exec(context);
var bem = this.bem.exec(context);
var cls = this.cls.exec(context);
var mix = this.mix.exec(context);
var attrs = this.attrs.exec(context);
var content = this.content.exec(context);
// Default content
if (this.content.count === 0 && content === undefined)
content = context.ctx.content;
return this.bemxjst.render(context,
this,
tag,
js,
bem,
cls,
mix,
attrs,
content);
};
},{"./match":6,"./tree":7,"./utils":8}],5:[function(require,module,exports){
var inherits = require('inherits');
var Tree = require('./tree').Tree;
var PropertyMatch = require('./tree').PropertyMatch;
var Entity = require('./entity').Entity;
var Context = require('./context').Context;
var ClassBuilder = require('./class-builder').ClassBuilder;
var utils = require('./utils');
function BEMXJST(options) {
this.options = options || {};
this.entities = null;
this.defaultEnt = null;
// Current tree
this.tree = null;
// Current match
this.match = null;
// Create new Context constructor for overriding prototype
this.contextConstructor = function ContextChild(bemxjst) {
Context.call(this, bemxjst);
};
inherits(this.contextConstructor, Context);
this.context = null;
this.classBuilder = new ClassBuilder(this.options.naming || {});
// Execution depth, used to invalidate `applyNext` bitfields
this.depth = 0;
// Do not call `_flush` on overridden `def()` mode
this.canFlush = false;
// oninit templates
this.oninit = null;
// Initialize default entity (no block/elem match)
this.defaultEnt = new Entity(this, '', '', []);
this.defaultElemEnt = new Entity(this, '', '', []);
}
module.exports = BEMXJST;
BEMXJST.locals = Tree.methods.concat('local', 'applyCtx', 'applyNext', 'apply');
BEMXJST.prototype.compile = function compile(code) {
var self = this;
function applyCtx() {
return self._run(self.context.ctx);
}
function applyCtxWrap(ctx, changes) {
// Fast case
if (!changes)
return self.local({ ctx: ctx }, applyCtx);
return self.local(changes, function() {
return self.local({ ctx: ctx }, applyCtx);
});
}
function apply(mode, changes) {
return self.applyMode(mode, changes);
}
function localWrap(changes) {
return function localBody(body) {
return self.local(changes, body);
};
}
var tree = new Tree({
refs: {
applyCtx: applyCtxWrap,
local: localWrap
}
});
// Yeah, let people pass functions to us!
var templates = this.recompileInput(code);
var out = tree.build(templates, [
localWrap,
applyCtxWrap,
function applyNextWrap(changes) {
if (changes)
return self.local(changes, applyNextWrap);
return self.applyNext();
},
apply
]);
// Concatenate templates with existing ones
// TODO(indutny): it should be possible to incrementally add templates
if (this.tree) {
out = {
templates: out.templates.concat(this.tree.templates),
oninit: this.tree.oninit.concat(out.oninit)
};
}
this.tree = out;
// Group block+elem entities into a hashmap
var ent = this.groupEntities(out.templates);
// Transform entities from arrays to Entity instances
ent = this.transformEntities(ent);
this.entities = ent;
this.oninit = out.oninit;
};
BEMXJST.prototype.recompileInput = function recompileInput(code) {
var out = code.toString();
var args = BEMXJST.locals;
if (typeof code === 'function') {
var start = out.match(/^function\s*\(([^{]+)\)\s*{/);
// Reuse function if it already has right arguments
if (start !== null && start[1].split(/,\s*/).length === args.length)
return code;
}
// Strip the function
out = out.replace(/^function[^{]+{|}$/g, '');
// And recompile it with right arguments
out = new Function(args.join(', '), out);
return out;
};
BEMXJST.prototype.groupEntities = function groupEntities(tree) {
var res = {};
for (var i = 0; i < tree.length; i++) {
// Make sure to change only the copy, the original is cached in `this.tree`
var template = tree[i].clone();
var block = null;
var elem;
elem = undefined;
for (var j = 0; j < template.predicates.length; j++) {
var pred = template.predicates[j];
if (!(pred instanceof PropertyMatch))
continue;
if (pred.key === 'block')
block = pred.value;
else if (pred.key === 'elem')
elem = pred.value;
else
continue;
// Remove predicate, we won't much against it
template.predicates.splice(j, 1);
j--;
}
// TODO(indutny): print out the template itself
if (block === null)
throw new Error('block("...") not found in one of the templates');
var key = this.classBuilder.build(block, elem);
if (!res[key])
res[key] = [];
res[key].push(template);
}
return res;
};
BEMXJST.prototype.transformEntities = function transformEntities(entities) {
var wildcardElems = [];
var keys = Object.keys(entities);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// TODO(indutny): pass this values over
var parts = this.classBuilder.split(key);
var block = parts[0];
var elem = parts[1];
if (elem === '*')
wildcardElems.push(block);
entities[key] = new Entity(this, block, elem, entities[key]);
}
// Merge wildcard block templates
if (entities.hasOwnProperty('*')) {
var wildcard = entities['*'];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === '*')
continue;
entities[key].prepend(wildcard);
}
this.defaultEnt.prepend(wildcard);
this.defaultElemEnt.prepend(wildcard);
}
// Merge wildcard elem templates
for (var i = 0; i < wildcardElems.length; i++) {
var block = wildcardElems[i];
var wildcardKey = this.classBuilder.build(block, '*');
var wildcard = entities[wildcardKey];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === wildcardKey)
continue;
var entity = entities[key];
if (entity.block !== block)
continue;
if (entity.elem === undefined)
continue;
entities[key].prepend(wildcard);
}
this.defaultElemEnt.prepend(wildcard);
}
// Set default templates after merging with wildcard
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
entities[key].setDefaults();
this.defaultEnt.setDefaults();
this.defaultElemEnt.setDefaults();
}
return entities;
};
BEMXJST.prototype._run = function _run(context) {
var res;
if (context === undefined || context === '' || context === null)
res = this.runEmpty();
else if (utils.isArray(context))
res = this.runMany(context);
else if (utils.isSimple(context))
res = this.runSimple(context);
else
res = this.runOne(context);
return res;
};
BEMXJST.prototype.run = function run(json) {
var match = this.match;
var context = this.context;
this.match = null;
this.context = new this.contextConstructor(this);
this.canFlush = this.context._flush !== null;
this.depth = 0;
var res = this._run(json);
if (this.canFlush)
res = this.context._flush(res);
this.match = match;
this.context = context;
return res;
};
BEMXJST.prototype.runEmpty = function runEmpty() {
this.context._listLength--;
return '';
};
BEMXJST.prototype.runMany = function runMany(arr) {
var out = '';
var context = this.context;
var prevPos = context.position;
var prevNotNewList = context._notNewList;
if (prevNotNewList) {
context._listLength += arr.length - 1;
} else {
context.position = 0;
context._listLength = arr.length;
}
context._notNewList = true;
if (this.canFlush) {
for (var i = 0; i < arr.length; i++)
out += context._flush(this._run(arr[i]));
} else {
for (var i = 0; i < arr.length; i++)
out += this._run(arr[i]);
}
if (!prevNotNewList)
context.position = prevPos;
return out;
};
BEMXJST.prototype.runSimple = function runSimple(context) {
this.context._listLength--;
var res = '';
if (context && context !== true || context === 0)
res += context;
return res;
};
BEMXJST.prototype.runOne = function runOne(json) {
var context = this.context;
var oldCtx = context.ctx;
var oldBlock = context.block;
var oldCurrBlock = context._currBlock;
var oldElem = context.elem;
var oldMods = context.mods;
var oldElemMods = context.elemMods;
if (json.block || json.elem)
context._currBlock = '';
else
context._currBlock = context.block;
context.ctx = json;
if (json.block) {
context.block = json.block;
if (json.mods)
context.mods = json.mods;
else
context.mods = {};
} else {
if (!json.elem)
context.block = '';
else if (oldCurrBlock)
context.block = oldCurrBlock;
}
context.elem = json.elem;
if (json.elemMods)
context.elemMods = json.elemMods;
else
context.elemMods = {};
var block = context.block || '';
var elem = context.elem;
// Control list position
if (block || elem)
context.position++;
else
context._listLength--;
// To invalidate `applyNext` flags
this.depth++;
var key = this.classBuilder.build(block, elem);
var restoreFlush = false;
var ent = this.entities[key];
if (ent) {
if (this.canFlush && !ent.canFlush) {
// Entity does not support flushing, do not flush anything nested
restoreFlush = true;
this.canFlush = false;
}
} else {
// No entity - use default one
ent = this.defaultEnt;
if (elem !== undefined)
ent = this.defaultElemEnt;
ent.init(block, elem);
}
var res = ent.run(context);
context.ctx = oldCtx;
context.block = oldBlock;
context.elem = oldElem;
context.mods = oldMods;
context.elemMods = oldElemMods;
context._currBlock = oldCurrBlock;
this.depth--;
if (restoreFlush)
this.canFlush = true;
return res;
};
BEMXJST.prototype.render = function render(context,
entity,
tag,
js,
bem,
cls,
mix,
attrs,
content) {
var ctx = context.ctx;
if (tag === undefined)
tag = 'div';
if (!tag)
return this.renderNoTag(context, js, bem, cls, mix, attrs, content);
var out = '<' + tag;
var ctxJS = ctx.js;
if (ctxJS !== false) {
if (js === true)
js = {};
if (js) {
if (ctxJS !== true)
js = utils.extend(ctxJS, js);
} else if (ctxJS === true) {
js = {};
} else {
js = ctxJS;
}
}
var jsParams;
if (js) {
jsParams = {};
jsParams[entity.jsClass] = js;
}
var isBEM = bem;
if (isBEM === undefined) {
if (ctx.bem === undefined)
isBEM = entity.block || entity.elem;
else
isBEM = ctx.bem;
}
isBEM = !!isBEM;
if (cls === undefined)
cls = ctx.cls;
var addJSInitClass = entity.block && jsParams && !entity.elem;
if (!isBEM && !cls) {
return this.renderClose(out, context, tag, attrs, isBEM, ctx, content);
}
out += ' class="';
if (isBEM) {
var mods = ctx.elemMods || ctx.mods;
if (!mods && ctx.block)
mods = context.mods;
out += entity.jsClass;
out += this.buildModsClasses(entity.block, entity.elem, mods);
var totalMix = mix;
if (ctx.mix) {
if (totalMix)
totalMix = [].concat(totalMix, ctx.mix);
else
totalMix = ctx.mix;
}
if (totalMix) {
var m = this.renderMix(entity, totalMix, jsParams, addJSInitClass);
out += m.out;
jsParams = m.jsParams;
addJSInitClass = m.addJSInitClass;
}
if (cls)
out += ' ' + cls;
} else {
if (cls)
out += cls;
}
if (addJSInitClass)
out += ' i-bem"';
else
out += '"';
if (isBEM && jsParams)
out += ' data-bem=\'' + utils.jsAttrEscape(JSON.stringify(jsParams)) + '\'';
return this.renderClose(out, context, tag, attrs, isBEM, ctx, content);
};
BEMXJST.prototype.renderClose = function renderClose(prefix,
context,
tag,
attrs,
isBEM,
ctx,
content) {
var out = prefix;
// NOTE: maybe we need to make an array for quicker serialization
attrs = utils.extend(attrs, ctx.attrs);
if (attrs) {
var name; // TODO: do something with OmetaJS and YUI Compressor
/* jshint forin : false */
for (name in attrs) {
var attr = attrs[name];
if (attr === undefined)
continue;
// TODO(indutny): support `this.reapply()`
out += ' ' + name + '="' +
utils.attrEscape(utils.isSimple(attr) ?
attr :
this.reapply(attr)) +
'"';
}
}
if (utils.isShortTag(tag)) {
out += '/>';
if (this.canFlush)
out = context._flush(out);
} else {
out += '>';
if (this.canFlush)
out = context._flush(out);
// TODO(indutny): skip apply next flags
if (content || content === 0)
out += this.renderContent(content, isBEM);
out += '</' + tag + '>';
}
if (this.canFlush)
out = context._flush(out);
return out;
};
BEMXJST.prototype.renderMix = function renderMix(entity,
mix,
jsParams,
addJSInitClass) {
var visited = {};
var context = this.context;
var js = jsParams;
var addInit = addJSInitClass;
visited[entity.jsClass] = true;
// Transform mix to the single-item array if it's not array
if (!utils.isArray(mix))
mix = [ mix ];
var classBuilder = this.classBuilder;
var out = '';
for (var i = 0; i < mix.length; i++) {
var item = mix[i];
if (item === undefined)
continue;
if (typeof item === 'string')
item = { block: item, elem: undefined };
var hasItem = item.block || item.elem;
var block = item.block || item._block || context.block;
var elem = item.elem || item._elem || context.elem;
var key = classBuilder.build(block, elem);
var classElem = item.elem ||
item._elem ||
(item.block ? undefined : context.elem);
if (hasItem)
out += ' ' + classBuilder.build(block, classElem);
out += this.buildModsClasses(block, classElem, item.elemMods || item.mods);
if (item.js) {
if (!js)
js = {};
js[classBuilder.build(block, item.elem)] =
item.js === true ? {} : item.js;
if (!addInit)
addInit = block && !item.elem;
}
// Process nested mixes
if (!hasItem || visited[key])
continue;
visited[key] = true;
var nestedEntity = this.entities[key];
if (!nestedEntity)
continue;
var oldBlock = context.block;
var oldElem = context.elem;
var nestedMix = nestedEntity.mix.exec(context);
context.elem = oldElem;
context.block = oldBlock;
if (!nestedMix)
continue;
for (var j = 0; j < nestedMix.length; j++) {
var nestedItem = nestedMix[j];
if (!nestedItem.block &&
!nestedItem.elem ||
!visited[classBuilder.build(nestedItem.block, nestedItem.elem)]) {
nestedItem._block = block;
nestedItem._elem = elem;
mix = mix.slice(0, i + 1).concat(
nestedItem,
mix.slice(i + 1)
);
}
}
}
return {
out: out,
jsParams: js,
addJSInitClass: addInit
};
};
BEMXJST.prototype.buildModsClasses = function buildModsClasses(block,
elem,
mods) {
if (!mods)
return '';
var res = '';
var modName;
for (modName in mods) {
if (!mods.hasOwnProperty(modName))
continue;
var modVal = mods[modName];
if (!modVal && modVal !== 0) continue;
if (typeof modVal !== 'boolean')
modVal += '';
var builder = this.classBuilder;
res += ' ' + (elem ?
builder.buildElemClass(block, elem, modName, modVal) :
builder.buildBlockClass(block, modName, modVal));
}
return res;
};
BEMXJST.prototype.renderContent = function renderContent(content, isBEM) {
var context = this.context;
var oldPos = context.position;
var oldListLength = context._listLength;
var oldNotNewList = context._notNewList;
context._notNewList = false;
if (isBEM) {
context.position = 1;
context._listLength = 1;
}
var res = this._run(content);
context.position = oldPos;
context._listLength = oldListLength;
context._notNewList = oldNotNewList;
return res;
};
BEMXJST.prototype.renderNoTag = function renderNoTag(context,
js,
bem,
cls,
mix,
attrs,
content) {
// TODO(indutny): skip apply next flags
if (content || content === 0)
return this._run(content);
return '';
};
BEMXJST.prototype.local = function local(changes, body) {
var keys = Object.keys(changes);
var restore = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var parts = key.split('.');
var value = this.context;
for (var j = 0; j < parts.length - 1; j++)
value = value[parts[j]];
restore.push({
parts: parts,
value: value[parts[j]]
});
value[parts[j]] = changes[key];
}
var res = body.call(this.context);
for (var i = 0; i < restore.length; i++) {
var parts = restore[i].parts;
var value = this.context;
for (var j = 0; j < parts.length - 1; j++)
value = value[parts[j]];
value[parts[j]] = restore[i].value;
}
return res;
};
BEMXJST.prototype.applyNext = function applyNext() {
return this.match.exec(this.context);
};
BEMXJST.prototype.applyMode = function applyMode(mode, changes) {
var match = this.match.entity.rest[mode];
if (!match)
return;
if (!changes)
return match.exec(this.context);
var self = this;
// Allocate function this way, to prevent allocation at the top of the
// `applyMode`
var fn = function localBody() {
return match.exec(self.context);
};
return this.local(changes, fn);
};
BEMXJST.prototype.exportApply = function exportApply(exports) {
var self = this;
exports.apply = function apply(context) {
return self.run(context);
};
// Add templates at run time
exports.compile = function compile(templates) {
return self.compile(templates);
};
var sharedContext = {};
exports.BEMContext = this.contextConstructor;
sharedContext.BEMContext = exports.BEMContext;
for (var i = 0; i < this.oninit.length; i++) {
var oninit = this.oninit[i];
oninit(exports, sharedContext);
}
};
},{"./class-builder":2,"./context":3,"./entity":4,"./tree":7,"./utils":8,"inherits":9}],6:[function(require,module,exports){
var utils = require('./utils');
var PropertyMatch = require('./tree').PropertyMatch;
var OnceMatch = require('./tree').OnceMatch;
var WrapMatch = require('./tree').WrapMatch;
var PropertyAbsent = require('./tree').PropertyAbsent;
var CustomMatch = require('./tree').CustomMatch;
function MatchProperty(template, pred) {
this.template = template;
this.key = pred.key;
this.value = pred.value;
}
MatchProperty.prototype.exec = function exec(context) {
return context[this.key] === this.value;
};
function MatchNested(template, pred) {
this.template = template;
this.keys = pred.key;
this.value = pred.value;
}
MatchNested.prototype.exec = function exec(context) {
var val = context;
for (var i = 0; i < this.keys.length - 1; i++) {
val = val[this.keys[i]];
if (!val)
return false;
}
return val[this.keys[i]] === this.value;
};
function MatchAbsent(template, pred) {
this.template = template;
this.key = pred.key;
}
MatchAbsent.prototype.exec = function exec(context) {
return !context[this.key];
};
function MatchCustom(template, pred) {
this.template = template;
this.body = pred.body;
}
MatchCustom.prototype.exec = function exec(context) {
return this.body.call(context);
};
function MatchOnce(template) {
this.template = template;
this.once = null;
}
MatchOnce.prototype.exec = function exec(context) {
var res = this.once !== context._onceRef;
this.once = context._onceRef;
return res;
};
function MatchWrap(template) {
this.template = template;
this.wrap = null;
}
MatchWrap.prototype.exec = function exec(context) {
var res = this.wrap !== context.ctx;
this.wrap = context.ctx;
return res;
};
function MatchTemplate(mode, template) {
this.mode = mode;
this.predicates = new Array(template.predicates.length);
this.body = template.body;
var postpone = [];
for (var i = 0, j = 0; i < this.predicates.length; i++, j++) {
var pred = template.predicates[i];
if (pred instanceof PropertyMatch) {
if (utils.isArray(pred.key))
this.predicates[j] = new MatchNested(this, pred);
else
this.predicates[j] = new MatchProperty(this, pred);
} else if (pred instanceof PropertyAbsent) {
this.predicates[j] = new MatchAbsent(this, pred);
} else if (pred instanceof CustomMatch) {
this.predicates[j] = new MatchCustom(this, pred);
// Push OnceMatch and MatchWrap later, they should not be executed first.
// Otherwise they will set flag too early, and body might not be executed
} else if (pred instanceof OnceMatch) {
j--;
postpone.push(new MatchOnce(this));
} else if (pred instanceof WrapMatch) {
j--;
postpone.push(new MatchWrap(this));
} else {
// Skip
j--;
}
}
// Insert late predicates
for (var i = postpone.length - 1; i >= 0; i--)
this.predicates[i + j] = this.predicates[i];
for (var i = 0; i < postpone.length; i++)
this.predicates[i] = postpone[i];
j += postpone.length;
if (this.predicates.length !== j)
this.predicates.length = j;
}
exports.MatchTemplate = MatchTemplate;
function Match(entity) {
this.entity = entity;
this.bemxjst = this.entity.bemxjst;
this.templates = [];
// applyNext mask
this.mask = [ 0 ];
// We are going to create copies of mask for nested `applyNext()`
this.maskSize = 0;
this.maskOffset = 0;
this.count = 0;
this.depth = -1;
}
exports.Match = Match;
Match.prototype.clone = function clone(entity) {
var res = new Match(entity);
res.templates = this.templates.slice();
res.mask = this.mask.slice();
res.maskSize = this.maskSize;
res.count = this.count;
return res;
};
Match.prototype.prepend = function prepend(other) {
this.templates = other.templates.concat(this.templates);
this.count += other.count;
while (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);
this.maskSize = this.mask.length;
};
Match.prototype.push = function push(template) {
this.templates.push(new MatchTemplate(this, template));
this.count++;
if (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);
this.maskSize = this.mask.length;
};
Match.prototype.exec = function exec(context) {
var save = this.checkDepth();
var template;
var bitIndex = this.maskOffset;
var mask = this.mask[bitIndex];
var bit = 1;
for (var i = 0; i < this.count; i++) {
if ((mask & bit) === 0) {
template = this.templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
/* jshint maxdepth : false */
if (!pred.exec(context))
break;
}
// All predicates matched!
if (j === -1)
break;
}
if (bit === 0x40000000) {
bitIndex++;
mask = this.mask[bitIndex];
bit = 1;
} else {
bit <<= 1;
}
}
if (i === this.count)
return undefined;
var oldMask = mask;
var oldMatch = this.bemxjst.match;
this.mask[bitIndex] |= bit;
this.bemxjst.match = this;
var out;
if (typeof template.body === 'function')
out = template.body.call(context);
else
out = template.body;
this.mask[bitIndex] = oldMask;
this.bemxjst.match = oldMatch;
this.restoreDepth(save);
return out;
};
Match.prototype.checkDepth = function checkDepth() {
if (this.depth === -1) {
this.depth = this.bemxjst.depth;
return -1;
}
if (this.bemxjst.depth === this.depth)
return this.depth;
var depth = this.depth;
this.depth = this.bemxjst.depth;
this.maskOffset += this.maskSize;
while (this.mask.length < this.maskOffset + this.maskSize)
this.mask.push(0);
return depth;
};
Match.prototype.restoreDepth = function restoreDepth(depth) {
if (depth !== -1 && depth !== this.depth)
this.maskOffset -= this.maskSize;
this.depth = depth;
};
},{"./tree":7,"./utils":8}],7:[function(require,module,exports){
var assert = require('minimalistic-assert');
var inherits = require('inherits');
function Template(predicates, body) {
this.predicates = predicates;
this.body = body;
}
exports.Template = Template;
Template.prototype.wrap = function wrap() {
var body = this.body;
for (var i = 0; i < this.predicates.length; i++) {
var pred = this.predicates[i];
body = pred.wrapBody(body);
}
this.body = body;
};
Template.prototype.clone = function clone() {
return new Template(this.predicates.slice(), this.body);
};
function MatchBase() {
}
exports.MatchBase = MatchBase;
MatchBase.prototype.wrapBody = function wrapBody(body) {
return body;
};
function Item(tree, children) {
this.conditions = [];
this.children = [];
for (var i = children.length - 1; i >= 0; i--) {
var arg = children[i];
if (arg instanceof MatchBase)
this.conditions.push(arg);
else if (arg === tree.boundBody)
this.children[i] = tree.queue.pop();
else
this.children[i] = arg;
}
}
function OnceMatch() {
MatchBase.call(this);
}
inherits(OnceMatch, MatchBase);
exports.OnceMatch = OnceMatch;
function WrapMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(WrapMatch, MatchBase);
exports.WrapMatch = WrapMatch;
WrapMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
if (typeof body !== 'function') {
return function inlineAdaptor() {
return applyCtx(body);
};
}
return function wrapAdaptor() {
return applyCtx(body.call(this));
};
};
function ReplaceMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(ReplaceMatch, MatchBase);
exports.ReplaceMatch = ReplaceMatch;
ReplaceMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
if (typeof body !== 'function') {
return function inlineAdaptor() {
return applyCtx(body);
};
}
return function replaceAdaptor() {
return applyCtx(body.call(this));
};
};
function ExtendMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(ExtendMatch, MatchBase);
exports.ExtendMatch = ExtendMatch;
ExtendMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
var local = this.refs.local;
if (typeof body !== 'function') {
return function inlineAdaptor() {
var changes = {};
var keys = Object.keys(body);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = body[keys[i]];
return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
});
};
}
return function localAdaptor() {
var changes = {};
var obj = body.call(this);
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = obj[keys[i]];
return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
});
};
};
function CompilerOptions(options) {
MatchBase.call(this);
this.options = options;
}
inherits(CompilerOptions, MatchBase);
exports.CompilerOptions = CompilerOptions;
function PropertyMatch(key, value) {
MatchBase.call(this);
this.key = key;
this.value = value;
}
inherits(PropertyMatch, MatchBase);
exports.PropertyMatch = PropertyMatch;
function PropertyAbsent(key) {
MatchBase.call(this);
this.key = key;
}
inherits(PropertyAbsent, MatchBase);
exports.PropertyAbsent = PropertyAbsent;
function CustomMatch(body) {
MatchBase.call(this);
this.body = body;
}
inherits(CustomMatch, MatchBase);
exports.CustomMatch = CustomMatch;
function Tree(options) {
this.options = options;
this.refs = this.options.refs;
this.boundBody = this.body.bind(this);
var methods = this.methods('body');
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
// NOTE: method.name is empty because of .bind()
this.boundBody[Tree.methods[i]] = method;
}
this.queue = [];
this.templates = [];
this.initializers = [];
}
exports.Tree = Tree;
Tree.methods = [
'match', 'once', 'wrap', 'elemMatch', 'block', 'elem', 'mode', 'mod',
'elemMod', 'def', 'tag', 'attrs', 'cls', 'js', 'jsAttr',
'bem', 'mix', 'content', 'replace', 'extend', 'oninit',
'xjstOptions'
];
Tree.prototype.build = function build(templates, apply) {
var methods = this.methods('global').concat(apply);
methods[0] = this.match.bind(this);
templates.apply({}, methods);
return {
templates: this.templates.slice().reverse(),
oninit: this.initializers
};
};
function methodFactory(self, kind, name) {
var method = self[name];
var boundBody = self.boundBody;
if (kind !== 'body') {
if (name === 'replace' || name === 'extend' || name === 'wrap') {
return function wrapExtended() {
return method.apply(self, arguments);
};
}
return function wrapNotBody() {
method.apply(self, arguments);
return boundBody;
};
}
return function wrapBody() {
var res = method.apply(self, arguments);
// Insert body into last item
var child = self.queue.pop();
var last = self.queue[self.queue.length - 1];
last.conditions = last.conditions.concat(child.conditions);
last.children = last.children.concat(child.children);
if (name === 'replace' || name === 'extend' || name === 'wrap')
return res;
return boundBody;
};
}
Tree.prototype.methods = function methods(kind) {
var out = new Array(Tree.methods.length);
for (var i = 0; i < out.length; i++) {
var name = Tree.methods[i];
out[i] = methodFactory(this, kind, name);
}
return out;
};
// Called after all matches
Tree.prototype.flush = function flush(conditions, item) {
var subcond;
if (item.conditions)
subcond = conditions.concat(item.conditions);
else
subcond = item.conditions;
for (var i = 0; i < item.children.length; i++) {
var arg = item.children[i];
// Go deeper
if (arg instanceof Item) {
this.flush(subcond, item.children[i]);
// Body
} else {
var template = new Template(conditions, arg);
template.wrap();
this.templates.push(template);
}
}
};
Tree.prototype.body = function body() {
var children = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++)
children[i] = arguments[i];
var child = new Item(this, children);
this.queue[this.queue.length - 1].children.push(child);
if (this.queue.length === 1)
this.flush([], this.queue.shift());
return this.boundBody;
};
Tree.prototype.match = function match() {
var children = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (typeof arg === 'function')
arg = new CustomMatch(arg);
assert(arg instanceof MatchBase, 'Wrong .match() argument');
children[i] = arg;
}
this.queue.push(new Item(this, children));
return this.boundBody;
};
Tree.prototype.once = function once() {
return this.match(new OnceMatch());
};
Tree.prototype.wrap = function wrap() {
return this.def().match(new WrapMatch(this.refs));
};
Tree.prototype.xjstOptions = function xjstOptions(options) {
this.queue.push(new Item(this, [
new CompilerOptions(options)
]));
return this.boundBody;
};
Tree.prototype.block = function block(name) {
return this.match(new PropertyMatch('block', name));
};
Tree.prototype.elemMatch = function elemMatch() {
return this.match.apply(this, arguments);
};
Tree.prototype.elem = function elem(name) {
return this.match(new PropertyMatch('elem', name));
};
Tree.prototype.mode = function mode(name) {
return this.match(new PropertyMatch('_mode', name));
};
Tree.prototype.mod = function mod(name, value) {
return this.match(new PropertyMatch([ 'mods', name ], value));
};
Tree.prototype.elemMod = function elemMod(name, value) {
return this.match(new PropertyMatch([ 'elemMods', name ], value));
};
Tree.prototype.def = function def() { return this.mode('default'); };
Tree.prototype.tag = function tag() { return this.mode('tag'); };
Tree.prototype.attrs = function attrs() { return this.mode('attrs'); };
Tree.prototype.cls = function cls() { return this.mode('cls'); };
Tree.prototype.js = function js() { return this.mode('js'); };
Tree.prototype.jsAttr = function jsAttr() { return this.mode('jsAttr'); };
Tree.prototype.bem = function bem() { return this.mode('bem'); };
Tree.prototype.mix = function mix() { return this.mode('mix'); };
Tree.prototype.content = function content() { return this.mode('content'); };
Tree.prototype.replace = function replace() {
return this.def().match(new ReplaceMatch(this.refs));
};
Tree.prototype.extend = function extend() {
return this.def().match(new ExtendMatch(this.refs));
};
Tree.prototype.oninit = function oninit(fn) {
this.initializers.push(fn);
};
},{"inherits":9,"minimalistic-assert":10}],8:[function(require,module,exports){
/**
* Pattern for acceptable names of elements and modifiers
* @const
* @type String
*/
/* jshint unused : false */
var NAME_PATTERN = '[a-zA-Z0-9-]+';
var toString = Object.prototype.toString;
exports.isArray = Array.isArray;
if (!exports.isArray) {
exports.isArray = function isArrayPolyfill(obj) {
return toString.call(obj) === '[object Array]';
};
}
exports.xmlEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
exports.attrEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;');
};
exports.jsAttrEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/'/g, '&#39;');
};
exports.extend = function extend(o1, o2) {
if (!o1 || !o2)
return o1 || o2;
var res = {};
var n;
for (n in o1)
if (o1.hasOwnProperty(n))
res[n] = o1[n];
for (n in o2)
if (o2.hasOwnProperty(n))
res[n] = o2[n];
return res;
};
var SHORT_TAGS = { // хэш для быстрого определения, является ли тэг коротким
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, wbr: 1
};
exports.isShortTag = function isShortTag(t) {
return SHORT_TAGS.hasOwnProperty(t);
};
exports.isSimple = function isSimple(obj) {
if (!obj || obj === true) return true;
return typeof obj === 'string' || typeof obj === 'number';
};
var uniqCount = 0;
var uniqId = +new Date();
var uniqExpando = '__' + uniqId;
var uniqPrefix = 'uniq' + uniqId;
function getUniq() {
return uniqPrefix + (++uniqCount);
}
exports.getUniq = getUniq;
exports.identify = function identify(obj, onlyGet) {
if (!obj)
return getUniq();
if (onlyGet || obj[uniqExpando])
return obj[uniqExpando];
var u = getUniq();
obj[uniqExpando] = u;
return u;
};
},{}],9:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],10:[function(require,module,exports){
module.exports = assert;
function assert(val, msg) {
if (!val)
throw new Error(msg || 'Assertion failed');
}
assert.equal = function assertEqual(l, r, msg) {
if (l != r)
throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r));
};
},{}]},{},[1])(1)
});;
return module.exports ||
exports.BEMHTML;
}({}, {});
/// -------------------------------------
/// --------- BEM-XJST Runtime End ------
/// -------------------------------------
var api = new BEMHTML({});
/// -------------------------------------
/// ------ BEM-XJST User-code Start -----
/// -------------------------------------
api.compile(function(match, once, wrap, elemMatch, block, elem, mode, mod, elemMod, def, tag, attrs, cls, js, jsAttr, bem, mix, content, replace, extend, oninit, xjstOptions, local, applyCtx, applyNext, apply) {
;
});
api.exportApply(exports);
/// -------------------------------------
/// ------ BEM-XJST User-code End -------
/// -------------------------------------
/// -------------------------------------
/// --------- BEM-XJST Runtime Start ----
/// -------------------------------------
var BEMHTML = function(module, exports) {
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.bemtree = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var BEMXJST = require('../../bemxjst/runtime/index');
function BEMTREE() {
BEMXJST.apply(this, arguments);
}
BEMTREE.prototype = Object.create(BEMXJST.prototype);
BEMTREE.prototype.runMany = function runMany(arr) {
var out = [];
var context = this.context;
var prevPos = context.position;
var prevNotNewList = context._notNewList;
if (prevNotNewList) {
context._listLength += arr.length - 1;
} else {
context.position = 0;
context._listLength = arr.length;
}
context._notNewList = true;
if (this.canFlush) {
for (var i = 0; i < arr.length; i++)
out += context._flush(this._run(arr[i])); // TODO: fixme!
} else {
for (var i = 0; i < arr.length; i++)
out.push(this._run(arr[i]));
}
if (!prevNotNewList)
context.position = prevPos;
return out;
};
BEMTREE.prototype.render = function render(context,
entity,
tag,
js,
bem,
cls,
mix,
attrs,
content) {
var ctx = context.ctx;
var isBEM = !!(ctx.block || ctx.elem || ctx.bem); // TODO: check
if (typeof content === 'undefined') return ctx;
ctx.content = this.renderContent(content, isBEM);
return ctx;
};
BEMTREE.prototype._run = function _run(context) {
if (!context) return context;
return BEMXJST.prototype._run.call(this, context);
};
var Tree = require('../../bemxjst/runtime/tree').Tree;
// TODO: fixme copy/paste
BEMTREE.locals = Tree.methods.concat('local', 'applyCtx', 'applyNext', 'apply');
module.exports = BEMTREE;
},{"../../bemxjst/runtime/index":5,"../../bemxjst/runtime/tree":7}],2:[function(require,module,exports){
function ClassBuilder(options) {
this.modDelim = options.mod || '_';
this.elemDelim = options.elem || '__';
}
exports.ClassBuilder = ClassBuilder;
ClassBuilder.prototype.build = function build(block, elem) {
if (elem === undefined)
return block;
else
return block + this.elemDelim + elem;
};
ClassBuilder.prototype.buildModPostfix = function buildModPostfix(modName,
modVal) {
var res = this.modDelim + modName;
if (modVal !== true) res += this.modDelim + modVal;
return res;
};
ClassBuilder.prototype.buildBlockClass = function buildBlockClass(name,
modName,
modVal) {
var res = name;
if (modVal) res += this.buildModPostfix(modName, modVal);
return res;
};
ClassBuilder.prototype.buildElemClass = function buildElemClass(block,
name,
modName,
modVal) {
var res = this.buildBlockClass(block) + this.elemDelim + name;
if (modVal) res += this.buildModPostfix(modName, modVal);
return res;
};
ClassBuilder.prototype.split = function split(key) {
return key.split(this.elemDelim, 2);
};
},{}],3:[function(require,module,exports){
var utils = require('./utils');
function Context(bemxjst) {
this._bemxjst = bemxjst;
this.ctx = null;
this.block = '';
// Save current block until the next BEM entity
this._currBlock = '';
this.elem = null;
this.mods = {};
this.elemMods = {};
this.position = 0;
this._listLength = 0;
this._notNewList = false;
this._uniq = null;
// Used in `OnceMatch` check to detect context change
this._onceRef = {};
}
exports.Context = Context;
Context.prototype._flush = null;
Context.prototype.isArray = utils.isArray;
Context.prototype.isSimple = utils.isSimple;
Context.prototype.isShortTag = utils.isShortTag;
Context.prototype.extend = utils.extend;
Context.prototype.identify = utils.identify;
Context.prototype.xmlEscape = utils.xmlEscape;
Context.prototype.attrEscape = utils.attrEscape;
Context.prototype.jsAttrEscape = utils.jsAttrEscape;
Context.prototype.BEM = {};
Context.prototype.isFirst = function isFirst() {
return this.position === 1;
};
Context.prototype.isLast = function isLast() {
return this.position === this._listLength;
};
Context.prototype.generateId = function generateId() {
if (this._uniq === null)
this._uniq = utils.getUniq();
return this._uniq;
};
Context.prototype.reapply = function reapply(ctx) {
return this._bemxjst.run(ctx);
};
},{"./utils":8}],4:[function(require,module,exports){
var utils = require('./utils');
var Template = require('./tree').Template;
var PropertyMatch = require('./tree').PropertyMatch;
var CompilerOptions = require('./tree').CompilerOptions;
var Match = require('./match').Match;
function Entity(bemxjst, block, elem, templates) {
this.bemxjst = bemxjst;
this.block = null;
this.elem = null;
this.jsClass = null;
// `true` if entity has just a default renderer for `def()` mode
this.canFlush = true;
// Compiler options via `xjstOptions()`
this.options = {};
// "Fast modes"
this.def = new Match(this);
this.tag = new Match(this);
this.attrs = new Match(this);
this.mod = new Match(this);
this.js = new Match(this);
this.mix = new Match(this);
this.bem = new Match(this);
this.cls = new Match(this);
this.content = new Match(this);
// "Slow modes"
this.rest = {};
// Initialize
this.init(block, elem);
this.initModes(templates);
}
exports.Entity = Entity;
Entity.prototype.init = function init(block, elem) {
this.block = block;
this.elem = elem;
// Class for jsParams
this.jsClass = this.bemxjst.classBuilder.build(this.block, this.elem);
};
function contentMode() {
return this.ctx.content;
}
Entity.prototype.initModes = function initModes(templates) {
/* jshint maxdepth : false */
for (var i = 0; i < templates.length; i++) {
var template = templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
if (!(pred instanceof PropertyMatch))
continue;
if (pred.key !== '_mode')
continue;
template.predicates.splice(j, 1);
this._initRest(pred.value);
// All templates should go there anyway
this.rest[pred.value].push(template);
break;
}
if (j === -1)
this.def.push(template);
// Merge compiler options
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
if (!(pred instanceof CompilerOptions))
continue;
this.options = utils.extend(this.options, pred.options);
}
}
};
Entity.prototype._initRest = function _initRest(key) {
if (key === 'tag' ||
key === 'attrs' ||
key === 'js' ||
key === 'mix' ||
key === 'bem' ||
key === 'cls' ||
key === 'content' ||
key === 'default') {
if (key === 'default')
this.rest[key] = this.def;
else
this.rest[key] = this[key];
} else {
if (!this.rest.hasOwnProperty(key))
this.rest[key] = new Match(this);
}
};
Entity.prototype.setDefaults = function setDefaults() {
// Default .content() template for applyNext()
if (this.content.count !== 0)
this.content.push(new Template([], contentMode));
// .def() default
if (this.def.count !== 0) {
// `.xjstOptions({ flush: true })` will override this
this.canFlush = this.options.flush || false;
var self = this;
this.def.push(new Template([], function defaultBodyProxy() {
return self.defaultBody(this);
}));
}
};
Entity.prototype.prepend = function prepend(other) {
// Prepend to the slow modes, fast modes are in this hashmap too anyway
var keys = Object.keys(this.rest);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (!other.rest[key])
continue;
this.rest[key].prepend(other.rest[key]);
}
// Add new slow modes
keys = Object.keys(other.rest);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (this.rest[key])
continue;
this._initRest(key);
this.rest[key].prepend(other.rest[key]);
}
};
// NOTE: This could be potentially compiled into inlined invokations
Entity.prototype.run = function run(context) {
if (this.def.count !== 0)
return this.def.exec(context);
return this.defaultBody(context);
};
Entity.prototype.defaultBody = function defaultBody(context) {
var tag = context.ctx.tag;
if (tag === undefined)
tag = this.tag.exec(context);
var js;
if (context.ctx.js !== false)
js = this.js.exec(context);
var bem = this.bem.exec(context);
var cls = this.cls.exec(context);
var mix = this.mix.exec(context);
var attrs = this.attrs.exec(context);
var content = this.content.exec(context);
// Default content
if (this.content.count === 0 && content === undefined)
content = context.ctx.content;
return this.bemxjst.render(context,
this,
tag,
js,
bem,
cls,
mix,
attrs,
content);
};
},{"./match":6,"./tree":7,"./utils":8}],5:[function(require,module,exports){
var inherits = require('inherits');
var Tree = require('./tree').Tree;
var PropertyMatch = require('./tree').PropertyMatch;
var Entity = require('./entity').Entity;
var Context = require('./context').Context;
var ClassBuilder = require('./class-builder').ClassBuilder;
var utils = require('./utils');
function BEMXJST(options) {
this.options = options || {};
this.entities = null;
this.defaultEnt = null;
// Current tree
this.tree = null;
// Current match
this.match = null;
// Create new Context constructor for overriding prototype
this.contextConstructor = function ContextChild(bemxjst) {
Context.call(this, bemxjst);
};
inherits(this.contextConstructor, Context);
this.context = null;
this.classBuilder = new ClassBuilder(this.options.naming || {});
// Execution depth, used to invalidate `applyNext` bitfields
this.depth = 0;
// Do not call `_flush` on overridden `def()` mode
this.canFlush = false;
// oninit templates
this.oninit = null;
// Initialize default entity (no block/elem match)
this.defaultEnt = new Entity(this, '', '', []);
this.defaultElemEnt = new Entity(this, '', '', []);
}
module.exports = BEMXJST;
BEMXJST.locals = Tree.methods.concat('local', 'applyCtx', 'applyNext', 'apply');
BEMXJST.prototype.compile = function compile(code) {
var self = this;
function applyCtx() {
return self._run(self.context.ctx);
}
function applyCtxWrap(ctx, changes) {
// Fast case
if (!changes)
return self.local({ ctx: ctx }, applyCtx);
return self.local(changes, function() {
return self.local({ ctx: ctx }, applyCtx);
});
}
function apply(mode, changes) {
return self.applyMode(mode, changes);
}
function localWrap(changes) {
return function localBody(body) {
return self.local(changes, body);
};
}
var tree = new Tree({
refs: {
applyCtx: applyCtxWrap,
local: localWrap
}
});
// Yeah, let people pass functions to us!
var templates = this.recompileInput(code);
var out = tree.build(templates, [
localWrap,
applyCtxWrap,
function applyNextWrap(changes) {
if (changes)
return self.local(changes, applyNextWrap);
return self.applyNext();
},
apply
]);
// Concatenate templates with existing ones
// TODO(indutny): it should be possible to incrementally add templates
if (this.tree) {
out = {
templates: out.templates.concat(this.tree.templates),
oninit: this.tree.oninit.concat(out.oninit)
};
}
this.tree = out;
// Group block+elem entities into a hashmap
var ent = this.groupEntities(out.templates);
// Transform entities from arrays to Entity instances
ent = this.transformEntities(ent);
this.entities = ent;
this.oninit = out.oninit;
};
BEMXJST.prototype.recompileInput = function recompileInput(code) {
var out = code.toString();
var args = BEMXJST.locals;
if (typeof code === 'function') {
var start = out.match(/^function\s*\(([^{]+)\)\s*{/);
// Reuse function if it already has right arguments
if (start !== null && start[1].split(/,\s*/).length === args.length)
return code;
}
// Strip the function
out = out.replace(/^function[^{]+{|}$/g, '');
// And recompile it with right arguments
out = new Function(args.join(', '), out);
return out;
};
BEMXJST.prototype.groupEntities = function groupEntities(tree) {
var res = {};
for (var i = 0; i < tree.length; i++) {
// Make sure to change only the copy, the original is cached in `this.tree`
var template = tree[i].clone();
var block = null;
var elem;
elem = undefined;
for (var j = 0; j < template.predicates.length; j++) {
var pred = template.predicates[j];
if (!(pred instanceof PropertyMatch))
continue;
if (pred.key === 'block')
block = pred.value;
else if (pred.key === 'elem')
elem = pred.value;
else
continue;
// Remove predicate, we won't much against it
template.predicates.splice(j, 1);
j--;
}
// TODO(indutny): print out the template itself
if (block === null)
throw new Error('block("...") not found in one of the templates');
var key = this.classBuilder.build(block, elem);
if (!res[key])
res[key] = [];
res[key].push(template);
}
return res;
};
BEMXJST.prototype.transformEntities = function transformEntities(entities) {
var wildcardElems = [];
var keys = Object.keys(entities);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
// TODO(indutny): pass this values over
var parts = this.classBuilder.split(key);
var block = parts[0];
var elem = parts[1];
if (elem === '*')
wildcardElems.push(block);
entities[key] = new Entity(this, block, elem, entities[key]);
}
// Merge wildcard block templates
if (entities.hasOwnProperty('*')) {
var wildcard = entities['*'];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === '*')
continue;
entities[key].prepend(wildcard);
}
this.defaultEnt.prepend(wildcard);
this.defaultElemEnt.prepend(wildcard);
}
// Merge wildcard elem templates
for (var i = 0; i < wildcardElems.length; i++) {
var block = wildcardElems[i];
var wildcardKey = this.classBuilder.build(block, '*');
var wildcard = entities[wildcardKey];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key === wildcardKey)
continue;
var entity = entities[key];
if (entity.block !== block)
continue;
if (entity.elem === undefined)
continue;
entities[key].prepend(wildcard);
}
this.defaultElemEnt.prepend(wildcard);
}
// Set default templates after merging with wildcard
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
entities[key].setDefaults();
this.defaultEnt.setDefaults();
this.defaultElemEnt.setDefaults();
}
return entities;
};
BEMXJST.prototype._run = function _run(context) {
var res;
if (context === undefined || context === '' || context === null)
res = this.runEmpty();
else if (utils.isArray(context))
res = this.runMany(context);
else if (utils.isSimple(context))
res = this.runSimple(context);
else
res = this.runOne(context);
return res;
};
BEMXJST.prototype.run = function run(json) {
var match = this.match;
var context = this.context;
this.match = null;
this.context = new this.contextConstructor(this);
this.canFlush = this.context._flush !== null;
this.depth = 0;
var res = this._run(json);
if (this.canFlush)
res = this.context._flush(res);
this.match = match;
this.context = context;
return res;
};
BEMXJST.prototype.runEmpty = function runEmpty() {
this.context._listLength--;
return '';
};
BEMXJST.prototype.runMany = function runMany(arr) {
var out = '';
var context = this.context;
var prevPos = context.position;
var prevNotNewList = context._notNewList;
if (prevNotNewList) {
context._listLength += arr.length - 1;
} else {
context.position = 0;
context._listLength = arr.length;
}
context._notNewList = true;
if (this.canFlush) {
for (var i = 0; i < arr.length; i++)
out += context._flush(this._run(arr[i]));
} else {
for (var i = 0; i < arr.length; i++)
out += this._run(arr[i]);
}
if (!prevNotNewList)
context.position = prevPos;
return out;
};
BEMXJST.prototype.runSimple = function runSimple(context) {
this.context._listLength--;
var res = '';
if (context && context !== true || context === 0)
res += context;
return res;
};
BEMXJST.prototype.runOne = function runOne(json) {
var context = this.context;
var oldCtx = context.ctx;
var oldBlock = context.block;
var oldCurrBlock = context._currBlock;
var oldElem = context.elem;
var oldMods = context.mods;
var oldElemMods = context.elemMods;
if (json.block || json.elem)
context._currBlock = '';
else
context._currBlock = context.block;
context.ctx = json;
if (json.block) {
context.block = json.block;
if (json.mods)
context.mods = json.mods;
else
context.mods = {};
} else {
if (!json.elem)
context.block = '';
else if (oldCurrBlock)
context.block = oldCurrBlock;
}
context.elem = json.elem;
if (json.elemMods)
context.elemMods = json.elemMods;
else
context.elemMods = {};
var block = context.block || '';
var elem = context.elem;
// Control list position
if (block || elem)
context.position++;
else
context._listLength--;
// To invalidate `applyNext` flags
this.depth++;
var key = this.classBuilder.build(block, elem);
var restoreFlush = false;
var ent = this.entities[key];
if (ent) {
if (this.canFlush && !ent.canFlush) {
// Entity does not support flushing, do not flush anything nested
restoreFlush = true;
this.canFlush = false;
}
} else {
// No entity - use default one
ent = this.defaultEnt;
if (elem !== undefined)
ent = this.defaultElemEnt;
ent.init(block, elem);
}
var res = ent.run(context);
context.ctx = oldCtx;
context.block = oldBlock;
context.elem = oldElem;
context.mods = oldMods;
context.elemMods = oldElemMods;
context._currBlock = oldCurrBlock;
this.depth--;
if (restoreFlush)
this.canFlush = true;
return res;
};
BEMXJST.prototype.render = function render(context,
entity,
tag,
js,
bem,
cls,
mix,
attrs,
content) {
var ctx = context.ctx;
if (tag === undefined)
tag = 'div';
if (!tag)
return this.renderNoTag(context, js, bem, cls, mix, attrs, content);
var out = '<' + tag;
var ctxJS = ctx.js;
if (ctxJS !== false) {
if (js === true)
js = {};
if (js) {
if (ctxJS !== true)
js = utils.extend(ctxJS, js);
} else if (ctxJS === true) {
js = {};
} else {
js = ctxJS;
}
}
var jsParams;
if (js) {
jsParams = {};
jsParams[entity.jsClass] = js;
}
var isBEM = bem;
if (isBEM === undefined) {
if (ctx.bem === undefined)
isBEM = entity.block || entity.elem;
else
isBEM = ctx.bem;
}
isBEM = !!isBEM;
if (cls === undefined)
cls = ctx.cls;
var addJSInitClass = entity.block && jsParams && !entity.elem;
if (!isBEM && !cls) {
return this.renderClose(out, context, tag, attrs, isBEM, ctx, content);
}
out += ' class="';
if (isBEM) {
var mods = ctx.elemMods || ctx.mods;
if (!mods && ctx.block)
mods = context.mods;
out += entity.jsClass;
out += this.buildModsClasses(entity.block, entity.elem, mods);
var totalMix = mix;
if (ctx.mix) {
if (totalMix)
totalMix = [].concat(totalMix, ctx.mix);
else
totalMix = ctx.mix;
}
if (totalMix) {
var m = this.renderMix(entity, totalMix, jsParams, addJSInitClass);
out += m.out;
jsParams = m.jsParams;
addJSInitClass = m.addJSInitClass;
}
if (cls)
out += ' ' + cls;
} else {
if (cls)
out += cls;
}
if (addJSInitClass)
out += ' i-bem"';
else
out += '"';
if (isBEM && jsParams)
out += ' data-bem=\'' + utils.jsAttrEscape(JSON.stringify(jsParams)) + '\'';
return this.renderClose(out, context, tag, attrs, isBEM, ctx, content);
};
BEMXJST.prototype.renderClose = function renderClose(prefix,
context,
tag,
attrs,
isBEM,
ctx,
content) {
var out = prefix;
// NOTE: maybe we need to make an array for quicker serialization
attrs = utils.extend(attrs, ctx.attrs);
if (attrs) {
var name; // TODO: do something with OmetaJS and YUI Compressor
/* jshint forin : false */
for (name in attrs) {
var attr = attrs[name];
if (attr === undefined)
continue;
// TODO(indutny): support `this.reapply()`
out += ' ' + name + '="' +
utils.attrEscape(utils.isSimple(attr) ?
attr :
this.reapply(attr)) +
'"';
}
}
if (utils.isShortTag(tag)) {
out += '/>';
if (this.canFlush)
out = context._flush(out);
} else {
out += '>';
if (this.canFlush)
out = context._flush(out);
// TODO(indutny): skip apply next flags
if (content || content === 0)
out += this.renderContent(content, isBEM);
out += '</' + tag + '>';
}
if (this.canFlush)
out = context._flush(out);
return out;
};
BEMXJST.prototype.renderMix = function renderMix(entity,
mix,
jsParams,
addJSInitClass) {
var visited = {};
var context = this.context;
var js = jsParams;
var addInit = addJSInitClass;
visited[entity.jsClass] = true;
// Transform mix to the single-item array if it's not array
if (!utils.isArray(mix))
mix = [ mix ];
var classBuilder = this.classBuilder;
var out = '';
for (var i = 0; i < mix.length; i++) {
var item = mix[i];
if (item === undefined)
continue;
if (typeof item === 'string')
item = { block: item, elem: undefined };
var hasItem = item.block || item.elem;
var block = item.block || item._block || context.block;
var elem = item.elem || item._elem || context.elem;
var key = classBuilder.build(block, elem);
var classElem = item.elem ||
item._elem ||
(item.block ? undefined : context.elem);
if (hasItem)
out += ' ' + classBuilder.build(block, classElem);
out += this.buildModsClasses(block, classElem, item.elemMods || item.mods);
if (item.js) {
if (!js)
js = {};
js[classBuilder.build(block, item.elem)] =
item.js === true ? {} : item.js;
if (!addInit)
addInit = block && !item.elem;
}
// Process nested mixes
if (!hasItem || visited[key])
continue;
visited[key] = true;
var nestedEntity = this.entities[key];
if (!nestedEntity)
continue;
var oldBlock = context.block;
var oldElem = context.elem;
var nestedMix = nestedEntity.mix.exec(context);
context.elem = oldElem;
context.block = oldBlock;
if (!nestedMix)
continue;
for (var j = 0; j < nestedMix.length; j++) {
var nestedItem = nestedMix[j];
if (!nestedItem.block &&
!nestedItem.elem ||
!visited[classBuilder.build(nestedItem.block, nestedItem.elem)]) {
nestedItem._block = block;
nestedItem._elem = elem;
mix = mix.slice(0, i + 1).concat(
nestedItem,
mix.slice(i + 1)
);
}
}
}
return {
out: out,
jsParams: js,
addJSInitClass: addInit
};
};
BEMXJST.prototype.buildModsClasses = function buildModsClasses(block,
elem,
mods) {
if (!mods)
return '';
var res = '';
var modName;
for (modName in mods) {
if (!mods.hasOwnProperty(modName))
continue;
var modVal = mods[modName];
if (!modVal && modVal !== 0) continue;
if (typeof modVal !== 'boolean')
modVal += '';
var builder = this.classBuilder;
res += ' ' + (elem ?
builder.buildElemClass(block, elem, modName, modVal) :
builder.buildBlockClass(block, modName, modVal));
}
return res;
};
BEMXJST.prototype.renderContent = function renderContent(content, isBEM) {
var context = this.context;
var oldPos = context.position;
var oldListLength = context._listLength;
var oldNotNewList = context._notNewList;
context._notNewList = false;
if (isBEM) {
context.position = 1;
context._listLength = 1;
}
var res = this._run(content);
context.position = oldPos;
context._listLength = oldListLength;
context._notNewList = oldNotNewList;
return res;
};
BEMXJST.prototype.renderNoTag = function renderNoTag(context,
js,
bem,
cls,
mix,
attrs,
content) {
// TODO(indutny): skip apply next flags
if (content || content === 0)
return this._run(content);
return '';
};
BEMXJST.prototype.local = function local(changes, body) {
var keys = Object.keys(changes);
var restore = [];
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var parts = key.split('.');
var value = this.context;
for (var j = 0; j < parts.length - 1; j++)
value = value[parts[j]];
restore.push({
parts: parts,
value: value[parts[j]]
});
value[parts[j]] = changes[key];
}
var res = body.call(this.context);
for (var i = 0; i < restore.length; i++) {
var parts = restore[i].parts;
var value = this.context;
for (var j = 0; j < parts.length - 1; j++)
value = value[parts[j]];
value[parts[j]] = restore[i].value;
}
return res;
};
BEMXJST.prototype.applyNext = function applyNext() {
return this.match.exec(this.context);
};
BEMXJST.prototype.applyMode = function applyMode(mode, changes) {
var match = this.match.entity.rest[mode];
if (!match)
return;
if (!changes)
return match.exec(this.context);
var self = this;
// Allocate function this way, to prevent allocation at the top of the
// `applyMode`
var fn = function localBody() {
return match.exec(self.context);
};
return this.local(changes, fn);
};
BEMXJST.prototype.exportApply = function exportApply(exports) {
var self = this;
exports.apply = function apply(context) {
return self.run(context);
};
// Add templates at run time
exports.compile = function compile(templates) {
return self.compile(templates);
};
var sharedContext = {};
exports.BEMContext = this.contextConstructor;
sharedContext.BEMContext = exports.BEMContext;
for (var i = 0; i < this.oninit.length; i++) {
var oninit = this.oninit[i];
oninit(exports, sharedContext);
}
};
},{"./class-builder":2,"./context":3,"./entity":4,"./tree":7,"./utils":8,"inherits":9}],6:[function(require,module,exports){
var utils = require('./utils');
var PropertyMatch = require('./tree').PropertyMatch;
var OnceMatch = require('./tree').OnceMatch;
var WrapMatch = require('./tree').WrapMatch;
var PropertyAbsent = require('./tree').PropertyAbsent;
var CustomMatch = require('./tree').CustomMatch;
function MatchProperty(template, pred) {
this.template = template;
this.key = pred.key;
this.value = pred.value;
}
MatchProperty.prototype.exec = function exec(context) {
return context[this.key] === this.value;
};
function MatchNested(template, pred) {
this.template = template;
this.keys = pred.key;
this.value = pred.value;
}
MatchNested.prototype.exec = function exec(context) {
var val = context;
for (var i = 0; i < this.keys.length - 1; i++) {
val = val[this.keys[i]];
if (!val)
return false;
}
return val[this.keys[i]] === this.value;
};
function MatchAbsent(template, pred) {
this.template = template;
this.key = pred.key;
}
MatchAbsent.prototype.exec = function exec(context) {
return !context[this.key];
};
function MatchCustom(template, pred) {
this.template = template;
this.body = pred.body;
}
MatchCustom.prototype.exec = function exec(context) {
return this.body.call(context);
};
function MatchOnce(template) {
this.template = template;
this.once = null;
}
MatchOnce.prototype.exec = function exec(context) {
var res = this.once !== context._onceRef;
this.once = context._onceRef;
return res;
};
function MatchWrap(template) {
this.template = template;
this.wrap = null;
}
MatchWrap.prototype.exec = function exec(context) {
var res = this.wrap !== context.ctx;
this.wrap = context.ctx;
return res;
};
function MatchTemplate(mode, template) {
this.mode = mode;
this.predicates = new Array(template.predicates.length);
this.body = template.body;
var postpone = [];
for (var i = 0, j = 0; i < this.predicates.length; i++, j++) {
var pred = template.predicates[i];
if (pred instanceof PropertyMatch) {
if (utils.isArray(pred.key))
this.predicates[j] = new MatchNested(this, pred);
else
this.predicates[j] = new MatchProperty(this, pred);
} else if (pred instanceof PropertyAbsent) {
this.predicates[j] = new MatchAbsent(this, pred);
} else if (pred instanceof CustomMatch) {
this.predicates[j] = new MatchCustom(this, pred);
// Push OnceMatch and MatchWrap later, they should not be executed first.
// Otherwise they will set flag too early, and body might not be executed
} else if (pred instanceof OnceMatch) {
j--;
postpone.push(new MatchOnce(this));
} else if (pred instanceof WrapMatch) {
j--;
postpone.push(new MatchWrap(this));
} else {
// Skip
j--;
}
}
// Insert late predicates
for (var i = postpone.length - 1; i >= 0; i--)
this.predicates[i + j] = this.predicates[i];
for (var i = 0; i < postpone.length; i++)
this.predicates[i] = postpone[i];
j += postpone.length;
if (this.predicates.length !== j)
this.predicates.length = j;
}
exports.MatchTemplate = MatchTemplate;
function Match(entity) {
this.entity = entity;
this.bemxjst = this.entity.bemxjst;
this.templates = [];
// applyNext mask
this.mask = [ 0 ];
// We are going to create copies of mask for nested `applyNext()`
this.maskSize = 0;
this.maskOffset = 0;
this.count = 0;
this.depth = -1;
}
exports.Match = Match;
Match.prototype.clone = function clone(entity) {
var res = new Match(entity);
res.templates = this.templates.slice();
res.mask = this.mask.slice();
res.maskSize = this.maskSize;
res.count = this.count;
return res;
};
Match.prototype.prepend = function prepend(other) {
this.templates = other.templates.concat(this.templates);
this.count += other.count;
while (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);
this.maskSize = this.mask.length;
};
Match.prototype.push = function push(template) {
this.templates.push(new MatchTemplate(this, template));
this.count++;
if (Math.ceil(this.count / 31) > this.mask.length)
this.mask.push(0);
this.maskSize = this.mask.length;
};
Match.prototype.exec = function exec(context) {
var save = this.checkDepth();
var template;
var bitIndex = this.maskOffset;
var mask = this.mask[bitIndex];
var bit = 1;
for (var i = 0; i < this.count; i++) {
if ((mask & bit) === 0) {
template = this.templates[i];
for (var j = template.predicates.length - 1; j >= 0; j--) {
var pred = template.predicates[j];
/* jshint maxdepth : false */
if (!pred.exec(context))
break;
}
// All predicates matched!
if (j === -1)
break;
}
if (bit === 0x40000000) {
bitIndex++;
mask = this.mask[bitIndex];
bit = 1;
} else {
bit <<= 1;
}
}
if (i === this.count)
return undefined;
var oldMask = mask;
var oldMatch = this.bemxjst.match;
this.mask[bitIndex] |= bit;
this.bemxjst.match = this;
var out;
if (typeof template.body === 'function')
out = template.body.call(context);
else
out = template.body;
this.mask[bitIndex] = oldMask;
this.bemxjst.match = oldMatch;
this.restoreDepth(save);
return out;
};
Match.prototype.checkDepth = function checkDepth() {
if (this.depth === -1) {
this.depth = this.bemxjst.depth;
return -1;
}
if (this.bemxjst.depth === this.depth)
return this.depth;
var depth = this.depth;
this.depth = this.bemxjst.depth;
this.maskOffset += this.maskSize;
while (this.mask.length < this.maskOffset + this.maskSize)
this.mask.push(0);
return depth;
};
Match.prototype.restoreDepth = function restoreDepth(depth) {
if (depth !== -1 && depth !== this.depth)
this.maskOffset -= this.maskSize;
this.depth = depth;
};
},{"./tree":7,"./utils":8}],7:[function(require,module,exports){
var assert = require('minimalistic-assert');
var inherits = require('inherits');
function Template(predicates, body) {
this.predicates = predicates;
this.body = body;
}
exports.Template = Template;
Template.prototype.wrap = function wrap() {
var body = this.body;
for (var i = 0; i < this.predicates.length; i++) {
var pred = this.predicates[i];
body = pred.wrapBody(body);
}
this.body = body;
};
Template.prototype.clone = function clone() {
return new Template(this.predicates.slice(), this.body);
};
function MatchBase() {
}
exports.MatchBase = MatchBase;
MatchBase.prototype.wrapBody = function wrapBody(body) {
return body;
};
function Item(tree, children) {
this.conditions = [];
this.children = [];
for (var i = children.length - 1; i >= 0; i--) {
var arg = children[i];
if (arg instanceof MatchBase)
this.conditions.push(arg);
else if (arg === tree.boundBody)
this.children[i] = tree.queue.pop();
else
this.children[i] = arg;
}
}
function OnceMatch() {
MatchBase.call(this);
}
inherits(OnceMatch, MatchBase);
exports.OnceMatch = OnceMatch;
function WrapMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(WrapMatch, MatchBase);
exports.WrapMatch = WrapMatch;
WrapMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
if (typeof body !== 'function') {
return function inlineAdaptor() {
return applyCtx(body);
};
}
return function wrapAdaptor() {
return applyCtx(body.call(this));
};
};
function ReplaceMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(ReplaceMatch, MatchBase);
exports.ReplaceMatch = ReplaceMatch;
ReplaceMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
if (typeof body !== 'function') {
return function inlineAdaptor() {
return applyCtx(body);
};
}
return function replaceAdaptor() {
return applyCtx(body.call(this));
};
};
function ExtendMatch(refs) {
MatchBase.call(this);
this.refs = refs;
}
inherits(ExtendMatch, MatchBase);
exports.ExtendMatch = ExtendMatch;
ExtendMatch.prototype.wrapBody = function wrapBody(body) {
var applyCtx = this.refs.applyCtx;
var local = this.refs.local;
if (typeof body !== 'function') {
return function inlineAdaptor() {
var changes = {};
var keys = Object.keys(body);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = body[keys[i]];
return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
});
};
}
return function localAdaptor() {
var changes = {};
var obj = body.call(this);
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++)
changes['ctx.' + keys[i]] = obj[keys[i]];
return local(changes)(function preApplyCtx() {
return applyCtx(this.ctx);
});
};
};
function CompilerOptions(options) {
MatchBase.call(this);
this.options = options;
}
inherits(CompilerOptions, MatchBase);
exports.CompilerOptions = CompilerOptions;
function PropertyMatch(key, value) {
MatchBase.call(this);
this.key = key;
this.value = value;
}
inherits(PropertyMatch, MatchBase);
exports.PropertyMatch = PropertyMatch;
function PropertyAbsent(key) {
MatchBase.call(this);
this.key = key;
}
inherits(PropertyAbsent, MatchBase);
exports.PropertyAbsent = PropertyAbsent;
function CustomMatch(body) {
MatchBase.call(this);
this.body = body;
}
inherits(CustomMatch, MatchBase);
exports.CustomMatch = CustomMatch;
function Tree(options) {
this.options = options;
this.refs = this.options.refs;
this.boundBody = this.body.bind(this);
var methods = this.methods('body');
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
// NOTE: method.name is empty because of .bind()
this.boundBody[Tree.methods[i]] = method;
}
this.queue = [];
this.templates = [];
this.initializers = [];
}
exports.Tree = Tree;
Tree.methods = [
'match', 'once', 'wrap', 'elemMatch', 'block', 'elem', 'mode', 'mod',
'elemMod', 'def', 'tag', 'attrs', 'cls', 'js', 'jsAttr',
'bem', 'mix', 'content', 'replace', 'extend', 'oninit',
'xjstOptions'
];
Tree.prototype.build = function build(templates, apply) {
var methods = this.methods('global').concat(apply);
methods[0] = this.match.bind(this);
templates.apply({}, methods);
return {
templates: this.templates.slice().reverse(),
oninit: this.initializers
};
};
function methodFactory(self, kind, name) {
var method = self[name];
var boundBody = self.boundBody;
if (kind !== 'body') {
if (name === 'replace' || name === 'extend' || name === 'wrap') {
return function wrapExtended() {
return method.apply(self, arguments);
};
}
return function wrapNotBody() {
method.apply(self, arguments);
return boundBody;
};
}
return function wrapBody() {
var res = method.apply(self, arguments);
// Insert body into last item
var child = self.queue.pop();
var last = self.queue[self.queue.length - 1];
last.conditions = last.conditions.concat(child.conditions);
last.children = last.children.concat(child.children);
if (name === 'replace' || name === 'extend' || name === 'wrap')
return res;
return boundBody;
};
}
Tree.prototype.methods = function methods(kind) {
var out = new Array(Tree.methods.length);
for (var i = 0; i < out.length; i++) {
var name = Tree.methods[i];
out[i] = methodFactory(this, kind, name);
}
return out;
};
// Called after all matches
Tree.prototype.flush = function flush(conditions, item) {
var subcond;
if (item.conditions)
subcond = conditions.concat(item.conditions);
else
subcond = item.conditions;
for (var i = 0; i < item.children.length; i++) {
var arg = item.children[i];
// Go deeper
if (arg instanceof Item) {
this.flush(subcond, item.children[i]);
// Body
} else {
var template = new Template(conditions, arg);
template.wrap();
this.templates.push(template);
}
}
};
Tree.prototype.body = function body() {
var children = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++)
children[i] = arguments[i];
var child = new Item(this, children);
this.queue[this.queue.length - 1].children.push(child);
if (this.queue.length === 1)
this.flush([], this.queue.shift());
return this.boundBody;
};
Tree.prototype.match = function match() {
var children = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (typeof arg === 'function')
arg = new CustomMatch(arg);
assert(arg instanceof MatchBase, 'Wrong .match() argument');
children[i] = arg;
}
this.queue.push(new Item(this, children));
return this.boundBody;
};
Tree.prototype.once = function once() {
return this.match(new OnceMatch());
};
Tree.prototype.wrap = function wrap() {
return this.def().match(new WrapMatch(this.refs));
};
Tree.prototype.xjstOptions = function xjstOptions(options) {
this.queue.push(new Item(this, [
new CompilerOptions(options)
]));
return this.boundBody;
};
Tree.prototype.block = function block(name) {
return this.match(new PropertyMatch('block', name));
};
Tree.prototype.elemMatch = function elemMatch() {
return this.match.apply(this, arguments);
};
Tree.prototype.elem = function elem(name) {
return this.match(new PropertyMatch('elem', name));
};
Tree.prototype.mode = function mode(name) {
return this.match(new PropertyMatch('_mode', name));
};
Tree.prototype.mod = function mod(name, value) {
return this.match(new PropertyMatch([ 'mods', name ], value));
};
Tree.prototype.elemMod = function elemMod(name, value) {
return this.match(new PropertyMatch([ 'elemMods', name ], value));
};
Tree.prototype.def = function def() { return this.mode('default'); };
Tree.prototype.tag = function tag() { return this.mode('tag'); };
Tree.prototype.attrs = function attrs() { return this.mode('attrs'); };
Tree.prototype.cls = function cls() { return this.mode('cls'); };
Tree.prototype.js = function js() { return this.mode('js'); };
Tree.prototype.jsAttr = function jsAttr() { return this.mode('jsAttr'); };
Tree.prototype.bem = function bem() { return this.mode('bem'); };
Tree.prototype.mix = function mix() { return this.mode('mix'); };
Tree.prototype.content = function content() { return this.mode('content'); };
Tree.prototype.replace = function replace() {
return this.def().match(new ReplaceMatch(this.refs));
};
Tree.prototype.extend = function extend() {
return this.def().match(new ExtendMatch(this.refs));
};
Tree.prototype.oninit = function oninit(fn) {
this.initializers.push(fn);
};
},{"inherits":9,"minimalistic-assert":10}],8:[function(require,module,exports){
/**
* Pattern for acceptable names of elements and modifiers
* @const
* @type String
*/
/* jshint unused : false */
var NAME_PATTERN = '[a-zA-Z0-9-]+';
var toString = Object.prototype.toString;
exports.isArray = Array.isArray;
if (!exports.isArray) {
exports.isArray = function isArrayPolyfill(obj) {
return toString.call(obj) === '[object Array]';
};
}
exports.xmlEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
exports.attrEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;');
};
exports.jsAttrEscape = function(str) {
return (str + '')
.replace(/&/g, '&amp;')
.replace(/'/g, '&#39;');
};
exports.extend = function extend(o1, o2) {
if (!o1 || !o2)
return o1 || o2;
var res = {};
var n;
for (n in o1)
if (o1.hasOwnProperty(n))
res[n] = o1[n];
for (n in o2)
if (o2.hasOwnProperty(n))
res[n] = o2[n];
return res;
};
var SHORT_TAGS = { // хэш для быстрого определения, является ли тэг коротким
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, wbr: 1
};
exports.isShortTag = function isShortTag(t) {
return SHORT_TAGS.hasOwnProperty(t);
};
exports.isSimple = function isSimple(obj) {
if (!obj || obj === true) return true;
return typeof obj === 'string' || typeof obj === 'number';
};
var uniqCount = 0;
var uniqId = +new Date();
var uniqExpando = '__' + uniqId;
var uniqPrefix = 'uniq' + uniqId;
function getUniq() {
return uniqPrefix + (++uniqCount);
}
exports.getUniq = getUniq;
exports.identify = function identify(obj, onlyGet) {
if (!obj)
return getUniq();
if (onlyGet || obj[uniqExpando])
return obj[uniqExpando];
var u = getUniq();
obj[uniqExpando] = u;
return u;
};
},{}],9:[function(require,module,exports){
if (typeof Object.create === 'function') {
// implementation from standard node.js 'util' module
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
} else {
// old school shim for old browsers
module.exports = function inherits(ctor, superCtor) {
ctor.super_ = superCtor
var TempCtor = function () {}
TempCtor.prototype = superCtor.prototype
ctor.prototype = new TempCtor()
ctor.prototype.constructor = ctor
}
}
},{}],10:[function(require,module,exports){
module.exports = assert;
function assert(val, msg) {
if (!val)
throw new Error(msg || 'Assertion failed');
}
assert.equal = function assertEqual(l, r, msg) {
if (l != r)
throw new Error(msg || ('Assertion failed: ' + l + ' != ' + r));
};
},{}]},{},[1])(1)
});;
return module.exports ||
exports.BEMHTML;
}({}, {});
/// -------------------------------------
/// --------- BEM-XJST Runtime End ------
/// -------------------------------------
var api = new BEMHTML({});
/// -------------------------------------
/// ------ BEM-XJST User-code Start -----
/// -------------------------------------
api.compile(function(match, once, wrap, elemMatch, block, elem, mode, mod, elemMod, def, tag, attrs, cls, js, jsAttr, bem, mix, content, replace, extend, oninit, xjstOptions, local, applyCtx, applyNext, apply) {
;
});
api.exportApply(exports);
/// -------------------------------------
/// ------ BEM-XJST User-code End -------
/// -------------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment