// stupid little workaround so modes can be set without magic numbers in tag definitions // now made moot by being unable to use variables as a key var MARKUP_MODE_COMMENT = 1, MARKUP_MODE_ARTICLE = 2, MARKUP_MODE_QUICKFACTS = 3, MARKUP_MODE_SIGNATURE = 4, MARKUP_MODE_REPLY = 5, MARKUP_CLASS_ADMIN = 40, MARKUP_CLASS_STAFF = 30, MARKUP_CLASS_PREMIUM = 20, MARKUP_CLASS_USER = 10, MARKUP_CLASS_PENDING = 1; var MARKUP_SOURCE_LIVE = 1, MARKUP_SOURCE_PTR = 2, MARKUP_SOURCE_BETA = 3; var MarkupModeMap = {}; MarkupModeMap[MARKUP_MODE_COMMENT] = 'comment'; MarkupModeMap[MARKUP_MODE_REPLY] = 'reply'; MarkupModeMap[MARKUP_MODE_ARTICLE] = 'article'; MarkupModeMap[MARKUP_MODE_QUICKFACTS] = 'quickfacts'; MarkupModeMap[MARKUP_MODE_SIGNATURE] = 'signature'; var MarkupSourceMap = {}; MarkupSourceMap[MARKUP_SOURCE_LIVE] = 'live'; MarkupSourceMap[MARKUP_SOURCE_PTR] = 'ptr'; MarkupSourceMap[MARKUP_SOURCE_BETA] = 'beta'; var Markup = { MODE_COMMENT: MARKUP_MODE_COMMENT, MODE_REPLY: MARKUP_MODE_REPLY, MODE_ARTICLE: MARKUP_MODE_ARTICLE, MODE_QUICKFACTS: MARKUP_MODE_QUICKFACTS, MODE_SIGNATURE: MARKUP_MODE_SIGNATURE, SOURCE_LIVE: MARKUP_SOURCE_LIVE, SOURCE_PTR: MARKUP_SOURCE_PTR, SOURCE_BETA: MARKUP_SOURCE_BETA, CLASS_ADMIN: MARKUP_CLASS_ADMIN, CLASS_STAFF: MARKUP_CLASS_STAFF, CLASS_PREMIUM: MARKUP_CLASS_PREMIUM, CLASS_USER: MARKUP_CLASS_USER, CLASS_PENDING: MARKUP_CLASS_PENDING, whitelistedWebsites: [/(.*\.)?wowhead.com/i, /(.*\.)?thottbot.com/i, /(.*\.)?torhead.com/i, /(.*\.)?mmoui.com/i, /(.*\.)?tankspot.com/i, /(.*\.)?guildfans.com/i, /(.*\.)?allakhazam.com/i, /(.*\.)?zam.com/i, /(.*\.)?blizzard.com/i, /(.*\.)?worldofwarcraft.com/i, /(.*\.)?wow-europe.com/i, /(.*\.)?battle.net/i, /(.*\.)?sc2ranks.com/i, /(.*\.)?torchlightarmory.com/i, /(.*\.)?vindictusdb.com/i, /(.*\.)?wowinterface.com/i, /(.*\.)?vginterface.com/i, /(.*\.)?lotrointerface.com/i, /(.*\.)?eq2interface.com/i, /(.*\.)?eqinterface.com/i, /(.*\.)?mmo-champion.com/i, /(.*\.)?joystiq.com/i, /(.*\.)?wow-heroes.com/i, /(.*\.)?be-imba.hu/i, /(.*\.)?wowpedia.org/i, /(.*\.)?curse.com/i, /(.*\.)?elitistjerks.com/i, /(.*\.)?wowwiki.com/i, /(.*\.)?worldoflogs.com/i, /(.*\.)?wowinsider.com/i, /(.*\.)?guildwork.com/i], rolesToClass: function(roles) { if(roles & (U_GROUP_ADMIN|U_GROUP_VIP|U_GROUP_DEV)) return Markup.CLASS_ADMIN; else if(roles & U_GROUP_STAFF) return Markup.CLASS_STAFF; else if(roles & U_GROUP_PREMIUM) return Markup.CLASS_PREMIUM; else if(roles & U_GROUP_PENDING) return Markup.CLASS_PENDING; else return Markup.CLASS_USER; }, defaultSource: false, nameCol: 'name_enus', domainToLocale: { 'www': 'enus', 'ptr': 'ptr', 'beta': 'beta', 'mop': 'beta', 'fr': 'frfr', 'de': 'dede', 'es': 'eses', 'ru': 'ruru', 'pt': 'ptbr' }, maps: [], firstTags: {}, postTags: [], collectTags: {}, excludeTags: {}, tooltipTags: {}, tooltipBare: {}, attributes: { id: { req: false, valid: /^[a-z0-9_-]+$/i }, title: { req: false, valid: /[\S ]+/ }, 'class': { req: false, valid: /\S+/ } }, IsLinkAllowed: function(link) { var matches = link.match('[a-z]+:\/\/([a-z0-9\.\-]+)'); if(!matches) return true; var domain = matches[1]; var allowed = false; for(var i in Markup.whitelistedWebsites) { var r = Markup.whitelistedWebsites[i]; if(domain.search(r) == 0) allowed = true; } return allowed; }, tags: { '': { empty: true, noHelp: true, allowInReplies: true, toHtml: function(attr, extra) { extra = extra || $.noop; if(attr._text == ' ' && !extra.noNbsp) attr._text = ' '; attr._text = attr._text.replace(/\\\[/g, '['); if(extra && extra.noLink) return attr._text; else if(extra && extra.needsRaw) return attr._rawText; else { var link = []; var text = Markup._preText(attr._rawText.replace(/(https?:\/\/|www\.)([\/_a-z0-9\%\?#@\-\+~&=;:']|\.[a-z0-9\-])+/gi, function(match) { matchUrl = Markup._preText(match.replace(/^www/, 'http://www')); match = Markup._preText(match); var i = link.length; link.push([matchUrl, match]); return '$L' + i; })); text = text.replace(/\$L([\d+]) /gi, '$L$1 '); for(var i in link) { text = text.replace('$L' + i, function(match) { if(Markup.allow < Markup.CLASS_USER && !Markup.IsLinkAllowed(link[i][0])) return $WH.sprintf('[$1]', LANG.linkremoved); var url = ''; return url; }); } return text; } }, toText: function(attr) { return attr._text; } }, achievement: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, diff: { req: false, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; var rel = []; if(attr.diff) rel.push('diff=' + attr.diff); if(g_achievements[id] && g_achievements[id][nameCol]) { var ach = g_achievements[id]; return ' ' + Markup._safeHtml(ach[nameCol]) + ''; } return '(' + LANG.types[10][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_achievements[id] && g_achievements[id][nameCol]) return Markup._safeHtml(g_achievements[id][nameCol]); return LANG.types[10][0] + ' #' + id; } }, achievementpoints: { empty: true, attr: { unnamed: {req: true, valid: /^[0-9]+$/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = '' + attr.unnamed + ''; return str; } }, anchor: { empty: true, ltrim: true, rtrim: true, attr: { unnamed: { req: false, valid: /\S+/ } }, validate: function(attr) { if(!attr.unnamed && !attr.id) return false; return true; }, toHtml: function(attr) { if(!attr.unnamed && attr.id) { attr.unnamed = attr.id; attr.id = null; } return ''; } }, acronym: { empty: false, attr: { unnamed: { req: false } }, toHtml: function(attr) { return ['', '']; } }, b: { empty: false, allowInReplies: true, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/<(b|big|strong)\b[\s\S]*?>([\s\S]*?)<\/\1>/gi, '[b]$2[/b]'); } }, blip: { empty: true, attr: { unnamed: { req: true, valid: /\S+/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var url = 'http://blip.tv/play/' + attr.unnamed; var width = 600; var height = 368; var html = ''; // object tag causing issues in chrome? /*html += ''; html += ''; html += ''; html += '';*/ html += ''; //html += ''; return html; } }, br: { empty: true, toHtml: function(attr) { return '
'; }, fromHtml: function(str) { return str.replace(//gi, "\n"); } }, 'class': { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/i }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; if(attr.unnamed >= 1 && attr.unnamed <= 11) return true; return false; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_classes[id] && g_classes[id][nameCol]) { var cls = g_classes[id]; return ' ' + Markup._safeHtml(cls[nameCol]) + ''; } return '(' + LANG.types[13][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_classes[id] && g_classes[id][nameCol]) return Markup._safeHtml(g_classes[id][nameCol]); return LANG.types[13][0] + ' #' + id; } }, code: { block: true, empty: false, rtrim: true, itrim: true, helpText: true, allowedChildren: { '': 1 }, toHtml: function(attr) { var open = '
';
                return [open, '
']; } }, color: { empty: false, attr: { unnamed: { req: true, valid: /^.*/i } }, allowedClass: MARKUP_CLASS_STAFF, /* Syntax: name: class */ extraColors: {deathknight: 'c6', dk: 'c6', druid: 'c11', hunter: 'c3', mage: 'c8', paladin: 'c2', priest: 'c5', rogue: 'c4', shaman: 'c7', warlock: 'c9', warrior: 'c1', poor: 'q0', common: 'q1', uncommon: 'q2', rare: 'q3', epic: 'q4', legendary: 'q5', artifact: 'q6', heirloom: 'q7'}, toHtml: function(attr) { var valid = /^(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow|c\d+|r\d+|q\d*?|#[a-f0-9]{6})$/i; var str = '']; } }, currency: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, amount: { req: false, valid: /^[0-9\:]+$/ }, icon: { req: false, valid: /^false$/i }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_gatheredcurrencies[id] && g_gatheredcurrencies[id][nameCol]) { var curr = g_gatheredcurrencies[id]; if(attr.amount) return ' ' + attr.amount.split(':').join(' - ') + ''; else return ' ' + Markup._safeHtml(curr[nameCol]) + ''; } return '(' + LANG.types[17][0] + ' #' + id + ')' + (attr.amount > 0 ? ' x' + attr.amount : ''); }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_gatheredcurrencies[id] && g_gatheredcurrencies[id][nameCol]) return Markup._safeHtml(g_gatheredcurrencies[id][nameCol]); return LANG.types[17][0] + ' #' + id; } }, db: { empty: true, attr: { unnamed: { req: true, valid: /^(live|ptr|beta|mop)$/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { if(attr.unnamed == 'live') Markup.defaultSource = Markup.SOURCE_LIVE; else if(attr.unnamed == 'ptr') Markup.defaultSource = Markup.SOURCE_PTR; else if(attr.unnamed == 'beta' || attr.unnamed == 'mop') Markup.defaultSource = Markup.SOURCE_BETA; return ''; }, toText: function(attr) { if(attr.unnamed == 'live') Markup.defaultSource = Markup.SOURCE_LIVE; else if(attr.unnamed == 'ptr') Markup.defaultSource = Markup.SOURCE_PTR; else if(attr.unnamed == 'beta' || attr.unnamed == 'mop') Markup.defaultSource = Markup.SOURCE_BETA; return ''; } }, del: { empty: false, attr: { copy: { req: false, valid: /^true$/ } }, toHtml: function(attr) { var str = '']; } }, div: { empty: false, block: true, ltrim: true, rtrim: true, itrim: true, attr: { clear: { req: false, valid: /^(left|right|both)$/i }, unnamed: { req: false, valid: /^hidden$/i }, 'float': { req: false, valid: /^(left|right)$/i }, align: { req: false, valid: /^(left|right|center)$/i }, margin: { req: false, valid: /^\d+(px|em|\%)$/ }, width: { req: false, valid: /^[0-9]+(px|em|\%)$/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = ' 0) str += ' style="' + styles.join(';') + '"'; if(classes.length > 0) str += ' class="' + classes.join(' ') + '"'; str += '>'; return [str, '']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) { var align = m[i][1].match(/float:\s*(left|right)"/i), width = m[i][1].match(/width[:="]+\s*([0-9]+)/i); str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n" + Array(depth + 1).join("\t") + '[div' + (align ? ' float=' + align[1] : '') + (width ? ' width=' + width[1] : '') + ']' + Markup.tags.div.fromHtml(m[i][0], depth + 1) + '[/div]'); } } return str; } }, event: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^-?[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_holidays[id] && g_holidays[id][nameCol]) { var evt = g_holidays[id]; return '' + Markup._safeHtml(evt[nameCol]) + ''; } return '(' + LANG.types[12][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_holidays[id] && g_holidays[id][nameCol]) return Markup._safeHtml(g_holidays[id][nameCol]); return LANG.types[12][0] + ' #' + id; } }, faction: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_factions[id] && g_factions[id][nameCol]) { var fac = g_factions[id]; return '' + Markup._safeHtml(fac[nameCol]) + ''; } return '(' + LANG.types[8][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_factions[id] && g_factions[id][nameCol]) return Markup._safeHtml(g_factions[id][nameCol]); return LANG.types[8][0] + ' #' + id; } }, feedback: { empty: true, allowedClass: MARKUP_CLASS_STAFF, attr: { mailto: { req: false, valid: /^true$/i } }, toHtml: function(attr) { return 'feedback@wowhead.com'; } }, forumrules: { empty: true, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { return 'forum rules'; } }, hr: { empty: true, trim: true, allowedModes: { article: 1, quickfacts: 1, comment: 1 }, toHtml: function(attr) { return '
'; }, fromHtml: function(str) { return str.replace(//gi, '[hr]'); } }, h2: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedClass: MARKUP_CLASS_STAFF, attr: { unnamed: { req: false, valid: /^first$/i }, clear: { req: false, valid: /^(true|both|left|right)$/i }, toc: { req: false, valid: /^false$/i } }, toHtml: function(attr) { if(!attr.id) attr.id = g_urlize(attr._textContents); str = ' 0) str += ' class="' + classes.join(' ') + '"'; if(attr.clear) { if(attr.clear == 'true' || attr.clear == 'both') str += ' style="clear: both"'; else str += ' style="clear: ' + attr.clear + '"'; } return [str + '>', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/h2>/gi, "\n[h2]$1[/h2]"); } }, h3: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, attr: { unnamed: { req: false, valid: /^first$/i }, toc: { req: false, valid: /^false$/i } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { if(!attr.id) attr.id = g_urlize(attr._textContents); var str = ' 0) str += ' class="' + classes.join(' ') + '"'; return [str + '>', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/h3>/gi, "\n[h3]$1[/h3]"); } }, html: { empty: false, allowedClass: MARKUP_CLASS_ADMIN, allowedChildren: { '': 1 }, rawText: true, taglessSkip: true, toHtml: function(attr) { return [attr._contents]; } }, i: { empty: false, allowInReplies: true, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/<(i|em)\b[\s\S]*?>([\s\S]*?)<\/\1>/gi, '[i]$1[/i]'); } }, icon: { empty: false, itrim: true, attr: { align: { req: false, valid: /^right$/i }, 'float': { req: false, valid: /^(left|right)$/i }, name: { req: false, valid: /\S+/ }, size: { req: false, valid: /^(tiny|small|medium|large)$/ }, unnamed: { req: false, valid: /^class$/i }, url: { req: false, valid: /\S+/ }, preset: { req: false, valid: /\S+/ } }, allowedClass: MARKUP_CLASS_STAFF, presets: { boss: g_staticUrl + '/images/icons/boss.gif', heroic: g_staticUrl + '/images/icons/heroic.gif' }, validate: function(attr) { if(!attr.name && !attr.url && !attr.preset) return false; if(attr.preset && !Markup.tags.icon.presets[attr.preset]) return false; return true; }, toHtml: function(attr) { var size = (attr.size ? attr.size : "tiny"); if(!attr.name) attr.name = ''; if(size == "tiny") { var str = ''; } else str += attr.name + '">'; return [str, '
']; } else { var str = '' : '">' ); var sizes = {'small': 0, 'medium': 1, 'large': 2}; var url = null; if(attr.url && Markup._isUrlSafe(attr.url)) url = attr.url; else if(attr._textContents && Markup._isUrlSafe(attr._textContents)) url = attr._textContents; icon = Icon.create(attr.name.toLowerCase(), sizes[size], null, url); str += icon.innerHTML + ''; return [str]; } } }, iconlist: { empty: false, block: true, ltrim: true, rtrim: true, attr: { domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, taglessSkip: true, allowedClass: MARKUP_CLASS_STAFF, allowedChildren: { b: 1, achievement: 1, currency: 1, faction: 1, holiday: 1, item: 1, itemset: 1, npc: 1, object: 1, pet: 1, quest: 1, spell: 1, title: 1, zone: 1 }, toHtml: function(attr) { var domain = Markup._getDatabaseDomainInfo(attr)[2]; var str = '', m; for(var i = 0; i < attr._nodes.length; ++i) { var node = $WH.dO(attr._nodes[i]); node.attr.domain = domain; var html = Markup.tags[node.name].toHtml(node.attr), type = node.name, href = '', icon = ''; if(typeof html != 'string') // Bold open/close tags html = html[0] + node.attr._contents + html[1]; else if(typeof html == 'string' && (m = html.match(/href="(.+?)".+?url\(\/images\/wow\/icons\/tiny\/(.+?)\.gif\)/))) { node.attr.icon = 'false'; html = Markup.tags[node.name].toHtml(node.attr); href = m[1]; icon = m[2]; } if(html) str += '' + (icon ? Markup.toHtml('[icon name=' + icon + ' size=small url=' + href + ']', { skipReset: true }) : '
  •  
') + '' + html + ''; } if(str) str = '
' + str + '
'; return [str]; } }, img: { empty: true, attr: { src: { req: false, valid: /\S+/ }, icon: { req: false, valid: /\S+/ }, id: { req: false, valid: /^[0-9]+$/ }, blog: { req: false, valid: /^[0-9]+$/ }, size: { req: false, valid: /^(thumb|resized|normal|large|medium|small|tiny)$/i }, width: { req: false, valid: /^[0-9]+$/ }, height: { req: false, valid: /^[0-9]+$/ }, 'float': { req: false, valid: /^(left|right|center)$/i }, border: { req: false, valid: /^[0-9]+$/ }, margin: { req: false, valid: /^[0-9]+$/ } }, blogSize: /^(thumb|normal)$/i, idSize: /^(thumb|resized|normal)$/i, iconSize: /^(large|medium|small|tiny)$/i, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if(attr.src) return true; else if(attr.id) return (attr.size ? Markup.tags.img.idSize.test(attr.size) : true); else if(attr.icon) return (attr.size ? Markup.tags.img.iconSize.test(attr.size) : true); else if(attr.blog) return (attr.size ? Markup.tags.img.blogSize.test(attr.size) : true); return false; }, toHtml: function(attr) { var str = '' + str; post = ''; var screenshot = { url: url, caption: img.alt, width: img.width, height: img.height, noMarkup: true }; g_screenshots[Markup.uid].push(screenshot); } else str += ' src="' + g_staticUrl + '/uploads/blog/images/' + attr.blog + (img.type == 3 ? '.png' : '.jpg') + '" alt="' + Markup._safeHtml(img.alt) + '" width="' + img.width + '" height="' + img.height + '"'; } else return ('Image #' + attr.blog); } if(attr.width) str += ' width="' + attr.width + '"'; if(attr.height) str += ' height="' + attr.height + '"'; if(attr['float']) { if(attr['float'] == 'center') { str = '
' + str + ' style="margin: 10px auto"'; post = '
'; } else { str += ' style="float: ' + attr['float'] + ';'; if(!attr.margin) attr.margin = 10; if(attr['float'] == 'left') str += ' margin: 0 ' + attr.margin + 'px ' + attr.margin + 'px 0"'; else str += ' margin: 0 0 ' + attr.margin + 'px ' + attr.margin + 'px"'; } } if(attr.border != 0) str += ' class="border"'; if(attr.title) str += ' alt="' + attr.title + '"'; else str += ' alt=""'; str += ' />' + post; return str; }, fromHtml: function(str) { var m; if(m = str.match(//gi)) { for(var i = 0; i < m.length; ++i) { var source = m[i].match(/src="([\s\S]+?)"/i), width = m[i].match(/width[:="]+\s*([0-9]+)/i), height = m[i].match(/height[:="]+\s*([0-9]+)/i), border = m[i].match(/border[:="]+\s*([0-9]+)/i); str = str.replace(m[i], '[img src=' + source[1] + (width ? ' width=' + width[1] : '') + (height ? ' height=' + height[1] : '') + ' border=' + (border ? border[1] : 0) + ']'); } } return str; } }, ins: { empty: false, toHtml: function(attr) { return ['', '']; } }, item: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/i }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_items[id] && g_items[id][nameCol]) { var item = g_items[id]; var str = ' '; str += Markup._safeHtml(item[nameCol]) + ''; return str; } return '(' + LANG.types[3][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_items[id] && g_items[id][nameCol]) return Markup._safeHtml(g_items[id][nameCol]); return LANG.types[3][0] + ' #' + id; } }, itemset: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^-?[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_itemsets[id] && g_itemsets[id][nameCol]) { var set = g_itemsets[id]; return '' + Markup._safeHtml(set[nameCol]) + ''; } return '(' + LANG.types[4][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_itemsets[id] && g_itemsets[id][nameCol]) return Markup._safeHtml(g_itemsets[id][nameCol]); return LANG.types[4][0] + ' #' + id; } }, li: { empty: false, itrim: true, allowedParents: { ul: 1, ol: 1 }, helpText: function() { var str = ''; str += '[ul]'; for(var i = 0; i < 3; ++i) str += '\n[li]' + LANG.markup_li + '[/li]'; str += '\n[/ul]\n\n'; str += '[ol]'; for(var i = 0; i < 3; ++i) str += '\n[li]' + LANG.markup_li + '[/li]'; str += '\n[/ol]\n'; return str.toLowerCase(); }, toHtml: function(attr) { return ['
', '
']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n\t" + Array(depth + 1).join("\t") + '[li]' + Markup.tags.li.fromHtml(m[i][0], depth + 1) + '[/li]'); } return str; } }, lightbox: { empty: false, allowedClass: MARKUP_CLASS_STAFF, attr: { unnamed: { req: true, valid: /^(map|model|screenshot)$/ }, zone: { req: false, valid: /^-?[0-9]+[a-z]?$/i }, floor: { req: false, valid: /^[0-9]+$/ }, pins: { req: false, valid: /^[0-9]+$/ } }, validate: function(attr) { switch(attr.unnamed) { case 'map': if(attr.zone) return true; break; case 'model': break; case 'screenshot': break; } return false; }, toHtml: function(attr) { var url = ''; var onclick = ''; switch(attr.unnamed) { case 'map': url = '?maps=' + attr.zone; if(attr.floor) url += '.' + attr.floor; if(attr.pins) url += ':' + attr.pins; var link = url.substr(6); onclick = 'if(!g_isLeftClick(event)) return; MapViewer.show({ link: \'' + link + '\' }); return false;'; break; } if(url && onclick) return ['', '']; return ''; } }, map: { empty: false, attr: { zone: { req: true, valid: /^-?[0-9a-z\-_]+$/i }, source: { req: false, valid: /\S+/ } }, allowedClass: MARKUP_CLASS_STAFF, allowedChildren: { pin: 1 }, toHtml: function(attr) { var coords = attr._contents; attr.id = 'dsgdfngjkfdg' + (Markup.maps.length); var str = '
'; Markup.maps.push([attr.id, attr.zone, coords]); return [ str ]; } }, n5: { empty: true, attr: { unnamed: { req: true, valid: /^[0-9\.]+$/ } }, toHtml: function(attr) { return GetN5(attr.unnamed); } }, pin: { empty: false, attr: { url: { req: false, valid: /\S+/ }, type: { req: false, valid: /^[0-9]+$/ }, x: { req: true, valid: /^[0-9]{1,2}(\.[0-9])?$/ }, y: { req: true, valid: /^[0-9]{1,2}(\.[0-9])?$/ }, path: { req: false, valid: /^([0-9]{1,2}(\.[0-9])?[,:]?)+$/ } }, taglessSkip: true, allowedClass: MARKUP_CLASS_STAFF, allowedParents: { map: 1 }, toHtml: function(attr) { if(attr.url && !Markup._isUrlSafe(attr.url)) attr.url = ''; var label = attr._contents; if(attr.url && attr.url.indexOf('npc=') != -1) label = '' + label + '
Click to view this NPC'; var lines = null; if(attr.path) { var coords = attr.path.split(':'), lines = []; for(var i = 0, len = coords.length; i < len; ++i) { var parts = coords[i].split(','); if(parts.length == 2) lines.push([parseFloat(parts[0] || 0), parseFloat(parts[1] || 0)]); } } return [ [parseFloat(attr.x || 0), parseFloat(attr.y || 0), { label: label, url: attr.url, type: attr.type, lines: lines }] ]; /*var str = '[' + parseFloat(attr.x || 0) + ',' + parseFloat(attr.y || 0) + ',{label: \'' + Markup._safeJsString(label) + '\''; if(attr.url) str += ', url: \'' + Markup._safeJsString(Markup._fixUrl(attr.url)) + '\''; if(attr.type) str += ', type: \'' + Markup._safeJsString(attr.type) + '\''; str += '}]'; return [ [str] ];*/ } }, markupdoc: { empty: true, attr: { tag: { req: false, valid: /[a-z0-9]+/i }, help: { req: false, valid: /^(admin|staff|premium|user|pending)$/ } }, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if(attr.tag && !Markup.tags[attr.tag]) return false; return true; }, toHtml: function(attr) { var str = '', helpClass = (attr.help ? Markup['CLASS_' + attr.help.toUpperCase()] : false); if(helpClass) str += LANG.markup_helpdoc + '
'; if(attr.tag) str = Markup._generateTagDocs(attr.tag, helpClass); else { for(var tag in Markup.tags) { if(!helpClass && str != '') str += '
'; str += Markup._generateTagDocs(tag, helpClass); } } return str + (helpClass ? '
' + LANG.markup_help1 + '' + LANG.markup_help2 + '
' : ''); } }, menu: { empty: true, trim: true, ltrim: true, rtrim: true, attr: { tab: { req: true, valid: /^[0-9]+$/ }, path: { req: true, valid: /\S+/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var path = attr.path.split(','); PageTemplate.set({activeTab: attr.tab, breadcrumb: path}); } }, minibox: { empty: false, rtrim: true, itrim: true, attr: { 'float': { req: false, valid: /^(left|right)$/i } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = ''; return [str, '']; } }, model: { empty: false, attr: { item: { req: false, valid: /^[0-9]+$/ }, object: { req: false, valid: /^[0-9]+$/ }, npc: { req: false, valid: /^[0-9]+$/ }, itemset: { req: false, valid: /^[0-9,]+$/ }, slot: { req: false, valid: /^[0-9]+$/ }, humanoid: { req: false, valid: /^1$/ }, 'float': { req: false, valid: /^(left|right)$/i }, img: { req: false, valid: /\S+/ }, link: { req: false, valid: /\S+/ }, label: { req: false, valid: /[\S ]+/ } }, allowedClass: MARKUP_CLASS_STAFF, skipSlots: { 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 16: 1, 19: 1, 20: 1 }, toHtml: function(attr) { var str = ''; if(attr.npc) { str = '' + Markup._safeHtml(attr._contents) + '' + Markup._safeHtml(attr._contents) + '' + Markup._safeHtml(attr._contents) + ''; } else return ['[model]', '[/model]']; return [str, '']; } }, money: { empty: true, attr: { unnamed: { req: false, valid: /^[0-9]+$/ }, side: { req: false, valid: /^(alliance|horde|both)$/i }, items: { req: false, valid: /^[0-9,]+$/ }, currency: { req: false, valid: /^[0-9,]+$/ }, achievement: { req: false, valid: /\S+/ }, arena: { req: false, valid: /^[0-9]+$/ }, honor: { req: false, valid: /^[0-9]+$/ }, }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var items = [], currency = []; if(attr.items) { var split = attr.items.split(','); if(split.length >= 2) for(var i = 0; i < split.length-1; i += 2) items.push([split[i], split[i+1]]); } if(attr.currency) { var split = attr.currency.split(','); if(split.length >= 2) for(var i = 0; i < split.length-1; i += 2) currency.push([split[i], split[i+1]]); } // Backwards compatability if(attr.honor) currency.push([104, attr.honor]); if(attr.arena) currency.push([103, attr.arena]); return g_getMoneyHtml(attr.unnamed, attr.side, items, currency, attr.achievement); } }, npc: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_npcs[id] && g_npcs[id][nameCol]) { var npc = g_npcs[id]; return '' + Markup._safeHtml(npc[nameCol]) + ''; } return '(' + LANG.types[1][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_npcs[id] && g_npcs[id][nameCol]) return Markup._safeHtml(g_npcs[id][nameCol]); return LANG.types[1][0] + ' #' + id; } }, object: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_objects[id] && g_objects[id][nameCol]) { var obj = g_objects[id]; return '' + Markup._safeHtml(obj[nameCol]) + ''; } return '(' + LANG.types[2][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_objects[id] && g_objects[id][nameCol]) return Markup._safeHtml(g_objects[id][nameCol]); return LANG.types[2][0] + ' #' + id; } }, ol: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedChildren: { li: 1 }, toHtml: function(attr) { var open = ' 0) open += ' class="' + classes.join(' ') + '"'; open += Markup._addGlobalAttributes(attr) + '>'; return [open, '']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) { str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n" + Array(depth + 1).join("\t") + '[ol]' + Markup.tags.ol.fromHtml(m[i][0], depth + 1) + "\n" + Array(depth + 1).join("\t") + '[/ol]'); } } return str; } }, p: { empty: false, ltrim: true, rtrim: true, itrim: true, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { return ['

','

']; }, fromHtml: function(str) { var m; if(m = str.match(/[\s\S]*?<\/p>/gi)) { for(var i = 0; i < m.length; ++i) { var align = m[i].match(/^([\s\S]*?)<\/p>/i); str = str.replace(m[i], '[pad][div' + (align ? ' align=' + align[1] : '') + ']' + (inside ? inside[1] : '') + '[/div][pad]'); } } return str; } }, pad: { empty: true, block: true, trim: true, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = '
'; return str; } }, pet: { empty: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_pet_families && g_pet_families[id] && g_pets && g_pets[id]) { var str = ''; str += '' + Markup._safeHtml(g_pet_families[id]) + ''; return str; } return '(' + LANG.types[9][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; if(g_pet_families && g_pet_families[id]) return Markup._safeHtml(g_pet_families[id]); return LANG.types[9][0] + ' #' + id; } }, pre: { empty: false, block: true, rtrim: true, toHtml: function(attr) { var open = '
';
                return [open, '
']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/pre>/gi, '[pre]$1[/pre]'); } }, quest: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_quests[id] && g_quests[id][nameCol]) { var quest = g_quests[id]; return ' ' + Markup._safeHtml(quest[nameCol]) + ''; } return '(' + LANG.types[5][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_quests[id] && g_quests[id][nameCol]) return Markup._safeHtml(g_quests[id][nameCol]); return LANG.types[5][0] + ' #' + id; } }, quote: { block: true, empty: false, rtrim: true, ltrim: true, itrim: true, attr: { unnamed: { req: false, valid: /[\S ]+/ }, url: { req: false, valid: /\S+/ }, blizzard: { req: false, valid: /^true$/ }, pname: { req: false }, wowhead: { req: false, valid: /^true$/ }, display: { req: false, valid: /^block$/ }, align: { req: false, valid: /^(left|right|center)$/i }, collapse: { req: false, valid: /^true$/ } }, allowedModes: { article: 1, quickfacts: 1, comment: 1 }, validate: function(attr) { if(attr.blizzard || attr.wowhead || attr.collapse || attr.url) { if(Markup.allow < Markup.CLASS_STAFF) return false; } return true; }, toHtml: function(attr) { var str = '= 0) pname = 'Blue Tracker'; // override with attr.pname if it exists if(typeof(attr.pname) != 'undefined') pname = attr.pname; var username = attr.unnamed.trim(); if(username.length <= 0) return ['','']; str = str.replace('class="quote', 'class="quote-blizz'); str += (attr.collapse ? ' collapse' : '') + '">
' var matches = url.match(/https?:\/\/(us|eu)\.battle\.net\/wow\/en\/blog\/([0-9]+)/i) || url.match(/https?:\/\/(us|eu)\.battle\.net\/wow\/en\/forum\/topic\/([0-9]+)/i); if(matches) { str += 'Originally posted by Blizzard (Official Post' var topicId = matches[2]; str += ' | Blue Tracker)' + '

' + username + '

'; } else str += ( attr.url && Markup._isUrlSafe(attr.url) ? 'Originally posted by Blizzard ' + '(' + pname + ')' + '

' :'

' + username + '

' ); return [str, '
']; } return ['','']; } else if(attr.wowhead /*Markup.inBlog*/) { str = str.replace('class="quote', 'class="quote-wh'); str += (attr.collapse ? ' collapse' : '') + '">'; str += '
'; return [str, '
']; } else { str += '">'; if(attr.unnamed) { var username = attr.unnamed.trim(); if(username.length > 0) { str += ''; if(attr.url && Markup._isUrlSafe(attr.url)) str += '' + username + ''; else if(g_isUsernameValid(username)) str += '' + username + ''; else str += username; str += ' '+ LANG.markup_said + '
'; } } return [str, '']; } } }, race: { empty: true, allowInReplies: true, valid: { 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true, 22: true, 24: true, 25: true }, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, gender: { req: false, valid: /^(0|1)$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; if(Markup.tags.race.valid[attr.unnamed]) return true; return false; }, toHtml: function(attr) { var id = attr.unnamed; var gender = attr.gender | 0; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_races[id] && g_races[id][nameCol]) { var race = g_races[id]; return ' ' + Markup._safeHtml(race[nameCol]) + ''; } return '(' + LANG.types[14][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_races[id] && g_races[id][nameCol]) return Markup._safeHtml(g_races[id][nameCol]); return LANG.types[14][0] + ' #' + id; } }, reveal: { empty: false, rtrim: true, ltrim: true, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { if(!Markup.inBlog || Markup.inBlog > 1) return ['', '']; return [' (read more)']; Markup.reveals++; } }, s: { empty: false, allowInReplies: true, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/del>/gi, '[s]$1[/s]'); } }, screenshot: { empty: false, attr: { id: { req: false, valid: /^[0-9]+$/ }, url: { req: false, valid: /\S+/ }, thumb: { req: false, valid: /\S+/ }, size: { req: false, valid: /^(thumb|resized|normal)$/i }, width: { req: false, valid: /^[0-9]+$/ }, height: { req: false, valid: /^[0-9]+$/ }, 'float': { req: false, valid: /^(left|right)$/i }, border: { req: false, valid: /^[0-9]+$/ } }, taglessSkip: true, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if(attr.url && !attr.thumb) return false; else if(!attr.id && !attr.url) return false; return true; }, toHtml: function(attr) { var url = ''; var thumb = ''; if(attr.id) { url = g_staticUrl + '/uploads/screenshots/normal/' + attr.id + '.jpg'; var thumbId = attr.id; if(attr.thumb && attr.thumb.match(/^[0-9]+$/)) { thumbId = attr.thumb; attr.thumb = null; } thumb = g_staticUrl + '/uploads/screenshots/' + (attr.size ? attr.size : 'thumb') + '/' + thumbId + '.jpg'; } else if(attr.url) url = attr.url; if(attr.thumb) thumb = attr.thumb; var caption = attr._contents.replace(/\n/g, '
'); if(!g_screenshots[Markup.uid]) g_screenshots[Markup.uid] = []; var str = ''; str += '']; } }, script: { ltrim: true, rtrim: true, empty: false, attr: { src: { req: false, valid: /^\S+$/ } }, allowedClass: MARKUP_CLASS_ADMIN, allowedChildren: { '': 1 }, rawText: true, taglessSkip: true, toHtml: function(attr) { if(attr.src) { $.getScript(attr.src, function() { $.globalEval(attr._contents); }); } else $.globalEval(attr._contents); return ['']; } }, section: { empty: false, ltrim: true, rtrim: true, trim: true, allowedClass: MARKUP_CLASS_STAFF, attr: { }, toHtml: function(attr) { return ['
', '
']; } }, skill: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_skills[id] && g_skills[id][nameCol]) { var skill = g_skills[id]; return ' ' + Markup._safeHtml(skill[nameCol]) + ''; } return '(' + LANG.types[15][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_skills[id] && g_skills[id][nameCol]) return Markup._safeHtml(g_skills[id][nameCol]); return LANG.types[15][0] + ' #' + id; } }, sig: { empty: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ } }, allowedClass: MARKUP_CLASS_PREMIUM, allowedModes: { signature: 1 }, toHtml: function(attr) { return; return ''; } }, small: { empty: false, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/small>/gi, '[small]$1[/small]'); } }, span: { empty: false, attr: { unnamed: { req: false, valid: /^(hidden|invisible)$/ }, tooltip: { req: false, valid: /\S+/ }, tooltip2: { req: false, valid: /\S+/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = ' 0) str += ' style="' + styles.join(';') + '"'; if(attr.tooltip && Markup.tooltipTags[attr.tooltip]) str += ' onmouseover="$WH.Tooltip.showAtCursor(event, Markup.tooltipTags[\'' + attr.tooltip + '\'], 0, 0, ' + (Markup.tooltipBare[attr.tooltip] ? 'null' : "'q'") + ', ' + (attr.tooltip2 && Markup.tooltipTags[attr.tooltip2] ? "Markup.tooltipTags['" + attr.tooltip2 + "']" : 'null') + ')" onmousemove="$WH.Tooltip.cursorUpdate(event)" onmouseout="$WH.Tooltip.hide()"'; str += '>'; return [str, '']; } }, spell: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, diff: { req: false, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, buff: { req: false, valid: /^true$/ }, }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; var rel = []; if(attr.buff) rel.push('buff'); if(attr.diff) rel.push('diff=' + attr.diff); if(g_spells[id] && g_spells[id][nameCol]) { var spell = g_spells[id]; return ' ' + Markup._safeHtml(spell[nameCol]) + ''; } return '(' + LANG.types[6][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_spells[id] && g_spells[id][nameCol]) return Markup._safeHtml(g_spells[id][nameCol]); return LANG.types[6][0] + ' #' + id; } }, spoiler: { block: true, empty: false, rtrim: true, ltrim: true, itrim: true, toHtml: function(attr) { return ['
' + LANG.markup_spoil + '', '']; } }, statistic: { empty: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, icon: { req: false, valid: /^false$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_achievements[id] && g_achievements[id][nameCol]) { var ach = g_achievements[id]; return ' ' + Markup._safeHtml(ach[nameCol]) + ''; } return '(' + LANG.types[10][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_achievements[id] && g_achievements[id][nameCol]) return Markup._safeHtml(g_achievements[id][nameCol]); return LANG.types[10][0] + ' #' + id; } }, style: { ltrim: true, rtrim: true, empty: false, allowedClass: MARKUP_CLASS_ADMIN, allowedChildren: { '': 1 }, rawText: true, taglessSkip: true, toHtml: function(attr) { g_addCss(attr._contents); return ['']; } }, sub: { empty: false, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/sub>/gi, '[sub]$1[/sub]'); } }, sup: { empty: false, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/sup>/gi, '[sup]$1[/sup]'); } }, tabs: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedClass: MARKUP_CLASS_STAFF, allowedChildren: { tab: 1 }, attr: { name: { req: true, valid: /\S+/ }, width: { req: false, valid: /^[0-9]+(px|em|\%)$/ } }, toHtml: function(attr) { attr.id = g_urlize(attr.name); var x = Markup.preview; var str = '
'; str += ''; str += tab.content; str += '
'; str += ''; } str += ''; str += ''; setTimeout(Markup.createTabs.bind(null, attr, tabs, (x ? 'preview' : '')), 100); return [str]; } }, tab: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedClass: MARKUP_CLASS_STAFF, allowedParents: { tabs: 1 }, attr: { name: { req:true, valid: /[\S ]+/ }, icon: { req:false, valid: /\S+/ } }, toHtml: function(attr) { attr.id = g_urlize(attr.name); attr.name = $WH.str_replace(attr.name, "_", ' '); if(typeof(attr['class']) != 'undefined') attr['class'] = $WH.str_replace(attr['class'], "_", ' '); return [{content: attr._contents, id: attr.id, name: attr.name, icon: attr.icon, 'class': attr['class']}]; } }, table: { //block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedChildren: { tr: 1 }, attr: { border: { req: false, valid: /^[0-9]+$/ }, cellspacing: { req: false, valid: /^[0-9]+$/ }, cellpadding: { req: false, valid: /^[0-9]+$/ }, width: { req: false, valid: /^[0-9]+(px|em|\%)$/ } }, toHtml: function(attr) { var str = '']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) { var border = m[i][1].match(/border[:="]+\s*([0-9]+)/i), width = m[i][1].match(/width[:="]+\s*([0-9]+)/i), spacing = m[i][1].match(/cellspacing="([\s\S]+?)"/i), padding = m[i][1].match(/cellpadding="([\s\S]+?)"/i); str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n" + Array(depth + 1).join("\t") + '[table' + (border ? ' border=' + border[1] : '') + (width ? ' width=' + width[1] : '') + (spacing ? ' cellspacing=' + spacing[1] : '') + (padding ? ' cellpadding=' + padding[1] : '') + ']' + Markup.tags.table.fromHtml(m[i][0], depth + 1) + "\n" + Array(depth + 1).join("\t") + '[/table]'); } } return str; } }, tr: { empty: false, itrim: true, allowedChildren: { td: 1 }, allowedParents: { table: 1 }, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) { str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n\t" + Array(depth + 1).join("\t") + '[tr]' + Markup.tags.tr.fromHtml(m[i][0], depth + 1) + "\n" + Array(depth + 1).join("\t") + '[/tr]'); } } return str; } }, td: { empty: false, itrim: true, allowedParents: { tr: 1 }, attr: { unnamed: { req: false, valid: /^header$/ }, align: { req: false, valid: /^(right|left|center|justify)$/i }, valign: { req: false, valid: /^(top|middle|bottom|baseline)$/i }, colspan: { req: false, valid: /^[0-9]+$/ }, rowspan: { req: false, valid: /^[0-9]+$/ }, width: { req: false, valid: /^[0-9]+(px|em|\%)$/ } }, toHtml: function(attr) { var str = '<' + (attr.unnamed ? 'th' : 'td') + Markup._addGlobalAttributes(attr); if(attr.align != undefined) str += ' align="' + attr.align + '"'; if(attr.valign != undefined) str += ' valign="' + attr.valign + '"'; if(attr.colspan != undefined) str += ' colspan="' + attr.colspan + '"'; if(attr.rowspan != undefined) str += ' rowspan="' + attr.rowspan + '"'; if(attr.width != undefined) str += ' style="width: ' + attr.width + '"'; str += '>'; return [str, '']; }, fromHtml: function(str, depth) { depth = depth || 0; var t = ['td', 'th'], m; for(var j = 0; j < t.length; ++j) { if(m = Markup.matchOuterTags(str, '<' + t[j] + '\\b[\\s\\S]*?>', '', 'g')) { for(var i = 0; i < m.length; ++i) { var width = m[i][1].match(/width[:="]+\s*([0-9]+)/i), align = m[i][1].match(/align="([\s\S]+?)"/i), valign = m[i][1].match(/valign="([\s\S]+?)"/i), colspan = m[i][1].match(/colspan="([\s\S]+?)"/i), rowspan = m[i][1].match(/rowspan="([\s\S]+?)"/i); str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n\t\t" + Array(depth + 1).join("\t") + '[td' + (t[j] == 'th' ? '=header' : '') + (width ? ' width=' + width[1] : '') + (align ? ' align=' + align[1] : '') + (valign ? ' valign=' + valign[1] : '') + (colspan ? ' colspan=' + colspan[1] : '') + (rowspan ? ' rowspan=' + rowspan[1] : '') + ']' + Markup.tags.td.fromHtml(m[i][0], depth + 1) + '[/td]'); } } } return str; } }, time: { empty: true, count: 0, attr: { until: { req: false, valid: /^\d+$/ }, since: { req: false, valid: /^\d+$/ }, server: { req: false, valid: /^true$/ } }, validate: function(attr) { if(!attr.until && !attr.since) return false; return true; }, toHtml: function(attr) { var id = Markup.tags.time.count++; var str = '' + Markup.tags.time.getTime(attr) + ''; setInterval(Markup.tags.time.updateTime.bind(null, id, attr), 5000); return str; }, getTime: function(attr) { var now; if(attr.server) now = g_serverTime.getTime() / 1000; else now = (new Date()).getTime() / 1000; var delay = 0; if(attr.until) delay = attr.until - now; else delay = now - attr.since; if(delay > 0) return g_formatTimeElapsed(delay); else return '0 ' + LANG.timeunitspl[6]; }, updateTime: function(id, attr) { var span = $WH.ge('markupTime' + id); if(!span) return; span.firstChild.nodeValue = Markup.tags.time.getTime(attr); } }, toc: { block: true, post: true, trim: true, ltrim: true, rtrim: true, collect: { h2: 1, h3: 1 }, exclude: { tabs: { h2: 1, h3: 1 }, minibox: { h2: 1, h3: 1 } }, allowedClass: MARKUP_CLASS_STAFF, attr: { h3: { req: false, valid: /^false$/ } }, postHtml: function(attr, nodes) { var str = ' 0) str += ' class="' + classes.join(' ') + '"'; str += Markup._addGlobalAttributes(attr) + '>' + LANG.markup_toc + '
    '; var lastNode = ""; var indent = 1; var allowH3 = (attr.h3 != 'false'); var myNodes = []; for(var node in nodes.h2) myNodes.push(nodes.h2[node]); for(var node in nodes.h3) myNodes.push(nodes.h3[node]); myNodes.sort(function(a, b) { return a.offset - b.offset; }); for(var i in myNodes) { node = myNodes[i]; if(node.name == 'h2' && node.attr.toc != 'false') { if(lastNode == 'h3') { str += '
'; indent--; } str += '
  • ' + node.attr._textContents + '
  • '; lastNode = 'h2'; } if(node.name == 'h3' && allowH3 && node.attr.toc != 'false' && (lastNode != '' || nodes.h2.length == 0)) { if(lastNode == 'h2') { str += ''; } return str; } }, toggler: { empty: false, attr: { id: { req: true, valid: /^[a-z0-9_-]+$/i }, unnamed: { req: false, valid: /^hidden$/i } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = ''; return [str, '']; } }, tooltip: { empty: false, attr: { unnamed: { req: false, valid: /\S+/ }, name: { req: false, valid: /\S+/ }, bare: { req: false, valid: /^true$/i }, label: { req: false, valid: /[\S ]+/ } }, taglessSkip: true, allowedClass: MARKUP_CLASS_STAFF, validate: function(attr) { if(!attr.unnamed && !attr.name) return false; return true; }, toHtml: function(attr) { if(attr.unnamed) return ['', '']; else { Markup.tooltipTags[attr.name] = (attr.label ? '
    ' + attr.label + '
    ' : '') + attr._contents; if(attr.bare) Markup.tooltipBare[attr.name] = true; return ['']; } } }, u: { empty: false, allowInReplies: true, toHtml: function(attr) { return ['', '']; }, fromHtml: function(str) { return str.replace(/<(ins|u)\b[\s\S]*?>([\s\S]*?)<\/\1>/gi, '[u]$2[/u]'); } }, ul: { block: true, empty: false, ltrim: true, rtrim: true, itrim: true, allowedChildren: { li: 1 }, toHtml: function(attr) { var open = ' 0) open += ' class="' + classes.join(' ') + '"'; open += Markup._addGlobalAttributes(attr) + '>'; return [open, '']; }, fromHtml: function(str, depth) { depth = depth || 0; var m; if(m = Markup.matchOuterTags(str, '', '', 'g')) { for(var i = 0; i < m.length; ++i) { str = str.replace(m[i][1] + m[i][0] + m[i][2], "\n" + Array(depth + 1).join("\t") + '[ul]' + Markup.tags.ul.fromHtml(m[i][0], depth + 1) + "\n" + Array(depth + 1).join("\t") + '[/ul]'); } } return str; } }, url: { allowedClass: MARKUP_CLASS_USER, allowInReplies: true, empty: false, helpText: '[url=http://www.google.com]' + LANG.markup_url + '[/url]', attr: { unnamed: { req: false, valid: /\S+/ }, rel: { req: false, valid: /(item|quest|spell|achievement|npc|object)=([0-9]+)/ }, onclick: { req: false, valid: /[\S ]+/ }, tooltip: { req: false, valid: /\S+/ }, tooltip2: { req: false, valid: /\S+/ } }, validate: function(attr) { if(attr.onclick && Markup.allow < Markup.CLASS_ADMIN) return false; if(attr.tooltip && Markup.allow < Markup.CLASS_STAFF) return false; var target = ''; if(attr.unnamed && /^(mailto:|irc:)/i.test(attr.unnamed.trim()) && Markup.allow < Markup.CLASS_STAFF) return false; if(attr.unnamed && /^(javascript:)/i.test(attr.unnamed.trim())) return false; return true; }, toHtml: function(attr) { var target; if(attr.unnamed) // in the form [url=blah] { target = attr.unnamed; target = target.replace(/&/, '&'); if(!target.match(/^([^:\\.\/]+):/i) && target.charAt(0) != '/' && target.charAt(0) != '#') target = '/' + target; if(Markup._isUrlSafe(target, true)) { var pre = '']; } else { return ['', '']; } } else // [url]blah[/url] { target = attr._textContents; target = target.replace(/&/, '&'); if(Markup._isUrlSafe(target)) { var pre = '']; } else { return ['', '']; } } }, fromHtml: function(str) { return str.replace(/([\s\S]*?)<\/a>/gi, '[url=$1]$2[\/url]'); } }, video: { empty: true, attr: { id: { req: true, valid: /^[0-9]+$/ }, unnamed: { req: false, valid: /^embed$/i }, 'float': { req: false, valid: /^(left|right)$/i }, // Thumbnail only border: { req: false, valid: /^[0-9]+$/ } // Thumbnail only }, ltrim: true, rtrim: true, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { if(g_videos[attr.id]) { var html = '', video = g_videos[attr.id]; if(attr.unnamed) { if(video.videoType == 1) // YouTube html += Markup.toHtml('[youtube=' + video.videoId + ']', { skipReset: true }); } else { if(!g_videos[Markup.uid]) g_videos[Markup.uid] = []; html += ''; g_videos[Markup.uid].push($WH.dO(video)); } return html; } return 'Video #' + attr.id + ''; } }, visitedpage: { empty: false, attr: { unnamed: { req: true, valid: /^[0-9]+$/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { $.post('/visited-page', { id: attr.unnamed }, function() { AchievementCheck(); }); return ''; } }, wowheadresponse: { block: true, empty: false, rtrim: true, ltrim: true, itrim: true, attr: { unnamed: { req: true, valid: /[\S ]+/ }, roles: { req: true, valid: /[0-9]+/ } }, allowedModes: { article: 1, quickfacts: 1, comment: 1 }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var str = '' + username + ' ' + LANG.markup_said + '
    '; return [str, '']; } }, youtube: { empty: true, attr: { unnamed: { req: true, valid: /\S+/ }, width: { req: false, valid: /^[0-9]+$/ }, height: { req: false, valid: /^[0-9]+$/ }, autoplay: { req: false, valid: /^true$/ } }, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { var url = 'http://www.youtube.com/v/' + attr.unnamed + '&fs=1&rel=0' + (attr.autoplay ? '&autoplay=1' : ''); var width = attr.width ? attr.width : 640; var height = attr.height ? attr.height : 385; var html = ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; return html; }, fromHtml: function(str) { var m; if(m = str.match(/<\/iframe>/gi)) { for(var i = 0; i < m.length; ++i) { var source = m[i].match(/src="[\s\S]*?youtube\.com\/embed\/([\s\S]*?)"/i), width = m[i].match(/width[:="]+\s*([0-9]+)/i), height = m[i].match(/height[:="]+\s*([0-9]+)/i), border = m[i].match(/border[:="]+\s*([0-9]+)/i); str = str.replace(m[i], '[youtube=' + source[1] + (width ? ' width=' + width[1] : '') + (height ? ' height=' + height[1] : '') + ']'); } } return str; } }, center: { empty: false, allowInReplies: false, allowedClass: MARKUP_CLASS_STAFF, toHtml: function(attr) { return ['
    ', '
    ']; }, fromHtml: function(str) { return str.replace(/
    ([\s\S]+?)<\/center>/gi, '[pad][div align=center]$1[/div][pad]'); } }, title: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_titles[id] && g_titles[id][nameCol]) { return '' + Markup._safeHtml(g_titles[id][nameCol]) + ''; } return '(' + LANG.types[11][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_titles[id] && g_titles[id][nameCol]) return Markup._safeHtml(g_titles[id][nameCol]); return LANG.types[11][0] + ' #' + id; } }, zone: { empty: true, allowInReplies: true, attr: { unnamed: { req: true, valid: /^[0-9]+$/ }, domain: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ }, site: { req: false, valid: /^(beta|mop|ptr|www|de|es|fr|ru|pt)$/ } }, validate: function(attr) { if((attr.domain || attr.site) && Markup.dbpage) return false; return true; }, toHtml: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var url = domainInfo[0]; var nameCol = domainInfo[1]; if(g_gatheredzones[id] && g_gatheredzones[id][nameCol]) { return '' + Markup._safeHtml(g_gatheredzones[id][nameCol]) + ''; } return '(' + LANG.types[7][0] + ' #' + id + ')'; }, toText: function(attr) { var id = attr.unnamed; var domainInfo = Markup._getDatabaseDomainInfo(attr); var nameCol = domainInfo[1]; if(g_gatheredzones[id] && g_gatheredzones[id][nameCol]) return Markup._safeHtml(g_gatheredzones[id][nameCol]); return LANG.types[7][0] + ' #' + id; } } }, _addGlobalAttributes: function(attr) { var attribs = ''; if(Markup.allow < Markup.CLASS_STAFF) return attribs; if(attr.id) attribs += ' id="' + attr.id + '"'; if(attr.title) attribs += ' title="' + Markup._safeQuotes(attr.title) + '"'; if(attr['class']) attribs += ' class="' + attr['class'] + '"'; if(attr['data-highlight']) attribs += ' data-highlight="' + attr['data-highlight'] + '"'; return attribs; }, _generateTagDocs: function(tagName, helpClass) { var tag = Markup.tags[tagName]; if(!tag) return ''; if(helpClass) { if((tag.allowedClass && tag.allowedClass > helpClass) || (!tag.helpText && (tag.empty || tag.allowedParents || tag.allowedChildren || !LANG['markup_' + tagName]))) return ''; if(tag.helpText && typeof tag.helpText == 'function') var str = tag.helpText(); else if(tag.helpText && typeof tag.helpText == 'string') var str = tag.helpText; else var str = '[' + tagName + ']' + LANG['markup_' + tagName].toLowerCase() + '[/' + tagName + ']'; return '
    ' + str + '
    ' + Markup.toHtml(str, { skipReset: true }) + ''; } var str = '

    Tag: [' + Markup._safeHtml(tagName) + ']

    '; str += ''; if(tag.attr) { /*str += '';*/ str += ''; } str += ''; str += ''; if(tag.allowedChildren) { str += ''; } if(tag.allowedParents) { str += ''; } if(tag.presets) { str += ''; } if(tag.trim) { str += ''; } if(tag.ltrim) { str += ''; } if(tag.rtrim) { str += ''; } if(tag.itrim) { str += ''; } if(tag.block) { str += ''; } str += '
    Attributes:'; for(var a in tag.attr) { str += ''; str += ''; str += ''; } str += '
    ' + a + '
    Required:' + (tag.attr[a].req ? 'Yes' : 'No') + '
    Valid:' + (tag.attr[a].valid ? Markup._safeHtml(tag.attr[a].valid.toString()) : '--') + '
    Attributes:'; for(var a in tag.attr) { str += '
    '; str += ''; str += '
    '; if(a == 'unnamed') str += 'Self ([' + tagName + '=???])'; else str += a; str += '
    Required:' + (tag.attr[a].req ? 'Yes' : 'No') + '
    Valid:' + (tag.attr[a].valid ? Markup._safeHtml(tag.attr[a].valid.toString()) : '--') + '
    '; } str += '
    Has closing tag:' + (tag.empty ? 'No' : 'Yes') + '
    Required group:'; if(tag.allowedClass == MARKUP_CLASS_ADMIN) str += 'Administrator'; else if(tag.allowedClass == MARKUP_CLASS_STAFF) str += 'Staff'; else if(tag.allowedClass == MARKUP_CLASS_PREMIUM) str += 'Premium'; else if(tag.allowedClass && tag.allowedClass != MARKUP_CLASS_PENDING) str += 'Not pending'; else str += 'None'; str += '
    Allowed children:'; for(var t in tag.allowedChildren) str += Markup._safeHtml(t) + '
    '; str += '
    Allowed parents:'; for(var t in tag.allowedParents) str += Markup._safeHtml(t) + '
    '; str += '
    Preset values:'; for(var p in tag.presets) str += ''; str += '
    ' + p + '' + Markup._safeHtml(tag.presets[p]) + '
    Trim whitespace
    Trim preceding whitespace
    Trim following whitespace
    Trim whitespace around interior content
    Automatically remove top padding if not the first item
    '; return str; }, _init: function() { if(!this.inited) { var ltrimTags = [], rtrimTags = [], trimTags = []; for(var tag in Markup.tags) { if(Markup.tags[tag].block) this.firstTags[tag] = true; if(Markup.tags[tag].exclude) { for(var ex in Markup.tags[tag].exclude) { if(!this.excludeTags[ex]) this.excludeTags[ex] = {}; this.excludeTags[ex][tag] = Markup.tags[tag].exclude[ex]; } } if(Markup.tags[tag].post) this.postTags.push(tag); if(Markup.tags[tag].trim) trimTags.push(tag); if(Markup.tags[tag].ltrim) ltrimTags.push(tag); if(Markup.tags[tag].rtrim) rtrimTags.push(tag); } if(ltrimTags.length > 0) this.ltrimRegex = new RegExp('\\s*\\[(' + ltrimTags.join('|') + ')([^a-z0-9]+.*)?]', 'ig'); if(rtrimTags.length > 0) this.rtrimRegex = new RegExp('\\[\/(' + rtrimTags.join('|') + ')\\]\\s*', 'ig'); if(trimTags.length > 0) this.trimRegex = new RegExp('\\s*\\[(' + trimTags.join('|') + ')([^\\[]*)?\\]\\s*', 'ig'); this.inited = true; $('[data-highlight]') .live('mouseenter', function() { var _ = $(this).attr('data-highlight').split(':'); if(_.length != 2) return; var elem = $('#' + _[0]).get(0), start = parseInt(_[1]), text = $(elem).val(); if(!elem || !start || !text) return; var top = $(elem).val(text.substr(0, start))[0].scrollHeight; $(elem).val(text).animate({ scrollTop: top }, 250); elem.selectionStart = start; elem.selectionEnd = start; }); } }, _safeJsString: function(str) { return str.replace(/'/g, '\''); }, _safeQuotes: function(str) { return str.replace('"', '\"').replace("'", "\'"); }, _safeHtml: function(html) { var allowedEscapes = ['nbsp', 'ndash']; html = html.replace(/&/g, '&'); if(allowedEscapes.length > 0) html = html.replace(new RegExp('&(' + allowedEscapes.join('|') + ');', 'g'), '&$1;'); return html.replace(//g, '>').replace(/"/g, '"'); }, _preText: function(str) { str = Markup._safeHtml(str); str = str.replace(/\n/g, '
    '); return str; }, _getDatabaseDomainInfo: function(attr) { var url = ''; var nameCol = Markup.nameCol; var domain = false; if(attr.domain) domain = attr.domain; else if(attr.site) domain = attr.site; else if(Markup.defaultSource) domain = MarkupSourceMap[Markup.defaultSource]; if(domain) { if(domain == 'beta') domain = 'mop'; url = 'http://' + domain + '.wowhead.com'; nameCol = 'name_' + Markup.domainToLocale[domain]; } else if(location.href.indexOf('wowheadnews.com') != -1) url = 'http://www.wowhead.com'; return [url, nameCol, domain]; }, _isUrlSafe: function(str, extras) { if(!str) return true; if(str == 'javascript:;') return true; var result = str.match(/^([^:\\./]+):/i); if(result && result[1]) { var protocol = result[1]; if(protocol == 'http' || protocol == 'https') return true; if(extras && (protocol == 'mailto' || protocol == 'irc')) return true; if(protocol != 'mailto' && str.indexOf('://') == -1) return true; return false; } return true; }, _fixUrl: function(url) { if(!url) return ''; // Make local URLs absolute var firstChar = url.charAt(0); if(firstChar == '/' || firstChar == '?') { url = url.replace(/^[\/\?]+/, ''); // url = '/' + url; url = '?' + url; } return url; }, _isUrlExternal: function(str) { if(!str) return false; return (str.indexOf('wowhead.com') == -1 && str.match(/^([^:\\./]+):/i)); }, _nodeSearch: function(node, name, depth) { if(!depth) depth = 0; if(depth >= 3) return; if(node.name == name) return true; else if(node.parent) return Markup._nodeSearch(node.parent, name, depth+1); }, _parse: function(str, opts) { Markup.nameCol = 'name_' + Locale.getName(); if(opts && opts.locale) Markup.nameCol = 'name_' + Markup.domainToLocale[opts.locale]; else if($WH.isset('g_beta') && g_beta) Markup.nameCol = 'name_beta'; else if($WH.isset('g_ptr') && g_ptr) Markup.nameCol = 'name_ptr'; else if($WH.isset('g_old') && g_old) Markup.nameCol = 'name_old'; if(!str) str = ""; str = str.replace(/\r/g, ''); if(!opts) opts = {}; if(!opts.skipReset) { Markup.uid = opts.uid || 'abc'; Markup.root = opts.root; Markup.preview = opts.preview || false; Markup.dbpage = opts.dbpage || false; Markup.defaultSource = false; if(Markup.uid != 'abc') g_screenshots[Markup.uid] = []; } if(opts.roles && (opts.roles & (U_GROUP_ADMIN|U_GROUP_EDITOR|U_GROUP_MOD|U_GROUP_BUREAU|U_GROUP_DEV|U_GROUP_BLOGGER)) && opts.mode != Markup.MODE_SIGNATURE) opts.mode = Markup.MODE_ARTICLE; Markup.mode = opts.mode || Markup.MODE_ARTICLE; Markup.allow = opts.allow || Markup.CLASS_STAFF; Markup.inBlog = opts.inBlog ? opts.inBlog : 0; if(opts.stopAtBreak) { var breakPos = str.indexOf('[break]'); if(breakPos != -1) str = str.substring(0, breakPos); } else str = str.replace('[break]', ''); var tree = new MarkupTree(); str = str.trim(); //str = Markup._safeHtml(str); // Resetting this prop causes TOC not to work when // there are multiple markup sections on a page. // this.collectTags = {}; if(this.postTags.length) { for(var i in this.postTags) { var tag = this.postTags[i]; if(str.indexOf('['+tag) != -1) if(!(Markup.tags[tag].allowedModes && Markup.tags[tag].allowedModes[MarkupModeMap[opts.mode]] == undefined)) for(var collect in Markup.tags[tag].collect) this.collectTags[collect] = true; } } //str = str.replace(this.ltrimRegex, function(match, $1, $2) { return '[' + $1 + ($2 ? $2 : '') + ']'; }); //str = str.replace(this.rtrimRegex, function(match, $1) { return '[/' + $1 + ']'; }); //str = str.replace(this.trimRegex, function(match, $1, $2) { return '[' + $1 + ($2 ? $2 : '') + ']'; }); str = str.replace(/\n(\s*)\n/g, '\n\n'); //str = str.replace(/\n/g, '
    '); var len = str.length; var textStart = 0, idx = 0, open = -1, close = -1, goodTag = true, isClose = false; var getValue = function(str) { var quote, space, value; if(str.charAt(0) == '"' || str.charAt(0) == "'") { quote = str.charAt(0); var end = str.indexOf(quote, 1); if(end > -1) { value = str.substring(1, end); str = str.substring(end+1).trim(); return { value: Markup._safeHtml(value), str: str }; } } space = str.indexOf(' '); if(space > -1) { value = str.substring(0, space); str = str.substring(space+1).trim(); } else { value = str; str = ''; } return { value: value, str: str }; }; var unnamedRe = /^\s*[a-z0-9]+\s*=/; while(idx < len) { open = str.indexOf('[', idx); if(open > -1) { idx = open + 1; if(open > 0 && str.charAt(open-1) == '\\') { goodTag = false; open = -1; } else { close = str.indexOf(']',idx); } } else idx = len; var tagName, attrs = {}; if(opts.highlight && $(opts.highlight)) attrs['data-highlight'] = opts.highlight + ':' + open; if(close > -1) { var tagContents = str.substring(open+1, close); if(tagContents.charAt(0) == '/') { isClose = true; tagName = tagContents.substr(1).trim().toLowerCase(); } if(!isClose) { var space = tagContents.indexOf(' '), assign = tagContents.indexOf('='); var quote; if((assign < space || space == -1) && assign > -1) { tagName = tagContents.substring(0, assign).toLowerCase(); tagContents = tagContents.substring(assign+1).trim(); var ret = getValue(tagContents); tagContents = ret.str; if(Markup.tags[tagName] == undefined || Markup.tags[tagName].attr == undefined || Markup.tags[tagName].attr.unnamed == undefined) { goodTag = false; } else attrs.unnamed = ret.value; } else if(space > -1) { tagName = tagContents.substring(0, space).toLowerCase(); tagContents = tagContents.substring(space+1).trim(); if(tagContents.indexOf('=') == -1) // legacy support, [quote name] { if(Markup.tags[tagName] == undefined || Markup.tags[tagName].attr == undefined || Markup.tags[tagName].attr.unnamed == undefined) { goodTag = false; } else attrs.unnamed = tagContents; tagContents = ''; } } else { tagName = tagContents.toLowerCase(); tagContents = ''; } if(Markup.tags[tagName] == undefined) { goodTag = false; } else if(goodTag) { var tag = Markup.tags[tagName]; while(tagContents != '') { var attr = ''; if(!unnamedRe.test(tagContents)) { attr = 'unnamed'; } else { assign = tagContents.indexOf('='); if(assign == -1) { goodTag = false; break; } attr = tagContents.substring(0, assign).trim().toLowerCase(); tagContents = tagContents.substring(assign+1).trim(); } var ret = getValue(tagContents); tagContents = ret.str; if(tag.attr == undefined || tag.attr[attr] == undefined) { if(Markup.attributes[attr] == undefined || (Markup.attributes[attr].valid != undefined && !Markup.attributes[attr].valid.test(ret.value))) { goodTag = false; break; } } attrs[attr] = ret.value; } if(goodTag && tag.attr) { for(var a in tag.attr) { if(tag.attr[a].req && attrs[a] == undefined) { goodTag = false; break; } else if(attrs[a] == undefined) continue; if(tag.attr[a].valid != undefined && !tag.attr[a].valid.test(attrs[a])) { goodTag = false; break; } } if(goodTag && tag.validate != undefined) { goodTag = tag.validate(attrs); } } } } else if(Markup.tags[tagName] == undefined) goodTag = false; } else goodTag = false; if(goodTag) { if(textStart != open) { var s = str.substring(textStart, open).replace(/\\\[/g, '['); var text = { _rawText: s }; tree.openTag('', text); } if(isClose) goodTag = tree.closeTag(tagName); else goodTag = tree.openTag(tagName, attrs); if(goodTag) textStart = idx = close + 1; else textStart = open; } goodTag = true; isClose = false; open = close = -1; } if(textStart < len) { var s = str.substr(textStart).replace(/\\\[/g, '['); var text = { _rawText: s }; tree.openTag('', text); } return tree; }, createMaps: function() { for(var i = 0; i < Markup.maps.length; ++i) { var m = Markup.maps[i]; new Mapper({parent: m[0], zone: m[1], coords: m[2], unique: i}); } Markup.maps = []; }, toHtml: function(str, opts) { if(!opts) opts = {}; if(!opts.allow) { if(opts.roles) opts.allow = Markup.rolesToClass(opts.roles); else opts.allow = Markup.CLASS_STAFF; } var tree = Markup._parse(str, opts); var html = tree.toHtml(); if(opts.prepend) html = opts.prepend + html; if(opts.append) html += opts['append']; setTimeout(Markup.createMaps, 250); return html; }, fromHtml: function(str, depth) { // Clean up the html source str = str.replace(/\n+/g, ''); str = str.replace(/\s+/g, ' '); str = str.replace(/> <'); //str = str.replace(/[\s\S]*?<\/style>/g, ''); str = str.replace(/<\/?[a-z][a-z0-9]*\b[\s\S]*?>/g, ' '); str = str.replace(//g, ''); str = str.replace(/\n[\n]+/g, "\n\n"); str = str.replace(/[ ]+/g, ' '); str = str.replace(/\t/g, ' '); return $WH.trim(str); }, removeTags: function(str, opts) { var tree = Markup._parse(str, opts); return tree.tagless(); }, matchOuterTags: function(str, left, right, flags) { var g = flags.indexOf('g') > -1, f = flags.replace(/g/g, ''), x = new RegExp(left + '|' + right, 'g' + f), // Open or close tag l = new RegExp(left, f), // Open tag only a = [], t, s, m, n; do { t = 0; while(m = x.exec(str)) { if(l.test(m[0])) { if (!t++) { s = x.lastIndex; n = m; } } else if (t) { if(!--t) { a.push([str.slice(s, m.index), n[0], m[0]]); if (!g) return a; } } } } while(t && (x.lastIndex = s)); return (a.length ? a : false); }, getImageUploadIds: function(str, opts) { var tree = Markup._parse(str, opts); return tree.imageUploadIds(); }, printHtml: function(str, div, opts) { div = $WH.ge(div); var html = Markup.toHtml(str, opts); div.innerHTML = html; Markup.createMaps(); }, toggleReveal: function(id) { var span = $('#reveal-' + id); if(span.length == 0) return; var toggle = $('#revealtoggle-' + id); if(span.is(':visible')) { span.hide(); toggle.text('(read more)'); } else { span.show(); toggle.text('(hide)'); } }, mapperPreview: function(id) { try { window.mapper = Markup.maps[id]; var win = window.open('?edit=mapper-preview', 'mapperpreview', 'toolbar=no,location=no,directories=no,status=yes,menubar=no,scrollbars=no,resizable=no,width=800,height=540'); win.focus(); } catch(e) {} }, createTabs: function(parent, tabs, preview) { var _ = new Tabs({parent: $WH.ge('dsf67g4d-' + parent.id + (preview ? '-preview' : '')), forum:1, noScroll: (preview ? true : false)}); for(var i = 0; i < tabs.length; ++i) { var tab = tabs[i]; _.add(tab.name, {id:parent.id + '-' + tab.id, icon: tab.icon, 'class': tab['class']}); } _.flush(); } }; var MarkupUtil = { ltrimText: function(attr) { attr._rawText = attr._rawText.ltrim(); return attr; }, rtrimText: function(attr) { attr._rawText = attr._rawText.rtrim(); return attr; }, checkSiblingTrim: function(lastNode, node) { if(node.name == '' && (Markup.tags[lastNode.name].rtrim || Markup.tags[lastNode.name].trim)) node.attr = MarkupUtil.ltrimText(node.attr); else if(lastNode.name == '' && (Markup.tags[node.name].ltrim || Markup.tags[node.name].trim)) lastNode.attr = MarkupUtil.rtrimText(lastNode.attr); return [lastNode, node]; } }; var MarkupTree = function() { this.nodes = []; this.currentNode = null; }; MarkupTree.prototype = { openTag: function(tag, attrs) { // Allowed class defaults to pending users if(tag != '' && Markup.tags[tag] && !Markup.tags[tag].allowedClass) Markup.tags[tag].allowedClass = MARKUP_CLASS_PENDING; if(!Markup.tags[tag]) { //document.write('bad tag name: "' + tag + '"
    '); return false; } else if(Markup.tags[tag].allowedModes && Markup.tags[tag].allowedModes[MarkupModeMap[Markup.mode]] == undefined) { return false; // tag not allowed in this mode } else if(Markup.tags[tag].allowedClass && Markup.tags[tag].allowedClass > Markup.allow) { return false; // tag requires a higher user class } if(Markup.mode == MARKUP_MODE_REPLY && !Markup.tags[tag].allowInReplies) return false; // Not allowed in comment replies var node = { name: tag, attr: attrs, parent: null, nodes: [] }; if(this.currentNode) node.parent = this.currentNode; if(Markup.tags[tag].allowedParents) { if(node.parent != null) { if(Markup.tags[tag].allowedParents[node.parent.name] === undefined) return false; } else if(Markup.root == undefined || Markup.tags[tag].allowedParents[Markup.root] == undefined) return false; } if(node.parent && Markup.tags[node.parent.name].allowedChildren && Markup.tags[node.parent.name].allowedChildren[tag] == undefined) return false; if(this.currentNode) { if(this.currentNode.nodes.length == 0 && node.name == '' && Markup.tags[this.currentNode.name].itrim) node.attr = MarkupUtil.ltrimText(node.attr); else if(this.currentNode.nodes.length > 0) { var lastNodeIndex = this.currentNode.nodes.length-1; var result = MarkupUtil.checkSiblingTrim(this.currentNode.nodes[lastNodeIndex], node); this.currentNode.nodes[lastNodeIndex] = result[0]; node = result[1]; } if(node.name == '') { node.attr._text = Markup._preText(node.attr._rawText); if(node.attr._text.length > 0) this.currentNode.nodes.push(node); } else this.currentNode.nodes.push(node); } else { if(this.nodes.length > 0) { var lastNodeIndex = this.nodes.length-1; var result = MarkupUtil.checkSiblingTrim(this.nodes[lastNodeIndex], node); this.nodes[lastNodeIndex] = result[0]; node = result[1]; } if(node.name == '') { node.attr._text = Markup._preText(node.attr._rawText); if(node.attr._text.length > 0) this.nodes.push(node); } else this.nodes.push(node); } if(!Markup.tags[tag].empty && !Markup.tags[tag].post) this.currentNode = node; return true; }, closeTag: function(tag) { if(Markup.tags[tag].empty || Markup.tags[tag].post) // empty tag means no close tag return false; if(!this.currentNode) // no tag open, so how are we closing one? return false; else if(this.currentNode.name == tag) // valid close { if(this.currentNode.nodes.length > 0) { var lastNodeIndex = this.currentNode.nodes.length-1; if(Markup.tags[this.currentNode.name].itrim && this.currentNode.nodes[lastNodeIndex].name == '') { var node = this.currentNode.nodes[lastNodeIndex]; node.attr = MarkupUtil.rtrimText(node.attr); node.attr._text = Markup._preText(node.attr._rawText); this.currentNode.nodes[lastNodeIndex] = node; } } this.currentNode = this.currentNode.parent; } else { // a tag was closed, but doesnt match the current open tag, so probably invalid nesting. attempt to find the matching tag, then fail if we cant var findLastNode = function(name, nodes) { for(var i = nodes.length-1; i >= 0; --i) { if(nodes[i].name == name) return i; } return -1; }; var idx; if(this.currentNode.parent) idx = findLastNode(tag, this.currentNode.parent.nodes); else idx = findLastNode(tag, this.nodes); if(idx == -1) // no matching tag return false; } return true; }, toHtml: function() { var postNodes = []; var collection = {}; for(var x in Markup.collectTags) collection[x] = []; this.tagless(true); var currentOffset = 0; var processNodes = function(nodes, depth, exclusions) { var str = ''; for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; if(depth == 0 && i == 0 && Markup.firstTags[node.name]) // first in text node.attr.first = true; else if(depth > 0 && i == 0 && Markup.firstTags[node.parent.name]) // first in block node.attr.first = true; if(i == nodes.length-1 && Markup.firstTags[node.name]) // last thing, block node.attr.last = true; if(Markup.excludeTags[node.name]) exclusions[node.name] = (exclusions[node.name] ? exclusions[node.name] + 1 : 1); for(var ex in exclusions) { for(var t in Markup.excludeTags[ex]) { if(Markup.excludeTags[ex][t][node.name]) node.attr[t] = false; } } if(Markup.collectTags[node.name]) { node.offset = currentOffset++; collection[node.name].push(node); } if(Markup.tags[node.name].post) { var comment = ''; str += comment; postNodes.push([node, comment]); } else if(Markup.tags[node.name].empty) { var html; if(node.parent && Markup.tags[node.parent.name].rawText) html = Markup.tags[node.name].toHtml(node.attr, { needsRaw: true }); else html = Markup.tags[node.name].toHtml(node.attr); if(typeof html == 'string') str += html; else if(html !== undefined) { if(str == '') str = []; str.push(html); } } else { var contents = arguments.callee(node.nodes, depth+1, exclusions); node.attr._contents = contents; node.attr._nodes = node.nodes; var tags = Markup.tags[node.name].toHtml(node.attr); if(tags.length == 2) str += tags[0] + contents + tags[1]; else if(tags.length == 1) { if(typeof tags[0] == 'string') str += tags[0]; else { if(str == '') str = []; str.push(tags[0]); } } } if(exclusions[node.name]) { exclusions[node.name]--; if(exclusions[node.name] == 0) delete exclusions[node.name]; } } return str; }; str = processNodes(this.nodes, 0, []); for(var i = 0; i < postNodes.length; ++i) { var node = postNodes[i][0]; var replace = postNodes[i][1]; var html = Markup.tags[node.name].postHtml(node.attr, collection); if(typeof html == 'string') str = str.replace(replace, html); } return str; }, tagless: function(n) { var processNodes = function(nodes) { var str = ''; for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; var contents = arguments.callee(node.nodes); if(n) { node.attr._textContents = contents; } else { node.attr._contents = contents; } if(node.name == '') str += Markup.tags[node.name].toHtml(node.attr, { noLink: true, noNbsp: true }); else if(Markup.tags[node.name].toText) str += Markup.tags[node.name].toText(node.attr); if(!Markup.tags[node.name].taglessSkip) str += contents; } return str; }; if(n) processNodes(this.nodes); else { var str = processNodes(this.nodes); str = str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); return str; } }, imageUploadIds: function() { var ids = []; var processNodes = function(nodes) { for(var i = 0; i < nodes.length; ++i) { var node = nodes[i]; if(node.name == 'img' && node.attr.upload) ids.push(node.attr.upload); arguments.callee(node.nodes); } }; processNodes(this.nodes); return ids; } }; Markup.tags.modelviewer = Markup.tags.model; // Backwards compatability Markup.reveals = 0; Markup._init(); $(document).ready(function() { $('.quote-header').each(function(i) { var $this = $(this); var sibs = $this.siblings(); if(sibs.hasClass('quote-body')) { var a = $('', { href: 'javascript:;', 'class': 'toggle' }); a.click(function(header) { var $head = $(header); var parent = $head.parent(); parent.toggleClass('collapse'); if(parent.hasClass('collapse')) $(this).html('Expand'); else $(this).html('Collapse'); }.bind(a, this)); if($(this).parent().hasClass('collapse')) a.html('Expand'); else a.html('Collapse'); $this.append(a); } }); $('.quote-wh').each(function(i) { var $this = $(this); var a = $('', { href: 'javascript:;', 'class': 'toggle' }); a.click(function(thisObj) { var $this = $(thisObj); $this.toggleClass('collapse'); if($this.hasClass('collapse')) $(this).html('Expand'); else $(this).html('Collapse'); }.bind(a, this)); if($(this).hasClass('collapse')) a.html('Expand'); else a.html('Collapse'); $this.append(a); }); });