html_codesniffer
Version:
HTML_CodeSniffer is a client-side JavaScript that checks a HTML document or source code, and detects violations of a defined coding standard.
1,374 lines (1,101 loc) • 82.6 kB
JavaScript
/**
* +--------------------------------------------------------------------+
* | This HTML_CodeSniffer file is Copyright (c) |
* | Squiz Pty Ltd (ABN 77 084 670 600) |
* +--------------------------------------------------------------------+
* | IMPORTANT: Your use of this Software is subject to the terms of |
* | the Licence provided in the file licence.txt. If you cannot find |
* | this file please contact Squiz (www.squiz.com.au) so we may |
* | provide you a copy. |
* +--------------------------------------------------------------------+
*
*/
_global.HTMLCSAuditor = new function()
{
var _prefix = 'HTMLCS-';
var _screen = '';
var _standard = '';
var _sources = [];
var _options = {};
var _doc = null;
var _top = null;
var _messages = [];
var _page = 1;
var _sbWidth = null;
var self = this;
this.pointerContainer = null;
/**
* Build the "summary section" square button.
*
* @return {HTMLDivElement}
*/
var buildSummaryButton = function(id, className, title, onclick) {
var button = _doc.createElement('div');
button.id = id;
button.className = _prefix + 'button';
button.setAttribute('title', title);
var buttonInner = _doc.createElement('span');
buttonInner.className = _prefix + 'button-icon ' + _prefix + 'button-' + className;
button.appendChild(buttonInner);
var nbsp = _doc.createTextNode(String.fromCharCode(160));
button.appendChild(nbsp);
if ((onclick instanceof Function) === true) {
button.onclick = function() {
if (/disabled/.test(button.className) === false) {
onclick(button);
}
};
}
return button;
};
/**
* Build a checkbox.
*
* @return {HTMLDivElement}
*/
var buildCheckbox = function(id, title, checked, disabled, onclick) {
if (checked === undefined) {
checked = false;
}
var label = _doc.createElement('label');
var content = '';
label.className = _prefix + 'checkbox';
content += '<span class="' + _prefix + 'checkbox-switch">';
content += '<span class="' + _prefix + 'checkbox-slider"></span>';
content += '<input id="' + id + '" type="checkbox"';
if (checked === true) {
content += ' checked="checked"';
label.className += ' active';
}
if (disabled === true) {
content += ' disabled="disabled"';
label.className += ' disabled';
}
content += ' title="' + title + '" /></span>';
label.innerHTML = content;
var input = label.getElementsByTagName('input')[0];
label.onclick = function(event) {
if (disabled === false) {
input.checked = !input.checked;
if (input.checked === true) {
label.className += ' active';
} else {
label.className = label.className.replace('active', '');
}
if (onclick instanceof Function === true) {
onclick(input);
}
}//end if
return false;
};
return label;
};
/**
* Build a radio button.
*
* @return {HTMLDivElement}
*/
var buildRadioButton = function(groupName, value, title, checked) {
if (checked === undefined) {
checked = false;
}
var label = _doc.createElement('label');
label.className = _prefix + 'radio';
var content = '<span class="' + _prefix + 'radio-title">' + title + '</span>';
content += '<span class="' + _prefix + 'radio-switch">';
content += '<span class="' + _prefix + 'radio-slider"></span>';
content += '<input type="radio" name="' + _prefix + groupName + '" ';
content += 'class="' + _prefix + 'radiobtn"';
content += 'value="' + value + '"';
if (checked === true) {
content += ' checked="true"';
}
content += ' /></span>';
label.innerHTML = content;
return label;
};
/**
* Build the header section at the absolute top of the interface.
*
* @return {HTMLDivElement}
*/
var buildHeaderSection = function(standard, wrapper) {
var header = _doc.createElement('div');
header.className = _prefix + 'header';
header.innerHTML = 'HTML_CodeSniffer by Squiz';
header.setAttribute('title', _global.HTMLCS.getTranslation("auditor_using_standard") + standard);
var dragging = false;
var prevX = 0;
var prevY = 0;
var mouseX = 0;
var mouseY = 0;
header.onmousedown = function(e) {
e = e || window.event;
dragging = true;
mouseX = e.clientX;
mouseY = e.clientY;
return false;
};
_doc.onmousemove = function(e) {
e = e || window.event;
if (dragging === true) {
var top = wrapper.offsetTop;
var left = wrapper.offsetLeft;
if (mouseY < e.clientY) {
top += (e.clientY - mouseY);
wrapper.style.top = top + 'px';
} else if (mouseY > e.clientY) {
top -= (mouseY - e.clientY);
wrapper.style.top = top + 'px';
}
if (mouseX < e.clientX) {
left += (e.clientX - mouseX);
wrapper.style.left = left + 'px';
} else if (mouseX > e.clientX) {
left -= (mouseX - e.clientX);
wrapper.style.left = left + 'px';
}
mouseX = e.clientX;
mouseY = e.clientY;
}//end if
};
_doc.onmouseup = function(e) {
var maxHeight = window.innerHeight - wrapper.offsetHeight;
if (mouseY > maxHeight) {
wrapper.style.top = maxHeight + 'px';
} else if (mouseY < 0) {
wrapper.style.top = 0 + 'px';
}
dragging = false;
};
var closeIcon = _doc.createElement('div');
closeIcon.className = _prefix + 'close';
closeIcon.setAttribute('title', _global.HTMLCS.getTranslation("auditor_close"));
closeIcon.onmousedown = function() {
self.close.call(self);
};
header.appendChild(closeIcon);
return header;
};
/**
* Build the summary section of the interface.
*
* This includes the number of errors, warnings and notices; as well as buttons
* to access the settings interface, and to recheck the content.
*
* @return {HTMLDivElement}
*/
var buildSummarySection = function(errors, warnings, notices) {
var summary = _doc.createElement('div');
summary.className = _prefix + 'summary';
var leftPane = _doc.createElement('div');
leftPane.className = _prefix + 'summary-left';
summary.appendChild(leftPane);
var rightPane = _doc.createElement('div');
rightPane.className = _prefix + 'summary-right';
summary.appendChild(rightPane);
var leftContents = [];
var divider = ',  <span class="' + _prefix + 'divider"></span>';
if (errors > 0) {
var typeName = _global.HTMLCS.getTranslation("auditor_errors");
if (errors === 1) {
typeName = _global.HTMLCS.getTranslation("auditor_error");
}
leftContents.push('<strong>' + errors + '</strong> ' + typeName);
}
if (warnings > 0) {
var typeName = _global.HTMLCS.getTranslation("auditor_warnings");
if (warnings === 1) {
typeName = _global.HTMLCS.getTranslation("auditor_warning");
}
leftContents.push('<strong>' + warnings + '</strong> ' + typeName);
}
if (notices > 0) {
var typeName = _global.HTMLCS.getTranslation("auditor_notices");
if (notices === 1) {
typeName = _global.HTMLCS.getTranslation("auditor_notice");
}
leftContents.push('<strong>' + notices + '</strong> ' + typeName);
}
// Start lineage in left pane.
var lineage = _doc.createElement('ol');
lineage.className = _prefix + 'lineage';
// Back to summary item.
var lineageHomeItem = _doc.createElement('li');
lineageHomeItem.className = _prefix + 'lineage-item';
var lineageHomeLink = _doc.createElement('a');
lineageHomeLink.className = _prefix + 'lineage-link';
lineageHomeLink.href = 'javascript:';
var lineageHomeSpan = _doc.createElement('span');
lineageHomeSpan.innerHTML = 'Home';
lineageHomeLink.appendChild(lineageHomeSpan);
lineageHomeLink.onmousedown = function() {
self.run(_standard, _sources, _options);
};
// Issue totals.
var lineageTotalsItem = _doc.createElement('li');
lineageTotalsItem.className = _prefix + 'lineage-item';
lineageTotalsItem.innerHTML = leftContents.join(divider);
lineageHomeItem.appendChild(lineageHomeLink);
lineage.appendChild(lineageHomeItem);
lineage.appendChild(lineageTotalsItem);
leftPane.appendChild(lineage);
rightPane.appendChild(_doc.createTextNode(String.fromCharCode(160)));
return summary;
};
/**
* Build the summary section of the interface.
*
* This includes the number of errors, warnings and notices; as well as buttons
* to access the settings interface, and to recheck the content.
*
* @return {HTMLDivElement}
*/
var buildDetailSummarySection = function(issue, totalIssues) {
var summary = _doc.createElement('div');
summary.className = _prefix + 'summary-detail';
var leftPane = _doc.createElement('div');
leftPane.className = _prefix + 'summary-left';
var rightPane = _doc.createElement('div');
rightPane.className = _prefix + 'summary-right';
// Start lineage.
var lineage = _doc.createElement('ol');
lineage.className = _prefix + 'lineage';
var lineageHomeItem = _doc.createElement('li');
lineageHomeItem.className = _prefix + 'lineage-item';
var lineageHomeLink = _doc.createElement('a');
lineageHomeLink.className = _prefix + 'lineage-link';
lineageHomeLink.href = 'javascript:';
var lineageHomeSpan = _doc.createElement('span');
lineageHomeSpan.innerHTML = _global.HTMLCS.getTranslation("auditor_home");
lineageHomeLink.appendChild(lineageHomeSpan);
lineageHomeLink.onmousedown = function() {
self.run(_standard, _sources, _options);
};
// Back to Report item.
var lineageReportItem = _doc.createElement('li');
lineageReportItem.className = _prefix + 'lineage-item';
var lineageReportLink = _doc.createElement('a');
lineageReportLink.className = _prefix + 'lineage-link';
lineageReportLink.href = 'javascript:';
lineageReportLink.innerHTML = _global.HTMLCS.getTranslation("auditor_report");
lineageReportLink.setAttribute('title', _global.HTMLCS.getTranslation("auditor_back_to_report"));
lineageReportLink.onmousedown = function() {
var list = _doc.querySelectorAll('.HTMLCS-inner-wrapper')[0];
list.style.marginLeft = '0px';
list.style.maxHeight = null;
summary.style.display = 'none';
var listSummary = _doc.querySelectorAll('.HTMLCS-summary')[0];
listSummary.style.display = 'block';
};
// Issue Count item.
var lineageTotalsItem = _doc.createElement('li');
lineageTotalsItem.className = _prefix + 'lineage-item';
lineageTotalsItem.innerHTML = _global.HTMLCS.getTranslation("auditor_issue") + ' ' + issue + ' ' + _global.HTMLCS.getTranslation("auditor_of") + ' ' + totalIssues;
lineageHomeItem.appendChild(lineageHomeLink);
lineageReportItem.appendChild(lineageReportLink);
lineage.appendChild(lineageHomeItem);
lineage.appendChild(lineageReportItem);
lineage.appendChild(lineageTotalsItem);
leftPane.appendChild(lineage);
var buttonGroup = _doc.createElement('div');
buttonGroup.className = _prefix + 'button-group';
var prevButton = buildSummaryButton(_prefix + 'button-previous-issue', 'previous', _global.HTMLCS.getTranslation("auditor_previous_issue"), function(target) {
var newIssue = Number(issue) - 1;
if (newIssue >= 1) {
setCurrentDetailIssue(newIssue - 1);
wrapper = summary.parentNode;
var newSummary = buildDetailSummarySection(newIssue, totalIssues);
wrapper.replaceChild(newSummary, summary);
newSummary.style.display = 'block';
var issueList = _doc.querySelectorAll('.HTMLCS-issue-detail-list')[0];
issueList.firstChild.style.marginLeft = (parseInt(issueList.firstChild.style.marginLeft, 10) + 300) + 'px';
pointToIssueElement(newIssue - 1);
}//end if
});
var nextButton = buildSummaryButton(_prefix + 'button-next-issue', 'next', _global.HTMLCS.getTranslation("auditor_next_issue"), function(target) {
var newIssue = Number(issue) + 1;
if (newIssue <= _messages.length) {
setCurrentDetailIssue(newIssue - 1);
wrapper = summary.parentNode;
var newSummary = buildDetailSummarySection(newIssue, totalIssues);
wrapper.replaceChild(newSummary, summary);
newSummary.style.display = 'block';
var issueList = _doc.querySelectorAll('.HTMLCS-issue-detail-list')[0];
issueList.firstChild.style.marginLeft = (parseInt(issueList.firstChild.style.marginLeft, 10) - 300) + 'px';
pointToIssueElement(newIssue - 1);
}//end if
});
if (issue === 1) {
prevButton.className += ' disabled';
}
if (issue === totalIssues) {
nextButton.className += ' disabled';
}
buttonGroup.appendChild(prevButton);
buttonGroup.appendChild(nextButton);
rightPane.appendChild(buttonGroup);
summary.appendChild(leftPane);
summary.appendChild(rightPane);
return summary;
};
/**
* Build the main issue list section of the interface.
*
* This is what you see when the tests have finished running. A summary list of
* , paged five at a time.
*
* @return {HTMLDivElement}
*/
var buildIssueListSection = function(messages) {
var issueListWidth = (Math.ceil(messages.length / 5) * 300);
var issueList = _doc.createElement('div');
issueList.id = _prefix + 'issues';
issueList.className = _prefix + 'details';
issueList.setAttribute('style', 'width: ' + issueListWidth + 'px');
var listSection = _doc.createElement('ol');
listSection.className = _prefix + 'issue-list';
listSection.setAttribute('style', 'margin-left: 0');
for (var i = 0; i < messages.length; i++) {
if ((i > 0) && ((i % 5) === 0)) {
issueList.appendChild(listSection);
var listSection = _doc.createElement('ol');
listSection.className = _prefix + 'issue-list';
}
var msg = buildMessageSummary(i, messages[i]);
listSection.appendChild(msg);
}
issueList.appendChild(listSection);
return issueList;
};
var buildIssueDetailSection = function(messages) {
var issueListWidth = (messages.length * 300);
var issueList = _doc.createElement('div');
issueList.id = _prefix + 'issues-detail';
issueList.className = _prefix + 'details';
issueList.setAttribute('style', 'width: ' + issueListWidth + 'px');
var listSection = _doc.createElement('ol');
listSection.className = _prefix + 'issue-detail-list';
listSection.setAttribute('style', 'margin-left: 0');
for (var i = 0; i < messages.length; i++) {
var msg = buildMessageDetail(i, messages[i]);
listSection.appendChild(msg);
}
issueList.appendChild(listSection);
return issueList;
};
var buildSettingsSection = function() {
var settingsDiv = _doc.createElement('div');
settingsDiv.className = _prefix + 'settings';
var useStandardDiv = _doc.createElement('div');
useStandardDiv.id = _prefix + 'settings-use-standard';
var useStandardLabel = _doc.createElement('label');
useStandardLabel.innerHTML = _global.HTMLCS.getTranslation("auditor_standards") + ':';
useStandardLabel.setAttribute('for', _prefix + 'settings-use-standard-select');
var useStandardSelect = _doc.createElement('select');
useStandardSelect.id = _prefix + 'settings-use-standard-select';
useStandardSelect.innerHTML = '';
var standards = HTMLCSAuditor.getStandardList();
for (var i = 0; i < standards.length; i++) {
var standard = standards[i];
var option = _doc.createElement('option');
option.value = standard;
option.innerHTML = _global['HTMLCS_' + standard].name;
if (standard === _standard) {
option.selected = true;
}
useStandardSelect.appendChild(option);
useStandardSelect.onchange = function() {
_standard = this.options[this.selectedIndex].value;
self.run(_standard, _sources, _options);
};
}
var issueCountDiv = _doc.createElement('div');
issueCountDiv.id = _prefix + 'settings-issue-count';
var issueCountHelpDiv = _doc.createElement('div');
issueCountHelpDiv.id = _prefix + 'settings-issue-count-help';
issueCountHelpDiv.innerHTML = _global.HTMLCS.getTranslation("auditor_select_types");
var viewReportDiv = _doc.createElement('div');
viewReportDiv.id = _prefix + 'settings-view-report';
viewReportDiv.innerHTML = _global.HTMLCS.getTranslation("auditor_view_report");
viewReportDiv.onclick = function() {
if (/disabled/.test(this.className) === false) {
_options.show = {
error: _doc.getElementById(_prefix + 'include-error').checked,
warning: _doc.getElementById(_prefix + 'include-warning').checked,
notice: _doc.getElementById(_prefix + 'include-notice').checked
};
var wrapper = _doc.getElementById(_prefix + 'wrapper');
var newWrapper = self.build(_standard, _messages, _options);
if (_options.parentElement) {
_options.parentElement.replaceChild(newWrapper, wrapper);
} else {
newWrapper.style.left = wrapper.style.left;
newWrapper.style.top = wrapper.style.top;
_doc.body.replaceChild(newWrapper, wrapper);
}
if (_options.listUpdateCallback) {
_options.listUpdateCallback.call(this, _messages);
}
}//end if
};
var wrapper = _doc.getElementById(_prefix + 'wrapper');
var levels = self.countIssues(_messages);
// Set default show options based on the first run. Don't re-do these, let
// the user's settings take priority, unless there is no message.
if ((_options.show === undefined) && (_messages.length > 0)) {
_options.show = {
error: true,
warning: true,
notice: false
};
if ((levels.error === 0) && (levels.warning === 0)) {
_options.show.notice = true;
}
}
for (var level in levels) {
var msgCount = levels[level];
var levelDiv = _doc.createElement('div');
levelDiv.className = _prefix + 'issue-tile ' + _prefix + level.toLowerCase();
var levelName = null;
var levelCountDiv = _doc.createElement('div');
levelCountDiv.className = 'HTMLCS-tile-text';
if(level == "error") {
levelName = _global.HTMLCS.getTranslation('auditor_error');
if (msgCount !== 1) {
levelName = _global.HTMLCS.getTranslation('auditor_errors');
}
}
if(level == "warning") {
levelName = _global.HTMLCS.getTranslation('auditor_warning');
if (msgCount !== 1) {
levelName = _global.HTMLCS.getTranslation('auditor_warnings');
}
}
if(level == "notice") {
levelName = _global.HTMLCS.getTranslation('auditor_notice');
if (msgCount !== 1) {
levelName = _global.HTMLCS.getTranslation('auditor_notices');
}
}
var content = '<strong>' + msgCount + '</strong> ' + levelName;
levelCountDiv.innerHTML = content;
if (_options.show === undefined) {
var checked = false;
var disabled = true;
} else {
var checked = _options.show[level];
var disabled = false;
if (msgCount === 0) {
checked = false;
disabled = true;
}
}
var levelSwitch = buildCheckbox(_prefix + 'include-' + level, 'Toggle display of ' + level + ' messages', checked, disabled, function(input) {
// Only change checkboxes that haven't been disabled.
var enable = false;
if (_doc.getElementById(_prefix + 'include-error').disabled === false) {
_options.show.error = _doc.getElementById(_prefix + 'include-error').checked;
enable = enable || _options.show.error;
}
if (_doc.getElementById(_prefix + 'include-warning').disabled === false) {
_options.show.warning = _doc.getElementById(_prefix + 'include-warning').checked;
enable = enable || _options.show.warning;
}
if (_doc.getElementById(_prefix + 'include-notice').disabled === false) {
_options.show.notice = _doc.getElementById(_prefix + 'include-notice').checked;
enable = enable || _options.show.notice;
}
if (enable === true) {
viewReportDiv.className = viewReportDiv.className.replace(/ disabled/g, '');
} else {
viewReportDiv.className += ' disabled';
}
});
levelDiv.appendChild(levelCountDiv);
levelDiv.appendChild(levelSwitch);
issueCountDiv.appendChild(levelDiv);
}
// Only disable if we have "currently showing" setting on.
if (_options.show !== undefined) {
var enable = (_options.show.error || _options.show.warning || _options.show.notice);
if (enable === false) {
viewReportDiv.className += ' disabled';
}
} else {
viewReportDiv.className += ' disabled';
}
useStandardDiv.appendChild(useStandardLabel);
useStandardDiv.appendChild(useStandardSelect);
settingsDiv.appendChild(useStandardDiv);
settingsDiv.appendChild(issueCountDiv);
settingsDiv.appendChild(issueCountHelpDiv);
settingsDiv.appendChild(viewReportDiv);
return settingsDiv;
};
var buildMessageSummary = function(id, message) {
var msg = '';
var typeText = '';
var typeClass = '';
switch (message.type) {
case HTMLCS.ERROR:
typeText = 'Error';
break;
case HTMLCS.WARNING:
typeText = 'Warning';
break;
case HTMLCS.NOTICE:
typeText = 'Notice';
break;
default:
// Not defined.
break;
}//end switch
var typeClass = typeText.toLowerCase();
var messageMsg = message.msg;
if (messageMsg.length > 115) {
messageMsg = messageMsg.substr(0, 115) + '...';
}
var msg = _doc.createElement('li');
msg.id = _prefix + 'msg-' + id;
var typeIcon = _doc.createElement('span');
typeIcon.className = _prefix + 'issue-type ' + _prefix + typeClass;
typeIcon.setAttribute('title', typeText);
msg.appendChild(typeIcon);
var msgTitle = _doc.createElement('span');
msgTitle.className = _prefix + 'issue-title';
msgTitle.innerHTML = messageMsg;
msg.appendChild(msgTitle);
msg.onclick = function() {
var id = this.id.replace(new RegExp(_prefix + 'msg-'), '');
setCurrentDetailIssue(id);
var detailList = _doc.querySelectorAll('.HTMLCS-issue-detail-list')[0];
detailList.className += ' ' + _prefix + 'transition-disabled';
detailList.firstChild.style.marginLeft = (id * -300) + 'px';
pointToIssueElement(id);
setTimeout(function() {
detailList.className = detailList.className.replace(new RegExp(' ' + _prefix + 'transition-disabled'), '');
}, 500);
var list = _doc.querySelectorAll('.HTMLCS-inner-wrapper')[0];
list.style.marginLeft = '-300px';
list.style.maxHeight = '15em';
summary = _doc.querySelectorAll('.HTMLCS-summary-detail')[0];
var newSummary = buildDetailSummarySection(parseInt(id) + 1, _messages.length);
summary.parentNode.replaceChild(newSummary, summary);
newSummary.style.display = 'block';
var oldSummary = _doc.querySelectorAll('.HTMLCS-summary')[0];
oldSummary.style.display = 'none';
};
return msg;
};
var setCurrentDetailIssue = function(id) {
var detailList = _doc.querySelectorAll('.HTMLCS-issue-detail-list')[0];
var items = detailList.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].className = items[i].className.replace(new RegExp(' ' + _prefix + 'current'), '');
}
var currentItem = _doc.getElementById('HTMLCS-msg-detail-' + id);
currentItem.className += ' ' + _prefix + 'current';
if (_options.showIssueCallback) {
_options.showIssueCallback.call(this, id);
}
};
var buildMessageDetail = function(id, message, standard) {
if (standard === undefined) {
standard = _standard;
}
var typeText = '';
switch (message.type) {
case HTMLCS.ERROR:
typeText = 'Error';
break;
case HTMLCS.WARNING:
typeText = 'Warning';
break;
case HTMLCS.NOTICE:
typeText = 'Notice';
break;
default:
// Not defined.
break;
}//end switch
var typeClass = _prefix + typeText.toLowerCase();
var standardObj = HTMLCS.util.getElementWindow(_doc)['HTMLCS_' + standard];
var standardObj = _top['HTMLCS_' + standard];
var msgInfo = [];
if (standardObj.getMsgInfo) {
msgInfo = standardObj.getMsgInfo(message.code);
}
var msgDiv = _doc.createElement('li');
msgDiv.id = _prefix + 'msg-detail-' + id;
var msgDetailsDiv = _doc.createElement('div');
msgDetailsDiv.className = _prefix + 'issue-details';
var msgType = _doc.createElement('span');
msgType.className = _prefix + 'issue-type ' + typeClass;
msgType.setAttribute('title', typeText);
var msgTitle = _doc.createElement('div');
msgTitle.className = _prefix + 'issue-title';
msgTitle.innerHTML = message.msg;
var msgRef = _doc.createElement('div');
msgRef.className = _prefix + 'issue-wcag-ref';
var refContent = '';
for (var i = 0; i < msgInfo.length; i++) {
refContent += '<em>' + msgInfo[i][0] + ':</em> ' + msgInfo[i][1] + '<br/>';
}
msgRef.innerHTML = refContent;
msgDetailsDiv.appendChild(msgType);
msgDetailsDiv.appendChild(msgTitle);
msgDetailsDiv.appendChild(msgRef);
msgDiv.appendChild(msgDetailsDiv);
// If the item cannot be pointed to, tell them why.
if (pointer.isPointable(message.element) === false) {
var msgElementSource = _doc.createElement('div');
msgElementSource.className = _prefix + 'issue-source';
msgDiv.appendChild(msgElementSource);
var msgElementSourceInner = _doc.createElement('div');
msgElementSourceInner.className = _prefix + 'issue-source-inner-u2p';
var msg = _global.HTMLCS.getTranslation('auditor_unable_to_point');
if (message.element.nodeName === '#document') {
msg = _global.HTMLCS.getTranslation('auditor_applies_entire_document');
} else if (message.element.ownerDocument === null) {
msg = _global.HTMLCS.getTranslation('auditor_unable_to_point_removed');
} else {
var body = message.element.ownerDocument.getElementsByTagName('body')[0];
if (HTMLCS.util.isInDocument(message.element) === false) {
msg += _global.HTMLCS.getTranslation('auditor_unable_to_point_entire');
} else if (HTMLCS.util.contains(body, message.element) === false) {
msg = _global.HTMLCS.getTranslation('auditor_unable_to_point_outside');
} else {
msg += ' ' + _global.HTMLCS.getTranslation('auditor_unable_to_point_outside');
}
}
if (msgElementSourceInner.textContent !== undefined) {
msgElementSourceInner.textContent = msg;
} else {
// IE8 uses innerText instead. Oh well.
msgElementSourceInner.innerText = msg;
}
msgElementSource.appendChild(msgElementSourceInner);
}
// Build the source view, if outerHTML exists (Firefox >= 11, Webkit, IE),
// and applies to the particular element (ie. document doesn't have it).
if (_options.customIssueSource) {
var msgElementSource = _doc.createElement('div');
msgElementSource.className = _prefix + 'issue-source';
msgDiv.appendChild(msgElementSource);
_options.customIssueSource.call(this, id, message, standard, msgElementSource, msgDetailsDiv);
} else {
var msgElementSource = _doc.createElement('div');
msgElementSource.className = _prefix + 'issue-source';
// Header row.
var msgElementSourceHeader = _doc.createElement('div');
msgElementSourceHeader.className = _prefix + 'issue-source-header';
var msgSourceHeaderText = _doc.createElement('strong');
msgSourceHeaderText.innerHTML = _global.HTMLCS.getTranslation("auditor_code_snippet");
var btnPointTo = buildSummaryButton(_prefix + 'button-point-to-element-' + id, 'pointer', _global.HTMLCS.getTranslation("auditor_point_to_element"), function() {
self.pointToElement(message.element);
});
msgElementSourceHeader.appendChild(msgSourceHeaderText);
msgElementSourceHeader.appendChild(btnPointTo);
msgElementSource.appendChild(msgElementSourceHeader);
if (message.element.outerHTML) {
var preText = '';
var postText = '';
if (message.element.innerHTML.length > 31) {
var outerHTML = message.element.outerHTML.replace(message.element.innerHTML, message.element.innerHTML.substr(0, 31) + '...');
} else {
var outerHTML = message.element.outerHTML;
}
// Find previous siblings.
var preNode = message.element.previousSibling;
while (preText.length <= 31) {
if (preNode === null) {
break;
} else {
if (preNode.nodeType === 1) {
// Element node.
preText = preNode.outerHTML;
} else if (preNode.nodeType === 3) {
// Text node.
if (preNode.textContent !== undefined) {
preText = preNode.textContent + preText;
} else {
preText = preNode.nodeValue + preText;
}
}
if (preText.length > 31) {
preText = '...' + preText.substr(preText.length - 31);
}
}
preNode = preNode.previousSibling;
}//end while
// Find following siblings.
var postNode = message.element.nextSibling;
while (postText.length <= 31) {
if (postNode === null) {
break;
} else {
if (postNode.nodeType === 1) {
// Element node.
postText += postNode.outerHTML;
} else if (postNode.nodeType === 3) {
// Text node.
if (postNode.textContent !== undefined) {
postText += postNode.textContent;
} else {
postText += postNode.nodeValue;
}
}
if (postText.length > 31) {
postText = postText.substr(0, 31) + '...';
}
}
postNode = postNode.nextSibling;
}//end while
// Actual source code, highlighting offending element.
var msgElementSourceInner = _doc.createElement('div');
msgElementSourceInner.className = _prefix + 'issue-source-inner';
var msgElementSourceMain = _doc.createElement('strong');
if (msgElementSourceMain.textContent !== undefined) {
msgElementSourceMain.textContent = outerHTML;
} else {
// IE8 uses innerText instead. Oh well.
msgElementSourceMain.innerText = outerHTML;
}
msgElementSourceInner.appendChild(_doc.createTextNode(preText));
msgElementSourceInner.appendChild(msgElementSourceMain);
msgElementSourceInner.appendChild(_doc.createTextNode(postText));
msgElementSource.appendChild(msgElementSourceInner);
} else if (message.element.nodeName === '#document') {
// Show nothing, it's the document root.
} else {
// No support for outerHTML.
var msgElementSourceInner = _doc.createElement('div');
msgElementSourceInner.className = _prefix + 'issue-source-not-supported';
var nsText = _global.HTMLCS.getTranslation('auditor_unsupported_browser');
msgElementSourceInner.appendChild(_doc.createTextNode(nsText));
msgElementSource.appendChild(msgElementSourceInner);
}//end if
msgDiv.appendChild(msgElementSource);
}//end if
return msgDiv;
};
var buildNavigation = function(page, totalPages) {
var navDiv = _doc.createElement('div');
navDiv.className = _prefix + 'navigation';
var prev = _doc.createElement('span');
prev.className = _prefix + 'nav-button ' + _prefix + 'previous';
prev.innerHTML = String.fromCharCode(160);
if (page === 1) {
prev.className += ' ' + _prefix + 'disabled';
}
navDiv.appendChild(prev);
var pageNum = _doc.createElement('span');
pageNum.className = _prefix + 'page-number';
// pageNum.innerHTML = 'Page ' + page + ' of ' + totalPages;
pageNum.innerHTML = _global.HTMLCS.getTranslation("auditor_page") + ' ' + page + ' ' + _global.HTMLCS.getTranslation("auditor_of") + ' ' + totalPages;
navDiv.appendChild(pageNum);
var next = _doc.createElement('span');
next.className = _prefix + 'nav-button ' + _prefix + 'next';
next.innerHTML = String.fromCharCode(160);
if (page === totalPages) {
next.className += ' ' + _prefix + 'disabled';
}
navDiv.appendChild(next);
prev.onclick = function() {
if (_page > 1) {
_page--;
if (_page === 1) {
prev.className += ' ' + _prefix + 'disabled';
}
}
if (totalPages > 1) {
next.className = next.className.replace(new RegExp(' ' + _prefix + 'disabled'), '');
}
pageNum.innerHTML = '';
pageNum.appendChild(document.createTextNode(_global.HTMLCS.getTranslation("auditor_page") + ' ' + _page + ' ' + _global.HTMLCS.getTranslation("auditor_of") + ' ' + totalPages));
var issueList = _doc.querySelectorAll('.HTMLCS-issue-list')[0];
issueList.style.marginLeft = ((_page - 1) * -300) + 'px';
};
next.onclick = function() {
if (_page < totalPages) {
_page++;
if (_page === totalPages) {
next.className += ' ' + _prefix + 'disabled';
}
}
if (totalPages > 1) {
prev.className = prev.className.replace(new RegExp(' ' + _prefix + 'disabled'), '');
}
pageNum.innerHTML = '';
pageNum.appendChild(document.createTextNode(_global.HTMLCS.getTranslation("auditor_page") + ' ' + _page + ' ' + _global.HTMLCS.getTranslation("auditor_of") + ' ' + totalPages));
var issueList = _doc.querySelectorAll('.HTMLCS-issue-list')[0];
issueList.style.marginLeft = ((_page - 1) * -300) + 'px';
};
return navDiv;
};
var pointToIssueElement = function(issue) {
var msg = _messages[Number(issue)];
if (!msg.element) {
return;
}
var btnPointTo = _doc.getElementById(_prefix + 'button-point-to-element-' + issue);
pointer.container = self.pointerContainer || _doc.getElementById('HTMLCS-wrapper');
if (pointer.isPointable(msg.element) === false) {
var myPointer = pointer.getPointer(msg.element);
if (pointer.pointer) {
myPointer.className += ' HTMLCS-pointer-hidden';
}
if (btnPointTo) {
btnPointTo.className += ' disabled';
}
} else {
if (btnPointTo) {
btnPointTo.className = btnPointTo.className.replace(' disabled', '');
}
pointer.pointTo(msg.element);
}
};
var loadStandards = function(standards, callback) {
if (standards.length === 0) {
callback.call(this);
return;
}
var standard = standards.shift();
HTMLCS.loadStandard(standard, function() {
loadStandards(standards, callback);
});
};
/**
* Includes the specified JS file.
*
* @param {String} src The URL to the JS file.
* @param {Function} callback The function to call once the script is loaded.
*/
var _includeScript = function(src, callback) {
var script = document.createElement('script');
script.onload = function() {
script.onload = null;
script.onreadystatechange = null;
if ((callback instanceof Function) === true) {
callback.call(this);
}
};
script.onreadystatechange = function() {
if (/^(complete|loaded)$/.test(this.readyState) === true) {
script.onreadystatechange = null;
script.onload();
}
};
script.src = src;
if (document.head) {
document.head.appendChild(script);
} else {
document.getElementsByTagName('head')[0].appendChild(script);
}
};
this.setOption = function(name, value) {
_options[name] = value;
};
this.getIssue = function(issueNumber)
{
return _messages[issueNumber];
};
this.countIssues = function(messages)
{
var counts = {
error: 0,
warning: 0,
notice: 0
};
for (var i = 0; i < messages.length; i++) {
switch (messages[i].type) {
case HTMLCS.ERROR:
counts.error++;
break;
case HTMLCS.WARNING:
counts.warning++;
break;
case HTMLCS.NOTICE:
counts.notice++;
break;
}//end switch
}//end for
return counts;
};
this.build = function(standard, messages, options) {
var wrapper = null;
if (_doc) {
var wrapper = _doc.getElementById(_prefix + 'wrapper');
}
var errors = 0;
var warnings = 0;
var notices = 0;
for (var i = 0; i < messages.length; i++) {
// Filter only the wanted error types.
var ignore = false;
switch (messages[i].type) {
case HTMLCS.ERROR:
if (_options.show.error === false) {
ignore = true;
} else {
errors++;
}
break;
case HTMLCS.WARNING:
if (_options.show.warning === false) {
ignore = true;
} else {
warnings++;
}
break;
case HTMLCS.NOTICE:
if (_options.show.notice === false) {
ignore = true;
} else {
notices++;
}
break;
}//end switch
if (ignore === true) {
messages.splice(i, 1);
i--;
}
}//end for
_messages = messages;
var settingsContents = '';
var summaryContents = '';
var detailContents = '';
for (var i = 0; i < messages.length; i++) {
if ((i % 5) === 0) {
summaryContents += '<ol class="HTMLCS-issue-list"';
if (i === 0) {
summaryContents += 'style="margin-left: 0em"';
}
summaryContents += '>';
}
summaryContents += buildMessageSummary(i, messages[i]);
if (((i % 5) === 4) || (i === (messages.length - 1))) {
summaryContents += '</ol>';
}
detailContents += buildMessageDetail(i, messages[i], standard);
}
var detailWidth = (i * 300);
var wrapper = _doc.createElement('div');
wrapper.id = _prefix + 'wrapper';
wrapper.className = 'showing-issue-list';
if (_options.noHeader !== true) {
var header = buildHeaderSection(standard, wrapper);
wrapper.appendChild(header);
}
var summary = buildSummarySection(errors, warnings, notices);
var summaryDetail = buildDetailSummarySection(1, messages.length);
var innerWrapper = _doc.createElement('div');
innerWrapper.id = _prefix + 'issues-wrapper';
innerWrapper.className = _prefix + 'inner-wrapper';
var issueList = buildIssueListSection(messages);
innerWrapper.appendChild(issueList);
var totalPages = Math.ceil(messages.length / 5);
var navDiv = buildNavigation(1, totalPages);
innerWrapper.appendChild(navDiv);
var outerWrapper = _doc.createElement('div');
outerWrapper.className = _prefix + 'outer-wrapper';
outerWrapper.appendChild(innerWrapper);
var innerWrapper = _doc.createElement('div');
innerWrapper.id = _prefix + 'issues-detail-wrapper';
innerWrapper.className = _prefix + 'inner-wrapper';
var issueDetail = buildIssueDetailSection(messages);
innerWrapper.appendChild(issueDetail);
outerWrapper.appendChild(innerWrapper);
wrapper.appendChild(summary);
wrapper.appendChild(summaryDetail);
wrapper.appendChild(outerWrapper);
return wrapper;
};
this.buildSummaryPage = function() {
var wrapper = _doc.createElement('div');
wrapper.id = _prefix + 'wrapper';
wrapper.className = 'showing-settings';
if (_options.noHeader !== true) {
var header = buildHeaderSection(_standard, wrapper);
wrapper.appendChild(header);
}
var summary = buildSettingsSection();
wrapper.appendChild(summary);
return wrapper;
};
this.changeScreen = function(screen) {
var wrapper = _doc.getElementById(_prefix + 'wrapper');
// Remove current "showing" section, add new one, then clean up the class name.
wrapper.className = wrapper.className.replace(new RegExp('showing-' + _screen), '');
wrapper.className += ' showing-' + screen;
wrapper.className = wrapper.className.replace(/\s+/, ' ');
_screen = screen;
};
this.includeCss = function(prefix, doc) {
if (_options.includeCss === false) {
return;
}
if (doc === undefined) {
doc = _doc;
}
var head = doc.querySelector('head');
var exLinks = head.getElementsByTagName('link');
var foundCss = false;
for (var i = 0; i < exLinks.length; i++) {
if (new RegExp(prefix + '\.css').test(exLinks[i].getAttribute('href')) === true) {
foundCss = true;
break;
}
}
if (foundCss === false) {
var cssLink = doc.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
cssLink.href = _options.path + prefix + '.css';
head.appendChild(cssLink);
}
};
this.getStandardList = function() {
var pattern = /^HTMLCS_[^_]+$/;
var standards = [];
for (i in window) {
if (pattern.test(i) === true) {
var standard = window[i];
if (standard.sniffs && standard.name) {
standards.push(i.substr(7));
}
}
}
return standards;
};
this.getParentElement = function() {
var parentEl = null;
if (_options.parentElement) {
parentEl = _options.parentElement;
} else if (_top.frames.length > 0) {
var largestFrameSize = -1;
var largestFrame = null;
for (var i = 0; i < _top.frames.length; i++) {
try {
if (window.frames[i].frameElement.nodeName.toLowerCase() === 'frame') {
if (window.frames[i].document) {
var frameSize = window.frames[i].innerWidth * window.frames[i].innerHeight;
if (frameSize > largestFrameSize) {
largestFrameSize = frameSize;
largestFrame = window.frames[i].document.body;
}
}//end if
}//end if
} catch (ex) {
// Skip cross-domain frames. Can't do much about those.
}//end try
}//end for
if (largestFrame === null) {
// They're all iframes. Just use the main document body.
parentEl = document.body;
} else {
parentEl = largestFrame;
}
} else {
parentEl = document.body;