From a410249671fd93948550c9469a6bd10d709057a7 Mon Sep 17 00:00:00 2001 From: Trevor Elliott Date: Wed, 31 Jul 2013 15:20:09 -0700 Subject: Rework the UI * Remove a dependency on jquery-ui for the injected interface * New, simpler injected ui --- src/js/fiveui/build.mk | 17 +- src/js/fiveui/css/ui.css | 116 +++++++ src/js/fiveui/injected/compute.js | 2 +- src/js/fiveui/injected/jquery-plugins.js | 2 + src/js/fiveui/injected/prelude.js | 4 +- src/js/fiveui/injected/ui.js | 526 +++++++++++++++++++++---------- src/js/fiveui/js/background.js | 2 + 7 files changed, 492 insertions(+), 177 deletions(-) create mode 100644 src/js/fiveui/css/ui.css (limited to 'src/js/fiveui') diff --git a/src/js/fiveui/build.mk b/src/js/fiveui/build.mk index e229eed..3679a95 100644 --- a/src/js/fiveui/build.mk +++ b/src/js/fiveui/build.mk @@ -45,7 +45,8 @@ $2: $(patsubst $(fiveui-dir)/%,$1/data/%,$(wildcard $(fiveui-dir)/js/*)) $(call fiveui-files,$1/data,css) -$2: $1/data/css/options.css +$2: $1/data/css/options.css \ + $1/data/css/ui.css $(call fiveui-files,$1/data,images) @@ -69,6 +70,20 @@ $2: $1/data/jquery/jquery-1.8.3.js \ $1/data/jquery/bundled.css +# font awesome +$1/data/font-awesome/css: | $1/data/font-awesome + $$(call cmd,mkdir) + +$1/data/font-awesome/css/%: $(lib-dir)/font-awesome/css/% \ + | $1/data/font-awesome/css + $$(call cmd,cp) + +$1/data/font-awesome: | $1/data + $$(call cmd,mkdir) + +$2: $1/data/font-awesome/css/font-awesome.css + + # simple libraries $1/data/%: $(lib-dir)/% | $1/data $$(call cmd,cp) diff --git a/src/js/fiveui/css/ui.css b/src/js/fiveui/css/ui.css new file mode 100644 index 0000000..0b723c9 --- /dev/null +++ b/src/js/fiveui/css/ui.css @@ -0,0 +1,116 @@ + + +div.fiveui { + background-color: #e1e1e1; + min-width: 400px; + min-height: 300px; + width: 400px; + height: 300px; + position: absolute; + z-index: 1000; + border: 1px solid black; + resize: both; + overflow: hidden; +} + +div.fiveui>* { + font-family: Arial; + padding: 4px; +} + +div.fiveui>div.fiveui-titlebar { + background-color: #366689; + color: #fff; + font-weight: bold; + cursor: hand; + cursor: pointer; + border-bottom: 1px solid black; +} + +div.fiveui-titlebar>div.fiveui-close { + position: absolute; + top: 0; + right: 0; + padding: 0.2em; + width: 1em; +} + +div.fiveui>div.fiveui-titlebar:active{ + cursor: hand; + cursor: pointer; +} + + +div.fiveui>div.fiveui-controls { + padding: 0; +} + +div.fiveui-control { + background-color: #8ea548; + color: #fff; + font-family: Awesome; + display: inline; + border-right: 1px solid black; + margin: 0; + cursor: hand; + cursor: pointer; + float: left; + padding: 4px; + width: 1.5em; + text-align: center; +} + +div.fiveui-control:active { + background-color: #366689; +} + +div.fiveui-problems { + overflow-y: auto; + overflow-x: hidden; + width: 100%; + border-top: 1px solid black; + margin: 0; + padding: 0; + resize: none; +} + +div.fiveui-stats { + background-color: #fff; + position: absolute; + width: 100%; + bottom: 10px; + left: 0px; + border-top: 1px solid black; + border-bottom: 1px solid black; +} + +div.fiveui-problem { + border-bottom: 1px solid black; + padding: 0; + margin: 0; + cursor: hand; + cursor: pointer; +} + +div.fiveui-problem-header { + padding: 4px; + font-size: 1.1em; +} + +div.fiveui-problem-toggle { + float: left; + width: 1em; + padding-left: 4px; +} + +div.fiveui-problem-body { + padding: 4px; +} + +div.fiveui-severity-0 { + background-color: #F2FC7E; +} + +div.fiveui-severity-1 { + background-color: #F5B3B3; +} diff --git a/src/js/fiveui/injected/compute.js b/src/js/fiveui/injected/compute.js index 921229e..9fc16a8 100644 --- a/src/js/fiveui/injected/compute.js +++ b/src/js/fiveui/injected/compute.js @@ -283,7 +283,7 @@ * @param {DOMNode} elt */ var underFiveUI = function(elt) { - var ancestor = $(elt).parentsUntil('#fiveui-top', 'body'); + var ancestor = $(elt).parentsUntil('.fiveui', 'body'); return ancestor.length == 0; }; diff --git a/src/js/fiveui/injected/jquery-plugins.js b/src/js/fiveui/injected/jquery-plugins.js index d210de6..309c566 100644 --- a/src/js/fiveui/injected/jquery-plugins.js +++ b/src/js/fiveui/injected/jquery-plugins.js @@ -19,6 +19,8 @@ * limitations under the License. */ +var fiveui = fiveui || {}; + /** * This module provides several useful jQuery plugins related to checking and reporting * UI consistency issues. diff --git a/src/js/fiveui/injected/prelude.js b/src/js/fiveui/injected/prelude.js index db32d7f..cb1422a 100644 --- a/src/js/fiveui/injected/prelude.js +++ b/src/js/fiveui/injected/prelude.js @@ -81,8 +81,8 @@ fiveui.query = function (sel, context) { } ); - $filteredResults = $results.not('#fiveui-top') - .not('#fiveui-top *') + $filteredResults = $results.not('.fiveui') + .not('.fiveui *') .filter(':visible'); // update global stats diff --git a/src/js/fiveui/injected/ui.js b/src/js/fiveui/injected/ui.js index 1eecd93..e02d83d 100644 --- a/src/js/fiveui/injected/ui.js +++ b/src/js/fiveui/injected/ui.js @@ -20,13 +20,363 @@ */ (function(){ + /** * Storage namespace for in-browser logic */ var core = {}; core.port = obtainPort(); - core.ui = $('
'); + + /* User Interface **********************************************************/ + + core.UI = function() { + this._initialize.apply(this, arguments); + }; + + + _.extend(core.UI, { + + /** + * Template for the UI dialog + */ + uiTemplate:_.template( + [ '
' + , '
' + , ' FiveUI
' + , '
' + , '
' + , '
' + , '
' + , '
' + , '
' + , '
' + , '
' + ].join('')), + + /** + * Template for the stats panel of the UI dialog. + */ + statsTemplate:_.template( + [ '' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , ' ' + , '
rules checked:<%= numRules %>
elements checked:<%= numElts %>
elapsed time (ms):<%= time %>
' + ].join('')), + + }); + + _.extend(core.UI.prototype, { + + /** + * Create the UI, and attach all event handlers. + * @private + */ + _initialize:function(opts) { + + // apply options + var optNames = [ 'port' ]; + _.defaults(this, _.pick(opts, optNames)); + + this.$el = $(core.UI.uiTemplate()); + this.$problems = this.$el.find('.fiveui-problems'); + this.$stats = this.$el.find('.fiveui-stats'); + + this._setupButtons(); + this._setupDragDrop(); + + // force the resize event + this.height = 0; + this._pollResize(); + + this._registerBackendListeners(); + + // initially, keep the window hidden + this.$el.hide(); + }, + + /** + * Setup the functionality of the close button on the ui + * @private + */ + _setupButtons:function() { + + var close = this.$el.find('.fiveui-close'); + close.on('click.fiveui', _.bind(this.hide, this)); + + var clear = this.$el.find('.fiveui-clear'); + clear.on('click.fiveui', _.bind(this.clearProblems, this)); + + // note, this only works in chrome + var pause = this.$el.find('.fiveui-break'); + pause.on('click.fiveui', function() { + debugger; + }); + + }, + + /** + * Setup the drag and drop functionality for the problems window. + * @private + */ + _setupDragDrop:function() { + + var self = this; + var header = this.$el.find('.fiveui-titlebar'); + var offset = { x: 0, y: 0 }; + + // update the location of the ui + var mouseMove = function(e) { + self.$el.css({ + left: e.originalEvent.clientX + offset.x, + top: e.originalEvent.clientY + offset.y, + }); + }; + + var cancel = function(e) { + e.stopPropagation(); + }; + + // both of these will cause funny things to happen with the text of the title + // bar. + header.on('dragstart', cancel); + header.on('selectstart', cancel); + + // figure out how far the cursor is from the top-left of the ui + header.on('mousedown.fiveui', function(e) { + + // prevent the close button from being used as a drag handle + if(e.target != header[0]) { + return false; + } + + var pos = self.$el.position(); + offset.x = pos.left - e.originalEvent.clientX; + offset.y = pos.top - e.originalEvent.clientY; + + $(window).on('mousemove.fiveui', mouseMove); + header.one('mouseup.fiveui', function() { + $(window).off('mousemove.fiveui', mouseMove); + + // deliver the new position to teh backend + self.port.emit('Position', self.$el.position()); + }); + }); + + }, + + _pollResize:function() { + + var height = this.$el.height(); + + if(height != this.height) { + + this.height = height; + + var ppos = this.$problems.position(); + var spos = this.$stats.position(); + + this.$problems.height(spos.top - ppos.top) + + // notify the backend about the new height + this.port.emit('Size', { + width: this.$el.width(), + height: this.$el.height() + }); + } + + setTimeout(_.bind(this._pollResize, this), 100); + }, + + /** + * Setup listeners to the backend. + */ + _registerBackendListeners:function() { + + var self = this; + + this.port.on('ShowUI', function(unused) { + self.show(); + }); + + this.port.on('ShowProblem', _.bind(this.addProblem, this)); + + this.port.on('ShowStats', _.bind(this.renderStats, this)); + + // initialize/create the ui, set its position and size + this.port.on('RestoreUI', function(state) { + + // set the position and size + self.$el.css({ + 'top': state.winState.y, + 'left': state.winState.x, + 'width': state.winState.width + 'px', + 'height': state.winState.height + 'px' + }); + + // optionally show the window + if(!state.winState.closed) { + self.show(); + } + + // add all problems + _.each(state.problems, _.bind(self.addProblem, self)); + + // render stats + self.renderStats(state.stats); + + }); + }, + + /** + * Clear the problems list + * @public + */ + clearProblems:function() { + this.$el.find('.fiveui-problems').children().remove(); + this.port.emit('ClearProblems'); + }, + + /** + * Add an entry in the problems list. + * @public + */ + addProblem:function(problem) { + + var p = new core.Problem(problem); + p.appendTo(this.$el.find('.fiveui-problems')); + + }, + + /** + * Attach the UI to a jquery selector. + * @public + */ + appendTo:function(el) { + el.append(this.$el); + }, + + /** + * Hide the UI + * @public + */ + hide:function() { + this.$el.hide(); + this.port.emit('CloseUI'); + }, + + /** + * Show the UI + * @public + */ + show:function() { + this.$el.show(); + }, + + /** + * Render statistics + */ + renderStats:function(stats) { + + stats = stats || {}; + _.defaults(stats, { + numRules: 0, + numElts: 0, + start: 0, + end: 0, + }); + + stats.time = stats.end - stats.start; + + this.$stats.html(core.UI.statsTemplate(stats)); + }, + + }); + + + /** + * Entries in the problem list. + */ + core.Problem = function() { + this._initialize.apply(this, arguments); + }; + + _.extend(core.Problem, { + + /** + * Template for entries in the problems list + */ + problemTemplate:_.template( + [ '
' + , '
' + , '
' + , ' <%= name %>' + , '
' + , '
' + , '

<%= msg %>

' + , '

<%= xpath %>

' + , '
' + , '
' + ].join('')), + + }); + + _.extend(core.Problem.prototype, { + + _initialize:function(problem) { + + this.problem = problem; + + this.$el = $(core.Problem.problemTemplate(problem)); + this.$toggle = this.$el.find('.fiveui-problem-toggle'); + this.$body = this.$el.find('.fiveui-problem-body'); + this.$header = this.$el.find('.fiveui-problem-header'); + + this.$body.hide(); + + this.close(); + }, + + appendTo:function(el) { + el.append(this.$el); + }, + + /** + * Close the context for a problem entry. + * @public + */ + close:function() { + this.$toggle.find('span').removeClass('icon-caret-down') + .addClass('icon-caret-right'); + + this.$el.one('click', _.bind(this.open, this)); + this.$body.slideUp(100); + + core.maskProblem(this.problem); + }, + + open:function() { + this.$toggle.find('span').addClass('icon-caret-down') + .removeClass('icon-caret-right'); + + this.$el.one('click', _.bind(this.close, this)); + this.$body.slideDown(100); + + core.highlightProblem(this.problem); + }, + + }); + core.lockDepth = 0; @@ -113,33 +463,9 @@ } }; - 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 () { @@ -152,153 +478,7 @@ }); }; - 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); - }); - }; + core.win = new core.UI({ port: core.port }); - registerBackendListeners(core.port); + core.win.appendTo($('body')); })(); diff --git a/src/js/fiveui/js/background.js b/src/js/fiveui/js/background.js index dfe0649..79e2512 100644 --- a/src/js/fiveui/js/background.js +++ b/src/js/fiveui/js/background.js @@ -160,6 +160,8 @@ fiveui.Background.prototype.pageLoad = function(tabId, url, data) { var uiScripts = [ this.dataLoader('underscore.js') + , this.dataLoader('font-awesome/css/font-awesome.css') + , this.dataLoader('css/ui.css') , this.dataLoader('jquery/bundled.css') , this.dataLoader('jquery/jquery-1.8.3.js') , this.dataLoader('jquery/jquery-ui-1.9.2.custom.js') -- cgit v1.2.3