/*
* Module : injected/fiveui-injected-ui.js
* Copyright : (c) 2011-2012, Galois, Inc.
*
* Maintainer :
* Stability : Provisional
* Portability: Portable
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function(){
/**
* Storage namespace for in-browser logic
*/
var core = {};
core.port = obtainPort();
core.ui = $('
');
core.lockDepth = 0;
core.lockMask = function() {
core.lockDepth = core.lockDepth + 1;
if(core.lockDepth == 1) {
core.port.emit('MaskRules', null);
}
};
core.unlockMask = function() {
core.lockDepth = core.lockDepth - 1;
if(core.lockDepth == 0) {
core.port.emit('UnmaskRules', null);
}
};
/**
* Due to the lack of a confirmation continuation with the port api, this
* function runs a continuation after 10ms of delivering the MaskRules
* message, then waits another 10ms before delivering the UnmaskRules
* message. The two delays seem to give a better chance that the
* continuation runs within the masked context.
*/
core.maskRules = function(body) {
core.lockMask();
setTimeout(function() {
body();
setTimeout(function() {
core.unlockMask();
}, 10);
}, 10);
};
core.highlighted = {};
core.highlightProblem = function(prob) {
var obj = core.highlighted[prob.hash];
if(obj) {
// increment the number of times this has been highlighted
obj.highlighted = obj.highlighted + 1;
} else {
// add the rule to the list of highlighted elements, and change its style
// to look obvious.
var elt = fiveui.query('.' + prob.hash);
var oldStyle = elt.attr('style');
core.maskRules(function() {
elt.attr('style', 'background-color: rgba(255,0,0,0.3); background-image: none;');
elt.addClass('uic-problem');
});
// record the element for the future
core.highlighted[prob.hash] = {
highlighted: 1,
oldStyle: oldStyle,
}
}
};
core.maskProblem = function(prob) {
var obj = core.highlighted[prob.hash];
if(obj) {
obj.highlighted = obj.highlighted - 1;
if(obj.highlighted == 0) {
var elt = fiveui.query('.' + prob.hash);
// remove the fiveui style
core.maskRules(function() {
if (_.isEmpty(obj.oldStyle)) {
elt.removeAttr('style');
} else {
elt.attr('style', obj.oldStyle);
}
elt.removeClass('uic-problem');
});
delete core.highlighted[prob.hash];
}
}
};
core.renderStatsTemplate = _.template(
[ ''
, ' '
, ' rules checked: | '
, ' <%= numRules %> | '
, '
'
, ' '
, ' elements checked: | '
, ' <%= numElts %> | '
, '
'
, ' '
, ' elapsed time (ms): | '
, ' <%= time %> | '
, '
'
, '
'
].join(''));
core.renderStats = function (stats) {
// give stats some sane defaults.
stats = stats || {};
_.defaults(stats, {
numRules: 0,
numElts: 0,
start: 0,
end: 0,
});
core.maskRules(function () {
var statsDiv, statsDetail;
statsDiv = $('#fiveui-stats');
statsDiv.children().remove();
stats.time = stats.end - stats.start;
statsDiv.html(core.renderStatsTemplate(stats));
});
};
core.renderProblem = function(prob) {
core.maskRules(function() {
var probDiv = $('');
/** Problem Controls **/
var prControls = $('');
probDiv.append(prControls);
var prSeverity = $('');
prControls.append(prSeverity);
if (1 == prob.severity) {
prSeverity.addClass('prSeverity-err');
} else {
prSeverity.addClass('prSeverity-warn');
}
var prExpand = $('');
prControls.append(prExpand);
/** Problem Content **/
var prMessage = $('');
probDiv.append(prMessage);
var prTitle = $(''+prob.name+'
');
prMessage.append(prTitle);
var prDetails = $('');
prMessage.append(prDetails);
var prDescr = $(''+prob.descr+'
');
var prPath = $(''+prob.xpath+'
');
prDetails.append(prDescr);
if (prob.msg) {
var reportMsg = $(''+prob.msg+'
');
prDetails.append(reportMsg);
}
prDetails.append(prPath);
prDetails.hide();
$('#problemList').append(probDiv);
prExpand.click(
function() {
var elt = $(this);
if(elt.is('.prExpand-down')) {
elt.removeClass('prExpand-down')
.addClass('prExpand-right');
prDetails.hide();
core.maskProblem(prob);
} else {
elt.addClass('prExpand-down')
.removeClass('prExpand-right');
prDetails.show();
core.highlightProblem(prob);
}
return false;
});
});
};
var dragStop = function(evt,e) {
core.port.emit('Position', core.ui.parent().position());
};
var resizeStop = function(evt,e) {
core.port.emit('Size', { width: core.ui.width(), height: core.ui.height() });
};
var beforeClose = function(evt,e) {
core.port.emit('CloseUI');
};
var registerBackendListeners = function(port) {
port.on('ShowUI', function(unused) {
core.ui.dialog('open');
});
port.on('ShowProblem', function(problem) {
core.renderProblem(problem);
});
port.on('ShowStats', function(stats) {
core.renderStats(stats);
});
port.on('RestoreUI', function(state) {
core.ui.append($(''));
core.ui.append($(''));
var newDialog = core.ui.dialog({ title: 'FiveUI',
dragStop: dragStop,
resizeStop: resizeStop,
beforeClose: beforeClose,
position: [state.winState.x, state.winState.y],
width: state.winState.width,
height: state.winState.height,
autoOpen: false,
zIndex: 50000
});
newDialog.parent().attr('id', 'fiveui-top');
$('#controls').append($('')
.button({ label: 'clear' }));
$('#clearButton').click(function() {
$('#problemList').children().remove();
port.emit('ClearProblems');
core.renderStats();
$('prExpand-down').click();
// Just in case the click event on prExpand-down missde anything:
core.maskProblem(fiveui.query('.uic-problem'), undefined);
core.renderStats();
});
///////////////////////////////////////////
// Add a button that causes a debuger break.
//
// handy for playing with Jquery on the dom.
// Note: This only works in Google Chrome.
$('#controls').append($('')
.button({ label: 'break' }));
$('#breakButton').click(function() {
debugger; //
}); //
////////////////////////////////////////////
core.ui.append($(''));
if(!state.winState.closed) {
core.ui.dialog('open');
}
$(state.problems).each(function(ix,prob) {
core.renderProblem(prob);
});
core.renderStats(state.stats);
});
};
registerBackendListeners(core.port);
})();