diff mbox

[Branch,~linaro-validation/lava-scheduler/trunk] Rev 108: Overhaul of the job view

Message ID 20111215160717.7947.22159.launchpad@ackee.canonical.com
State Accepted
Headers show

Commit Message

Paul Larson Dec. 15, 2011, 4:07 p.m. UTC
Merge authors:
  Le Chi Thu le.chi.thu@linaro.org <le.chi.thu@linaro.org>
  Michael Hudson-Doyle (mwhudson)
Related merge proposals:
  https://code.launchpad.net/~mwhudson/lava-scheduler/redesign-job-view/+merge/85425
  proposed by: Michael Hudson-Doyle (mwhudson)
  review: Approve - Paul Larson (pwlars)
  review: Approve - Le Chi Thu (le-chi-thu)
  https://code.launchpad.net/~le-chi-thu/lava-scheduler/redesign-job-view/+merge/83493
  proposed by: Le Chi Thu (le-chi-thu)
  review: Resubmit - Le Chi Thu (le-chi-thu)
  review: Needs Fixing - Michael Hudson-Doyle (mwhudson)
  review: Needs Fixing - Zygmunt Krynicki (zkrynicki)
------------------------------------------------------------
revno: 108 [merge]
committer: Paul Larson <paul.larson@canonical.com>
branch nick: lava-scheduler
timestamp: Thu 2011-12-15 10:03:59 -0600
message:
  Overhaul of the job view
added:
  lava_scheduler_app/logfile_helper.py
  lava_scheduler_app/static/
  lava_scheduler_app/static/css/
  lava_scheduler_app/static/css/logfile.css
  lava_scheduler_app/static/css/scheduler.css
  lava_scheduler_app/static/css/shCore.css
  lava_scheduler_app/static/css/shThemeDefault.css
  lava_scheduler_app/static/images/
  lava_scheduler_app/static/images/ajax-progress.gif
  lava_scheduler_app/static/js/
  lava_scheduler_app/static/js/jQuery.Rule.js
  lava_scheduler_app/static/js/shBrushJScript.js
  lava_scheduler_app/static/js/shCore.js
  lava_scheduler_app/templates/lava_scheduler_app/job_definition.html
  lava_scheduler_app/templates/lava_scheduler_app/job_log_file.html
  lava_scheduler_app/templates/lava_scheduler_app/job_sidebar.html
modified:
  .bzrignore
  lava_scheduler_app/templates/lava_scheduler_app/alljobs.html
  lava_scheduler_app/templates/lava_scheduler_app/job.html
  lava_scheduler_app/urls.py
  lava_scheduler_app/views.py


--
lp:lava-scheduler
https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk

You are subscribed to branch lp:lava-scheduler.
To unsubscribe from this branch go to https://code.launchpad.net/~linaro-validation/lava-scheduler/trunk/+edit-subscription
diff mbox

Patch

=== modified file '.bzrignore'
--- .bzrignore	2011-07-26 05:31:17 +0000
+++ .bzrignore	2011-11-26 21:49:36 +0000
@@ -1,4 +1,5 @@ 
 *.egg-info
+.idea
 ./twistd.pid
 ./_trial_temp
 ./twisted/plugins/dropin.cache

=== added file 'lava_scheduler_app/logfile_helper.py'
--- lava_scheduler_app/logfile_helper.py	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/logfile_helper.py	2011-12-13 04:22:40 +0000
@@ -0,0 +1,86 @@ 
+import os
+import re
+
+def getDispatcherErrors(logfile):
+    if not logfile:
+        return "Log file is missing"
+    errors = ""
+    for line in logfile:
+        if line.find("CriticalError:") != -1 or \
+           line.find("Lava failed on test:") != -1 :
+            errors += line
+
+    return errors
+
+def getDispatcherLogSize(logfile):
+    if not logfile:
+        return 0
+    else:
+        logfile.seek(0, os.SEEK_END)
+        size = logfile.tell()
+        return size
+
+def getDispatcherLogMessages(logfile):
+    if not logfile:
+        return ('', "Log file is missing")
+
+    logs = []
+    log_prefix = '<LAVA_DISPATCHER>'
+    level_pattern = re.compile('....-..-.. ..:..:.. .. ([A-Z]+):')
+    for line in logfile:
+        if not line.startswith(log_prefix):
+            continue
+        line = line[len(log_prefix):].strip()
+        match = level_pattern.match(line)
+        if not match:
+            continue
+        if len(line) > 90:
+            line = line[:90] + '...'
+        logs.append((match.group(1), line))
+    return logs
+
+class Sections:
+    def __init__(self):
+        self.sections = []
+        self.cur_section_type = None
+        self.cur_section = []
+    def push(self, type, line):
+        if type != self.cur_section_type:
+            self.close()
+            self.cur_section_type = type
+        self.cur_section.append(line)
+    def close(self):
+        if self.cur_section_type is not None:
+            self.sections.append(
+                (self.cur_section_type,
+                 len(self.cur_section),
+                 ''.join(self.cur_section)))
+        self.cur_section_type = None
+        self.cur_section = []
+
+def formatLogFile(logfile):
+    if not logfile:
+        return [('log', 1, "Log file is missing")]
+
+    sections = Sections()
+
+    for line in logfile:
+        line = line.replace('\r', '')
+        if not line:
+            continue
+        if line == 'Traceback (most recent call last):\n':
+            sections.push('traceback', line)
+        elif sections.cur_section_type == 'traceback':
+            sections.push('traceback', line)
+            if not line.startswith(' '):
+                sections.close()
+            continue
+        elif line.find("<LAVA_DISPATCHER>") != -1 or \
+                 line.find("lava_dispatcher") != -1 or \
+                 line.find("CriticalError:") != -1 :
+            sections.push('log', line)
+        else:
+            sections.push('console', line)
+    sections.close()
+
+    return sections.sections

=== added directory 'lava_scheduler_app/static'
=== added directory 'lava_scheduler_app/static/css'
=== added file 'lava_scheduler_app/static/css/logfile.css'
--- lava_scheduler_app/static/css/logfile.css	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/css/logfile.css	2011-12-13 04:22:40 +0000
@@ -0,0 +1,26 @@ 
+
+#logfile_content
+{
+    margin-left: 5px;
+}
+
+.log_log
+{
+    color: #a23212;
+}
+
+.console_log
+{
+    margin-left: 10px;
+}
+.traceback_log
+{
+    color: #ee1111;
+}
+
+.clickable
+{
+    border-style: solid;
+    border-width: medium;
+}
+

=== added file 'lava_scheduler_app/static/css/scheduler.css'
--- lava_scheduler_app/static/css/scheduler.css	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/css/scheduler.css	2011-12-09 01:53:59 +0000
@@ -0,0 +1,43 @@ 
+.column {
+    position: relative;
+    float: left;
+    padding-right: 2em;
+    padding-bottom: 1em;
+    width: 20%;
+}
+#tab-definition {
+    width: 70%; 
+}
+
+#tab-definition pre {
+ overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ /* width: 99%; */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ max-height: 400px;
+}
+
+#tab-output pre {
+    max-height: 400px;
+    max-width: 800px;
+    margin: 25px;
+}
+
+.skip {
+    color:red;
+}
+
+#dispatcher-error pre {
+    color: red;
+}
+
+pre.log {
+    margin: 0;
+}
+
+.logbuttons .ui-button-text {
+    padding: 0.1em 0.4em;
+}
\ No newline at end of file

=== added file 'lava_scheduler_app/static/css/shCore.css'
--- lava_scheduler_app/static/css/shCore.css	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/css/shCore.css	2011-11-26 21:49:36 +0000
@@ -0,0 +1,226 @@ 
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}

=== added file 'lava_scheduler_app/static/css/shThemeDefault.css'
--- lava_scheduler_app/static/css/shThemeDefault.css	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/css/shThemeDefault.css	2011-11-26 21:49:36 +0000
@@ -0,0 +1,117 @@ 
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: black !important;
+}
+.syntaxhighlighter table caption {
+  color: black !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #6ce26c !important;
+  color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: blue !important;
+  background: white !important;
+  border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: red !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #6ce26c !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: blue !important;
+}
+.syntaxhighlighter .keyword {
+  color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #006699 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}

=== added directory 'lava_scheduler_app/static/images'
=== added file 'lava_scheduler_app/static/images/ajax-progress.gif'
Binary files lava_scheduler_app/static/images/ajax-progress.gif	1970-01-01 00:00:00 +0000 and lava_scheduler_app/static/images/ajax-progress.gif	2011-12-12 04:38:14 +0000 differ
=== added directory 'lava_scheduler_app/static/js'
=== added file 'lava_scheduler_app/static/js/jQuery.Rule.js'
--- lava_scheduler_app/static/js/jQuery.Rule.js	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/js/jQuery.Rule.js	2011-12-09 04:45:32 +0000
@@ -0,0 +1,273 @@ 
+/*!
+ * jQuery.Rule - Css Rules manipulation, the jQuery way.
+ * Copyright (c) 2007-2011 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Dual licensed under MIT and GPL.
+ * Date: 02/7/2011
+ * Compatible with jQuery 1.2+
+ *
+ * @author Ariel Flesler
+ * @version 1.0.2
+ *
+ * @id jQuery.rule
+ * @param {Undefined|String|jQuery.Rule} The rules, can be a selector, or literal CSS rules. Many can be given, comma separated.
+ * @param {Undefined|String|DOMElement|jQuery) The context stylesheets, all of them by default.
+ * @return {jQuery.Rule} Returns a jQuery.Rule object.
+ *
+ * @example $.rule('p,div').filter(function(){ return this.style.display != 'block'; }).remove();
+ *
+ * @example $.rule('div{ padding:20px;background:#CCC}, p{ border:1px red solid; }').appendTo('style');
+ *
+ * @example $.rule('div{}').append('margin:40px').css('margin-left',0).appendTo('link:eq(1)');
+ *
+ * @example $.rule().not('div, p.magic').fadeOut('slow');
+ *
+ * @example var text = $.rule('#screen h2').add('h4').end().eq(4).text();
+ */
+;(function( $ ){	
+	
+   /**
+	* Notes
+	*	Some styles and animations might fail, please report it.
+	*	The plugin needs a style node to stay in the DOM all along to temporarily hold rules. DON'T TOUCH IT.
+	*	Opera requires this style to have alternate in the rel to allow disabling it.
+	*	Rules in IE don't have .parentStylesheet. We need to find it each time(slow).
+	*	Animations need close attention. Programatically knowing which rule has precedence, would require a LOT of work.
+	*	This plugin adds $.rule and also 4 methods to $.fn: ownerNode, sheet, cssRules and cssText
+	*	Note that rules are not directly inside nodes, you need to do: $('style').sheet().cssRules().
+	*/
+	
+	var storageNode = $('<style rel="alternate stylesheet" type="text/css" />').appendTo('head')[0],//we must append to get a stylesheet
+		sheet = storageNode.sheet ? 'sheet' : 'styleSheet',
+		storage = storageNode[sheet],//css rules must remain in a stylesheet for IE and FF
+		rules = storage.rules ? 'rules' : 'cssRules',
+		remove = storage.deleteRule ? 'deleteRule' : 'removeRule',
+		owner = storage.ownerNode ? 'ownerNode' : 'owningElement',		
+		reRule = /^([^{]+)\{([^}]*)\}/m,
+		reStyle = /([^:]+):([^;}]+)/;	
+
+//	storage.disabled = true;//let's ignore your rules 
+	
+	var $rule = $.rule = function( r, c ){
+		if(!(this instanceof $rule))
+			return new $rule( r, c );
+
+		this.sheets = $rule.sheets(c);
+		if( r && reRule.test(r) )
+			r = $rule.clean( r );
+		if( typeof r == 'object' && !r.exec ) {
+			setArray( this, r.get ? r.get() : r.splice ? r : [r] );
+		} else {
+			setArray( this, this.sheets.cssRules().get() );
+			if (r)
+				return this.filter( r );
+		}
+		return this;
+	};
+	
+	$.extend( $rule, {
+		sheets:function( c ){
+			var o = c;
+			if( typeof o != 'object' )
+				o = $.makeArray(document.styleSheets);
+			o = $(o).not(storage);//skip our stylesheet
+			if( typeof c == 'string' )
+				o = o.ownerNode().filter(c).sheet();
+			return o;
+		},
+		rule:function( str ){
+			if( str.selectorText )/* * */
+				return [ '', str.selectorText, str.style.cssText ];
+			return reRule.exec( str );
+		},
+		appendTo:function( r, ss, skip ){
+			switch( typeof ss ){//find the desired stylesheet
+				case 'string': ss = this.sheets(ss);
+				case 'object':
+					if( ss[0] ) ss = ss[0];
+					if( ss[sheet] ) ss = ss[sheet];
+					if( ss[rules] ) break;//only if the stylesheet is valid
+				default:
+					if( typeof r == 'object' ) return r;//let's not waist time, it is parsed
+					ss = storage;
+			}
+			var p;
+			if( !skip && (p = this.parent(r)) )//if this is an actual rule, and it's appended.
+				r = this.remove( r, p );
+				
+			var rule = this.rule( r );			
+			if( ss.addRule )
+				ss.addRule( rule[1], rule[2]||';' );//IE won't allow empty rules
+			else if( ss.insertRule )
+				ss.insertRule( rule[1] + '{'+ rule[2] +'}', ss[rules].length );
+			
+			return ss[rules][ ss[rules].length - 1 ];//return the added/parsed rule
+		},
+		remove:function( r, p ){
+			p = p || this.parent(r);
+			if( p != storage ){//let's save some unnecesary cycles.
+				var i = p ? $.inArray( r, p[rules] ) : -1;
+				if( i != -1 ){//if not stored before removal, IE will crash eventually, and some rules in FF get messed up
+					r = this.appendTo( r, 0 /*storage*/, true );//is faster and shorter to imply storage
+					p[remove](i);
+				}
+			}
+			return r;
+		},
+		clean:function( r ){
+			return $.map( r.split('}'), function( txt ){
+				if( txt )
+					return $rule.appendTo( txt + '}' /*, storage*/ );//parse the string, storage implied
+			});
+		},
+		parent:function( r ){//CSS rules in IE don't have parentStyleSheet attribute
+			if( typeof r == 'string' || !$.browser.msie )//if it's a string, just return undefined.
+				return r.parentStyleSheet;
+
+			var par;
+			this.sheets().each(function(){
+				if( $.inArray(r, this[rules]) != -1 ){
+					par = this;	
+					return false;
+				}
+			});
+			return par;
+		},
+		outerText:function( rule ){
+			return !rule || !rule.selectorText ? '' : [rule.selectorText+'{', '\t'+rule.style.cssText,'}'].join('\n').toLowerCase();
+		},
+		text:function( rule, txt ){
+			if( txt !== undefined )
+				rule.style.cssText = txt;
+			return !rule ? '' : rule.style.cssText.toLowerCase();
+		}
+	});
+	
+	$rule.fn = $rule.prototype = {
+		pushStack:function( rs, sh ){
+			var ret = $rule( rs, sh || this.sheets );
+			ret.prevObject = this;
+			return ret;
+		},
+		end:function(){
+			return this.prevObject || $rule(0,[]);
+		},
+		filter:function( s ){
+			var o;
+			if( !s ) s = /./;//just keep them all.
+			if( s.split ){
+				o = $.trim(s).toLowerCase().split(/\s*,\s*/);
+				s = function(){
+					var s = this.selectorText || '';
+					return !!$.grep( s.toLowerCase().split(/\s*,\s*/), function( sel ){
+						return $.inArray( sel, o ) != -1;
+					}).length;
+				};
+			}else if( s.exec ){//string regex, or actual regex
+				o = s;
+				s = function(){ return o.test(this.selectorText); };
+			}
+			return this.pushStack($.grep( this, function( e, i ){
+				return s.call( e, i );
+			}));
+		},
+		add:function( rs, c ){
+			return this.pushStack( $.merge(this.get(), $rule(rs, c)) );	
+		},
+		is:function( s ){
+			return !!(s && this.filter( s ).length);
+		},
+		not:function( n, c ){
+			n = $rule( n, c );
+			return this.filter(function(){
+				return $.inArray( this, n ) == -1;
+			});
+		},
+		append:function( s ){
+			var rules = this, rule;
+			$.each( s.split(/\s*;\s*/),function(i,v){
+				if(( rule = reStyle.exec( v ) ))
+					rules.css( rule[1], rule[2] );
+			});
+			return this;
+		},
+		text:function( txt ){
+			return !arguments.length ? $rule.text( this[0] )
+				: this.each(function(){	$rule.text( this, txt ); });
+		},
+		outerText:function(){
+			return $rule.outerText(this[0]);	
+		}
+	};
+	
+	$.each({
+		ownerNode:owner,//when having the stylesheet, get the node that contains it
+		sheet:sheet, //get the stylesheet from the node
+		cssRules:rules //get the rules from the stylesheet.
+	},function( m, a ){
+		var many = a == rules;//the rules need some more processing
+		$.fn[m] = function(){
+			return this.map(function(){
+				return many ? $.makeArray(this[a]) : this[a];
+			});
+		};
+	});
+	
+	$.fn.cssText = function(){
+		return this.filter('link,style').eq(0).sheet().cssRules().map(function(){
+			return $rule.outerText(this);							   
+		}).get().join('\n');
+	};
+	
+	$.each('remove,appendTo,parent'.split(','),function( k, f ){
+		$rule.fn[f] = function(){
+			var args = $.makeArray(arguments), that = this;
+			args.unshift(0);
+			return this.each(function( i ){
+				args[0] = this;
+				that[i] = $rule[f].apply( $rule, args ) || that[i];
+			});
+		};
+	});
+		
+	$.each(('each,index,get,size,eq,slice,map,attr,andSelf,css,show,hide,toggle,'+
+			'queue,dequeue,stop,animate,fadeIn,fadeOut,fadeTo').split(','),function( k, f ){
+		$rule.fn[f] = $.fn[f];																				  
+	});
+	
+	// this function has been pulled in from jQuery 1.4.1, because it is an internal function and has been dropped as of 1.4.2
+	function setArray(rule, elems) { 
+		rule.length = 0;
+		Array.prototype.push.apply( rule, elems );
+	}
+	
+	var curCSS = $.curCSS;
+	$.curCSS = function( e, a ){//this hack is still quite exprimental
+		return ('selectorText' in e ) ?
+			e.style[a] || $.prop( e, a=='opacity'? 1 : 0,'curCSS', 0, a )//TODO: improve these defaults
+		: curCSS.apply(this,arguments);
+	};
+	
+	/**
+	 * Time to hack jQuery.data for animations.
+	 * Only IE really needs this, but to keep the behavior consistent, I'll hack it for all browsers.
+	 * TODO: This kind of id doesn't seem to be good enough
+	 * TODO: Avoid animating similar rules simultaneously
+	 * TODO: Avoid rules' precedence from interfering on animations ?
+	 */
+	$rule.cache = {};
+	var mediator = function( original ){
+		return function( elm ){
+			var id = elm.selectorText;
+			if( id )
+				arguments[0] = $rule.cache[id] = $rule.cache[id] || {};
+			return original.apply( $, arguments );	
+		};
+	};
+	$.data = mediator( $.data );
+	$.removeData = mediator( $.removeData );
+	
+	$(window).unload(function(){
+		$(storage).cssRules().remove();//empty our rules bin
+	});
+		
+})( jQuery );

=== added file 'lava_scheduler_app/static/js/shBrushJScript.js'
--- lava_scheduler_app/static/js/shBrushJScript.js	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/js/shBrushJScript.js	2011-11-26 21:49:36 +0000
@@ -0,0 +1,52 @@ 
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'break case catch continue ' +
+						'default delete do else false  ' +
+						'for function if in instanceof ' +
+						'new null return super switch ' +
+						'this throw true try typeof var while with'
+						;
+
+		var r = SyntaxHighlighter.regexLib;
+		
+		this.regexList = [
+			{ regex: r.multiLineDoubleQuotedString,					css: 'string' },			// double quoted strings
+			{ regex: r.multiLineSingleQuotedString,					css: 'string' },			// single quoted strings
+			{ regex: r.singleLineCComments,							css: 'comments' },			// one line comments
+			{ regex: r.multiLineCComments,							css: 'comments' },			// multiline comments
+			{ regex: /\s*#.*/gm,									css: 'preprocessor' },		// preprocessor tags like #region and #endregion
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),	css: 'keyword' }			// keywords
+			];
+	
+		this.forHtmlScript(r.scriptScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['js', 'jscript', 'javascript'];
+
+	SyntaxHighlighter.brushes.JScript = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();

=== added file 'lava_scheduler_app/static/js/shCore.js'
--- lava_scheduler_app/static/js/shCore.js	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/static/js/shCore.js	2011-11-26 21:49:36 +0000
@@ -0,0 +1,17 @@ 
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a<f.L;a++)I(f[a]===e)H a;H-1}M=6(f,e){K a=[],b=M.1B,c=0,d,h;I(M.1R(f)){I(e!==1d)1S 3m("2a\'t 5r 5I 5F 5B 5C 15 5E 5p");H r(f)}I(v)1S 2U("2a\'t W 3l M 59 5m 5g 5x 5i");e=e||"";O(d={2N:11,19:[],2K:6(g){H e.1i(g)>-1},3d:6(g){e+=g}};c<f.L;)I(h=B(f,c,b,d)){a.U(h.3k);c+=h.1C[0].L||1}Y I(h=n.X.W(z[b],f.1a(c))){a.U(h[0]);c+=h[0].L}Y{h=f.3a(c);I(h==="[")b=M.2I;Y I(h==="]")b=M.1B;a.U(h);c++}a=15(a.1K(""),n.Q.W(e,w,""));a.1w={1m:f,19:d.2N?d.19:N};H a};M.3v="1.5.0";M.2I=1;M.1B=2;K C=/\\$(?:(\\d\\d?|[$&`\'])|{([$\\w]+)})/g,w=/[^5h]+|([\\s\\S])(?=[\\s\\S]*\\1)/g,A=/^(?:[?*+]|{\\d+(?:,\\d*)?})\\??/,v=11,u=[],n={X:15.Z.X,1A:15.Z.1A,1C:1r.Z.1C,Q:1r.Z.Q,1e:1r.Z.1e},x=n.X.W(/()??/,"")[1]===1d,D=6(){K f=/^/g;n.1A.W(f,"");H!f.12}(),y=6(){K f=/x/g;n.Q.W("x",f,"");H!f.12}(),E=15.Z.3n!==1d,z={};z[M.2I]=/^(?:\\\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S]))/;z[M.1B]=/^(?:\\\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\\d*|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S])|\\(\\?[:=!]|[?*+]\\?|{\\d+(?:,\\d*)?}\\??)/;M.1h=6(f,e,a,b){u.U({2q:r(f,"g"+(E?"y":"")),2b:e,3r:a||M.1B,2p:b||N})};M.2n=6(f,e){K a=f+"/"+(e||"");H M.2n[a]||(M.2n[a]=M(f,e))};M.3c=6(f){H r(f,"g")};M.5l=6(f){H f.Q(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,"\\\\$&")};M.5e=6(f,e,a,b){e=r(e,"g"+(b&&E?"y":""));e.12=a=a||0;f=e.X(f);H b?f&&f.P===a?f:N:f};M.3q=6(){M.1h=6(){1S 2U("2a\'t 55 1h 54 3q")}};M.1R=6(f){H 53.Z.1q.W(f)==="[2m 15]"};M.3p=6(f,e,a,b){O(K c=r(e,"g"),d=-1,h;h=c.X(f);){a.W(b,h,++d,f,c);c.12===h.P&&c.12++}I(e.1J)e.12=0};M.57=6(f,e){H 6 a(b,c){K d=e[c].1I?e[c]:{1I:e[c]},h=r(d.1I,"g"),g=[],i;O(i=0;i<b.L;i++)M.3p(b[i],h,6(k){g.U(d.3j?k[d.3j]||"":k[0])});H c===e.L-1||!g.L?g:a(g,c+1)}([f],0)};15.Z.1p=6(f,e){H J.X(e[0])};15.Z.W=6(f,e){H J.X(e)};15.Z.X=6(f){K e=n.X.1p(J,14),a;I(e){I(!x&&e.L>1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;b<e.L;b++)I(a=J.1w.19[b-1])e[a]=e[b];!D&&J.1J&&!e[0].L&&J.12>e.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;d<b.L;d++)I(b[d])14[0][b[d]]=14[d+1]}I(a&&f.1J)f.12=14[14.L-2]+14[0].L;H e.1p(N,14)});Y{c=J+"";c=n.Q.W(c,f,6(){K d=14;H n.Q.W(e,C,6(h,g,i){I(g)5b(g){24"$":H"$";24"&":H d[0];24"`":H d[d.L-1].1a(0,d[d.L-2]);24"\'":H d[d.L-1].1a(d[d.L-2]+d[0].L);5a:i="";g=+g;I(!g)H h;O(;g>d.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P<a.L&&3b.Z.U.1p(b,d.1a(1));h=d[0].L;c=f.12;I(b.L>=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a<d.L&&i==N;a++)i=p(d[a],b,c);H i}6 C(a,b){K c={},d;O(d 2g a)c[d]=a[d];O(d 2g b)c[d]=b[d];H c}6 w(a,b,c,d){6 h(g){g=g||1P.5y;I(!g.1F){g.1F=g.52;g.3N=6(){J.5w=11}}c.W(d||1P,g)}a.3g?a.3g("4U"+b,h):a.4y(b,h,11)}6 A(a,b){K c=e.1Y.2j,d=N;I(c==N){c={};O(K h 2g e.1U){K g=e.1U[h];d=g.4x;I(d!=N){g.1V=h.4w();O(g=0;g<d.L;g++)c[d[g]]=h}}e.1Y.2j=c}d=e.1U[c[a]];d==N&&b!=11&&1P.1X(e.13.1x.1X+(e.13.1x.3E+a));H d}6 v(a,b){O(K c=a.1e("\\n"),d=0;d<c.L;d++)c[d]=b(c[d],d);H c.1K("\\n")}6 u(a,b){I(a==N||a.L==0||a=="\\n")H a;a=a.Q(/</g,"&1y;");a=a.Q(/ {2,}/g,6(c){O(K d="",h=0;h<c.L-1;h++)d+=e.13.1W;H d+" "});I(b!=N)a=v(a,6(c){I(c.L==0)H"";K d="";c=c.Q(/^(&2s;| )+/,6(h){d=h;H""});I(c.L==0)H d;H d+\'<17 1g="\'+b+\'">\'+c+"</17>"});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+="                    ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.P<b.P)H-1;Y I(a.P>b.P)H 1;Y I(a.L<b.L)H-1;Y I(a.L>b.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'<a 2h="\'+c+\'">\'+c+"</a>"+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<a.L;c++)a[c].3s=="20"&&b.U(a[c]);H b}6 f(a){a=a.1F;K b=p(a,".20",R);a=p(a,".3O",R);K c=1E.4i("3t");I(!(!a||!b||p(a,"3t"))){B(b.1c);r(b,"1m");O(K d=a.3G,h=[],g=0;g<d.L;g++)h.U(d[g].4z||d[g].4A);h=h.1K("\\r");c.39(1E.4D(h));a.39(c);c.2C();c.4C();w(c,"4u",6(){c.2G.4E(c);b.1l=b.1l.Q("1m","")})}}I(1j 3F!="1d"&&1j M=="1d")M=3F("M").M;K e={2v:{"1g-27":"","2i-1s":1,"2z-1s-2t":11,1M:N,1t:N,"42-45":R,"43-22":4,1u:R,16:R,"3V-17":R,2l:11,"41-40":R,2k:11,"1z-1k":11},13:{1W:"&2s;",2M:R,46:11,44:11,34:"4n",1x:{21:"4o 1m",2P:"?",1X:"1v\\n\\n",3E:"4r\'t 4t 1D O: ",4g:"4m 4B\'t 51 O 1z-1k 4F: ",37:\'<!4T 1z 4S "-//4V//3H 4W 1.0 4Z//4Y" "1Z://2y.3L.3K/4X/3I/3H/3I-4P.4J"><1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v</1t></3J><3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;"><T 1L="2O-3D:3C;3w-32:1.6z;"><T 1L="25-22:6A-6E;">1v</T><T 1L="25-22:.6C;3w-6B:6R;"><T>3v 3.0.76 (72 73 3x)</T><T><a 2h="1Z://3u.2w/1v" 1F="38" 1L="2f:#3y">1Z://3u.2w/1v</a></T><T>70 17 6U 71.</T><T>6T 6X-3x 6Y 6D.</T></T><T>6t 61 60 J 1k, 5Z <a 2h="6u://2y.62.2w/63-66/65?64=5X-5W&5P=5O" 1L="2f:#3y">5R</a> 5V <2R/>5U 5T 5S!</T></T></3B></1z>\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'<T 1g="16">\',d=e.16.2x,h=d.2X,g=0;g<h.L;g++)c+=(d[h[g]].1H||b)(a,h[g]);c+="</T>";H c},2o:6(a,b,c){H\'<2W><a 2h="#" 1g="6e 6h\'+b+" "+b+\'">\'+c+"</a></2W>"},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h<c.L;h++)d.U(c[h]);c=d}c=c;d=[];I(e.13.2M)c=c.1O(z());I(c.L===0)H d;O(h=0;h<c.L;h++){O(K g=c[h],i=a,k=c[h].1l,j=3W 0,l={},m=1f M("^\\\\[(?<2V>(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g<c.L;g++){b=c[g];K i=b.1F,k=b.1n,j=k.1D,l;I(j!=N){I(k["1z-1k"]=="R"||e.2v["1z-1k"]==R){d=1f e.4l(j);j="4O"}Y I(d=A(j))d=1f d;Y 6H;l=i.3X;I(h.2M){l=l;K m=x(l),s=11;I(m.1i("<![6G[")==0){m=m.4h(9);s=R}K o=m.L;I(m.1i("]]\\>")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;m<j.L;m++)j[m].P+=l}K c=A(a),d,h=1f e.1U.5Y,g=J,i="2F 1H 2Q".1e(" ");I(c!=N){d=1f c;O(K k=0;k<i.L;k++)(6(){K j=i[k];g[j]=6(){H h[j].1p(h,14)}})();d.28==N?1P.1X(e.13.1x.1X+(e.13.1x.4g+a)):h.2J.U({1I:d.28.17,2D:6(j){O(K l=j.17,m=[],s=d.2J,o=j.P+j.18.L,F=d.28,q,G=0;G<s.L;G++){q=y(l,s[G]);b(q,o);m=m.1O(q)}I(F.18!=N&&j.18!=N){q=y(j.18,F.18);b(q,j.P);m=m.1O(q)}I(F.1b!=N&&j.1b!=N){q=y(j.1b,F.1b);b(q,j.P+j[0].5Q(j.1b));m=m.1O(q)}O(j=0;j<m.L;j++)m[j].1V=c.1V;H m}})}};e.4j=6(){};e.4j.Z={V:6(a,b){K c=J.1n[a];c=c==N?b:c;K d={"R":R,"11":11}[c];H d==N?c:d},3Y:6(a){H 1E.4i(a)},4c:6(a,b){K c=[];I(a!=N)O(K d=0;d<a.L;d++)I(1j a[d]=="2m")c=c.1O(y(b,a[d]));H J.4e(c.6b(D))},4e:6(a){O(K b=0;b<a.L;b++)I(a[b]!==N)O(K c=a[b],d=c.P+c.L,h=b+1;h<a.L&&a[b]!==N;h++){K g=a[h];I(g!==N)I(g.P>d)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P<d)a[h]=N}H a},4d:6(a){K b=[],c=2u(J.V("2i-1s"));v(a,6(d,h){b.U(h+c)});H b},3U:6(a){K b=J.V("1M",[]);I(1j b!="2m"&&b.U==N)b=[b];a:{a=a.1q();K c=3W 0;O(c=c=1Q.6c(c||0,0);c<b.L;c++)I(b[c]==a){b=c;1N a}b=-1}H b!=-1},2r:6(a,b,c){a=["1s","6i"+b,"P"+a,"6r"+(b%2==0?1:2).1q()];J.3U(b)&&a.U("67");b==0&&a.U("1N");H\'<T 1g="\'+a.1K(" ")+\'">\'+c+"</T>"},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i<d;i++){K k=b?b[i]:h+i,j;I(k==0)j=e.13.1W;Y{j=g;O(K l=k.1q();l.L<j;)l="0"+l;j=l}a=j;c+=J.2r(i,k,a)}H c},49:6(a,b){a=x(a);K c=a.1e("\\n");J.V("2z-1s-2t");K d=2u(J.V("2i-1s"));a="";O(K h=J.V("1D"),g=0;g<c.L;g++){K i=c[g],k=/^(&2s;|\\s)+/.X(i),j=N,l=b?b[g]:d+g;I(k!=N){j=k[0].1q();i=i.1o(j.L);j=j.Q(" ",e.13.1W)}i=x(i);I(i.L==0)i=e.13.1W;a+=J.2r(g,l,(j!=N?\'<17 1g="\'+h+\' 5N">\'+j+"</17>":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"</4a>":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i<b.L;i++){K k=b[i],j;I(!(k===N||k.L===0)){j=c(k);h+=u(a.1o(d,k.P-d),j+"48")+u(k.1T,j+k.23);d=k.P+k.L+(k.75||0)}}h+=u(a.1o(d),c()+"48");H h},1H:6(a){K b="",c=["20"],d;I(J.V("2k")==R)J.1n.16=J.1n.1u=11;1l="20";J.V("2l")==R&&c.U("47");I((1u=J.V("1u"))==11)c.U("6S");c.U(J.V("1g-27"));c.U(J.V("1D"));a=a.Q(/^[ ]*[\\n]+|[\\n]*[ ]*$/g,"").Q(/\\r/g," ");b=J.V("43-22");I(J.V("42-45")==R)a=n(a,b);Y{O(K h="",g=0;g<b;g++)h+=" ";a=a.Q(/\\t/g,h)}a=a;a:{b=a=a;h=/<2R\\s*\\/?>|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i<b.L&&g>0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i<b.L;i++)b[i]=b[i].1o(g);a=b.1K("\\n")}I(1u)d=J.4d(a);b=J.4c(J.2J,a);b=J.4b(a,b);b=J.49(b,d);I(J.V("41-40"))b=E(b);1j 2H!="1d"&&2H.3S&&2H.3S.1C(/5s/)&&c.U("5t");H b=\'<T 1c="\'+t(J.1c)+\'" 1g="\'+c.1K(" ")+\'">\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"</2d>":"")+\'<2d 1g="17"><T 1g="3O">\'+b+"</T></2d></3P></3T></3Z></T>"},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{}))

=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/alljobs.html'
--- lava_scheduler_app/templates/lava_scheduler_app/alljobs.html	2011-10-28 00:24:13 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/alljobs.html	2011-12-06 23:02:25 +0000
@@ -18,7 +18,7 @@ 
   <tbody>
     {% for job in jobs %}
     <tr>
-      <td><a href="{{ job.get_absolute_url }}">{{ job.id }}</a></td>
+      <td><a href="{{ job.get_absolute_url }}">{{ job.id }} </a></td>
       <td>{{ job.get_status_display }}</td>
       {% if job.actual_device %}
       <td><a href="{{ job.actual_device.get_absolute_url }}"

=== modified file 'lava_scheduler_app/templates/lava_scheduler_app/job.html'
--- lava_scheduler_app/templates/lava_scheduler_app/job.html	2011-10-28 00:24:13 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/job.html	2011-12-13 04:22:40 +0000
@@ -1,177 +1,101 @@ 
-{% extends "lava_scheduler_app/_content.html" %}
+{% extends "lava_scheduler_app/job_sidebar.html" %}
 
 {% block extrahead %}
 {{ block.super }}
-<style type="text/css">
-.column {
-    position: relative;
-    float: left;
-    padding-right: 2em;
-    padding-bottom: 1em;
-    width: 20%;
-}
-#tab-output pre {
-    margin: 0;
-}
-.skip {
-    color:red;
-}
-</style>
+<script type="text/javascript" src="{{ STATIC_URL }}lava_scheduler_app/js/jQuery.Rule.js"></script>
 {% endblock %}
 
+
 {% block content %}
-<h2>Job {{ job.pk }}</h2>
-
-{% if show_cancel %}
-<form style="display:inline; float:right" method="POST" 
-      action="{% url lava.scheduler.job.cancel job.pk %}">
-  {% csrf_token %}
-  <button id="cancel-button">Cancel</button>
-</form>
-{% endif %}
-
-<div id="columns">
-  <div class="column">
-    <dt>Submitted by:</dt>
-    <dd>{{ job.submitter }}</dd>
-
-    {% if job.requested_device %}
-    <dt>Requested device:</dt>
-    <dd>{{ job.requested_device }}</dd>
-    {% endif %}
-
-    {% if job.requested_device_type %}
-    <dt>Requested type:</dt>
-    <dd>{{ job.requested_device_type }}</dd>
-    {% endif %}
-
-    {% if job.description %}
-    <dt>Description:</dt>
-    <dd>{{ job.description }}</dd>
-    {% endif %}
-  </div>
-
-  <div class="column">
-    <dt>Status:</dt>
-    <dd><b>{{ job.get_status_display }}</b></dd>
-
-    {% if job.actual_device %}
-    <dt>On device:</dt>
-    <dd>{{ job.actual_device }}</dd>
-    {% endif %}
-
-    {% if job.results_link %}
-    <p style="text-align:center; font-weight: bold">
-      <a href="{{ job.results_link }}">
-        Results
-      </a>
-    </p>
-    {% endif %}
-  </div>
-
-  <div class="column">
-    <dt>Submitted at:</dt>
-    <dd>{{ job.submit_time }}</dd>
-
-    <dt>Started at:</dt>
-    <dd>{{ job.start_time|default:"not started" }}</dd>
-
-    <dt>Finished at:</dt>
-    <dd>{{ job.end_time|default:"not finished" }}</dd>
-  </div>
-  <div style="clear: both"></div>
-</div>
-
-<div id="tabs">
-  <ul>
-    <li><a href="#tab-definition">Job Definition</a></li>
-{% if log_file_present %}
-    <li><a href="#tab-output">Output</a></li>
-{% endif %}
-  </ul>
-  <div id="tab-definition" >
-    <pre style="overflow: auto;">{{ job.definition }}</pre>
-  </div>
-{% if log_file_present %}
-  <div id="tab-output">
-    <div style="max-height: 400px; overflow: auto;">
-      <img src="{{ STATIC_URL }}lava-server/images/ajax-progress.gif"/>
-    </div>
-  </div>
-{% endif %}
-</div>
-
-<script>
-var pollTimer = null, logLenth = '0', finished = false;
-
-function loadExtra (notice, start, count) {
- $.ajax({
-    url: '{% url lava_scheduler_app.views.job_output pk=job.pk %}?start=' + start + '&count=' + count,
-    dataType: 'text',
-    global: false,
-    success: function (data) {
-      var node = $("<pre></pre>");
-      node.text(data);
-      notice.replaceWith(node);
-    }
- });
-}
+<h2>Dispatcher Log Summary</h2>
+{% if job_has_error %}
+<div id="dispatcher-error">
+<h3>Dispatcher error or test failure</h3>
+<pre>{{ job_errors }}</pre>
+</div>
+{% endif %}
+<h3>Dispatcher Log messages (file size = {{ job_file_size|filesizeformat }})</h3>
+<span class="logbuttons">
+{% for level in levels %}
+{% if level.1 %}
+  <input type="checkbox" checked="checked" id="{{ level.0 }}"/><label for="{{ level.0 }}">{{ level.0 }}</label>
+{% else %}
+  <input type="checkbox" checked="checked" id="{{ level.0 }}" disabled="disabled"/><label for="{{ level.0 }}">{{ level.0 }}</label>
+{% endif %}
+{% endfor %}
+</span>
+<div id="log-messages">
+{% for log in job_log_messages %}
+<pre class="log {{log.0}}">{{ log.1 }}</pre>
+{% endfor %}
+{% if job.status == job.RUNNING %}
+<img src="{{ STATIC_URL }}lava_scheduler_app/images/ajax-progress.gif"/>
+{% endif %}
+</div>
+
+<script type="text/javascript">
+$('.logbuttons input').button()
+function showHideLogClick () {
+  var rule = rules[$(this).attr('id')];
+  if ($(this).is(':checked')) {
+    rule.css('display', 'block');
+  } else {
+    rule.css('display', 'none');
+  }
+}
+$('.logbuttons input').click(showHideLogClick)
+var rules = {
+{% for level in levels %}
+ {{ level.0 }}: $.rule('pre.{{ level.0 }} {}').appendTo('style'),
+{% endfor %}
+'': undefined
+}
+
+{% if job.status == job.RUNNING %}
+var pollTimer = null, logLenth = '{{ job_file_size }}';
 
 function poll (start) {
-  pollTimer = null;
   $.ajax({
-    url: '{% url lava_scheduler_app.views.job_output pk=job.pk %}?start=' + logLenth,
-    dataType: 'text',
+    url: '{% url lava_scheduler_app.views.job_log_incremental pk=job.pk %}?start=' + logLenth,
+    dataType: 'json',
     global: false,
     success: function (data, success, xhr) {
-      var node = $("<pre></pre>");
-      if (data.length > 0) {
-        node.text(data);
-        var progressNode = $('#tab-output img');
-        var scrollDiv = progressNode.closest('div');
-        var atBottom = scrollDiv.attr('offsetHeight') + scrollDiv.attr('scrollTop') >= scrollDiv.attr('scrollHeight') && scrollDiv.attr('offsetHeight') > progressNode.attr('offsetHeight');
-        var skipped = xhr.getResponseHeader('X-Skipped-Bytes');
-        if (skipped) {
-          var notice = $('<code class="skip">... skipped ' + skipped + ' bytes (<a href="#">load</a>) ...</code>');
-          var curLength = logLenth;
-          $('a', notice).click(function (e) {
-            e.preventDefault();
-            loadExtra(notice, curLength, skipped);
-          });
-          notice.insertBefore(progressNode);
-        }
-        node.insertBefore(progressNode);
-        if (atBottom) {
-          scrollDiv.attr('scrollTop', scrollDiv.attr('scrollHeight'))
-        }
+      var progressNode = $('#log-messages img');
+      for (var i = 0; i < data.length; i++) {
+          var d = data[i];
+          var node = $('<pre class="log"></pre>');
+          node.addClass(d[0]);
+          node.text(d[1]);
+          node.insertBefore(progressNode);
+          var toggleButton = $("#" + d[0]);
+          if (toggleButton.length) {
+                          console.log('a');
+              $("#" + d[0]).button('option', 'disabled', false);
+          } else {
+                          console.log('b');
+              var button = $('<input type="checkbox" checked="checked"/>').attr('id', d[0]);
+              var label = $('<label></label>').attr('for', d[0]).text(d[0]);
+              $('.logbuttons').append(button, label);
+              rules[d[0]] = $.rule('pre.'+d[0]+' {}').appendTo('style'),
+              button.button();
+              button.click(showHideLogClick);
+          }
       }
       logLenth = xhr.getResponseHeader('X-Current-Size');
       if (xhr.getResponseHeader('X-Is-Finished')) {
-        $('#tab-output img').css('display', 'none');
-        finished = true;
+        $('#log-messages img').css('display', 'none');
       } else {
         pollTimer = setTimeout(poll, 1000);
       }
     }
   });
 }
-
 $(document).ready(
-  function() {
-    $("#tabs").tabs(
-      {
-        select: function (event, ui) {
-          if (ui.index == 1) {
-            if (!finished) poll();
-          } else if (pollTimer !== null) {
-            clearTimeout(pollTimer);
-          }
-        }
-      }
-    );
-    $("#cancel-button").button();
-  }
+function () {
+pollTimer = setTimeout(poll, 1000);
+}
 );
+{% endif %}
 </script>
+{% endblock %}
 
-{% endblock %}

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/job_definition.html'
--- lava_scheduler_app/templates/lava_scheduler_app/job_definition.html	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/job_definition.html	2011-12-09 03:55:33 +0000
@@ -0,0 +1,21 @@ 
+{% extends "lava_scheduler_app/job_sidebar.html" %}
+
+{% block extrahead %}
+{{ block.super }}
+<script type="text/javascript" src="{{ STATIC_URL }}lava_scheduler_app/js/shCore.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}lava_scheduler_app/js/shBrushJScript.js"></script>
+
+<link href="{{ STATIC_URL }}lava_scheduler_app/css/shCore.css" rel="stylesheet" type="text/css" />
+<link href="{{ STATIC_URL }}lava_scheduler_app/css/shThemeDefault.css" rel="stylesheet" type="text/css" />
+{% endblock %}
+
+{% block content %}
+<h2>Job defintion file - {{ job.id }} </h2>
+<a href="{% url lava.scheduler.job.definition.plain job.pk %}">Download as text file</a>
+<pre class="brush: js">{{ job.definition }}</pre>
+
+<script type="text/javascript">
+     SyntaxHighlighter.all()
+</script>
+
+{% endblock %}

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/job_log_file.html'
--- lava_scheduler_app/templates/lava_scheduler_app/job_log_file.html	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/job_log_file.html	2011-12-13 04:22:40 +0000
@@ -0,0 +1,64 @@ 
+{% extends "lava_scheduler_app/job_sidebar.html" %}
+
+{% block extrahead %}
+{{ block.super }}
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava_scheduler_app/css/logfile.css"/>
+{% endblock %}
+
+{% block content %}
+<h2>Dispatcher log file - {{ job.id }} </h1>
+<a href="{% url lava.scheduler.job.log_file.plain job.pk %}">Download as text file</a>
+
+<div id="logfile_content">
+{% for section in sections %}
+<a name="entry{{ forloop.counter0 }}"></a>
+{% if section.0 == 'console' and section.1 > 20 and not forloop.last %}
+<a href="#entry{{ forloop.counter }}">skip {{ section.1 }} lines to next log entry &rarr;</a>
+{% endif %}
+<pre class="log_{{ section.0 }}">{{ section.2 }}</pre>
+{% endfor %}
+{% if job.status == job.RUNNING %}
+<img id="progress" src="{{ STATIC_URL }}lava_scheduler_app/images/ajax-progress.gif"/>
+{% endif %}
+</div>
+
+{% if job.status == job.RUNNING %}
+<script type="text/javascript">
+var pollTimer = null, logLenth = '{{ job_file_size }}';
+
+function poll (start) {
+  $.ajax({
+    url: '{% url lava_scheduler_app.views.job_full_log_incremental pk=job.pk %}?start=' + logLenth,
+    dataType: 'json',
+    global: false,
+    success: function (data, success, xhr) {
+      var progressNode = $('#progress');
+      for (var i = 0; i < data.length; i++) {
+          var d = data[i];
+          var cls = 'log_' + d[0];
+          var last_pre = $("#logfile_content pre:last");
+          if (last_pre.attr('class') == cls) {
+              last_pre.append(document.createTextNode(d[2]));
+          } else {
+              var newNode = $("<pre></pre>");
+              newNode.addClass('log_' + d[0]);
+              newNode.text(d[2]);
+              newNode.insertBefore(progressNode);
+          }
+      }
+      logLenth = xhr.getResponseHeader('X-Current-Size');
+      if (xhr.getResponseHeader('X-Is-Finished')) {
+        progressNode.css('display', 'none');
+      } else {
+        pollTimer = setTimeout(poll, 1000);
+      }
+    }
+  });
+}
+$(document).ready(function () {
+  pollTimer = setTimeout(poll, 1000);
+});
+</script>
+{% endif %}
+
+{% endblock %}

=== added file 'lava_scheduler_app/templates/lava_scheduler_app/job_sidebar.html'
--- lava_scheduler_app/templates/lava_scheduler_app/job_sidebar.html	1970-01-01 00:00:00 +0000
+++ lava_scheduler_app/templates/lava_scheduler_app/job_sidebar.html	2011-12-09 03:55:33 +0000
@@ -0,0 +1,76 @@ 
+{% extends "layouts/content_with_sidebar.html" %}
+
+
+{% block extrahead %}
+{{ block.super }}
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava-server/css/demo_table_jui.css"/>
+<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}lava_scheduler_app/css/scheduler.css"/>
+<script type="text/javascript" src="{{ STATIC_URL }}lava-server/js/jquery.dataTables.min.js"></script>
+{% endblock %}
+
+
+{% block sidebar %}
+<h2>Job information</h2>
+<dl>
+    <dt>Submitted by:</dt>
+    <dd>{{ job.submitter }}</dd>
+
+    {% if job.requested_device %}
+    <dt>Requested device:</dt>
+    <dd>{{ job.requested_device }}</dd>
+    {% endif %}
+
+    {% if job.requested_device_type %}
+    <dt>Requested type:</dt>
+    <dd>{{ job.requested_device_type }}</dd>
+    {% endif %}
+
+    {% if job.description %}
+    <dt>Description:</dt>
+    <dd>{{ job.description }}</dd>
+    {% endif %}
+     <dt>Status:</dt>
+    <dd>{{ job.get_status_display }}</dd>
+
+    {% if job.actual_device %}
+    <dt>On device:</dt>
+    <dd>{{ job.actual_device }}</dd>
+    {% endif %}
+     <dt>Submitted at:</dt>
+    <dd>{{ job.submit_time }}</dd>
+
+    <dt>Started at:</dt>
+    <dd>{{ job.start_time|default:"not started" }}</dd>
+
+    <dt>Finished at:</dt>
+    <dd>{{ job.end_time|default:"not finished" }}</dd>
+</dl>
+<h2>Views</h2>
+<ul>
+    <li>
+        <a href="{% url lava.scheduler.job.detail job.pk %}">Summary</a>
+    </li>
+    <li>
+        <a href="{% url lava.scheduler.job.log_file job.pk %}">Complete log</a>
+    </li>
+    <li>
+        <a href="{% url lava.scheduler.job.definition job.pk %}">Defintion</a>
+    </li>
+    {% if job.results_link %}
+    <li>
+        <a href="{{ job.results_link }}">Results Bundle</a>
+    </li>
+    {% endif %}
+
+</ul>
+{% if show_cancel %}
+<h2>Actions</h2>
+<form method="POST"
+      action="{% url lava.scheduler.job.cancel job.pk %}">
+  {% csrf_token %}
+  <button id="cancel-button">Cancel Job</button>
+</form>
+{% endif %}
+
+{% endblock %}
+

=== modified file 'lava_scheduler_app/urls.py'
--- lava_scheduler_app/urls.py	2011-10-28 00:24:13 +0000
+++ lava_scheduler_app/urls.py	2011-12-13 03:58:12 +0000
@@ -20,6 +20,18 @@ 
     url(r'^job/(?P<pk>[0-9]+)$',
         'job_detail',
         name='lava.scheduler.job.detail'),
+    url(r'^job/(?P<pk>[0-9]+)/definition$',
+        'job_definition',
+        name='lava.scheduler.job.definition'),
+    url(r'^job/(?P<pk>[0-9]+)/definition/plain$',
+            'job_definition_plain',
+            name='lava.scheduler.job.definition.plain'),
+    url(r'^job/(?P<pk>[0-9]+)/log_file$',
+            'job_log_file',
+            name='lava.scheduler.job.log_file'),
+    url(r'^job/(?P<pk>[0-9]+)/log_file/plain$',
+                'job_log_file_plain',
+                name='lava.scheduler.job.log_file.plain'),
     url(r'^job/(?P<pk>[0-9]+)/cancel$',
         'job_cancel',
         name='lava.scheduler.job.cancel'),
@@ -28,4 +40,11 @@ 
         name='lava.scheduler.job.json'),
     url(r'^job/(?P<pk>[0-9]+)/output$',
         'job_output',
-        name='lava.scheduler.job.output'))
+        name='lava.scheduler.job.output'),
+    url(r'^job/(?P<pk>[0-9]+)/log_incremental$',
+        'job_log_incremental',
+        name='lava.scheduler.job.log_incremental'),
+    url(r'^job/(?P<pk>[0-9]+)/full_log_incremental$',
+        'job_full_log_incremental',
+        name='lava.scheduler.job.full_log_incremental'),
+    )

=== modified file 'lava_scheduler_app/views.py'
--- lava_scheduler_app/views.py	2011-10-28 00:24:13 +0000
+++ lava_scheduler_app/views.py	2011-12-13 03:58:12 +0000
@@ -1,5 +1,11 @@ 
-import json
+from collections import defaultdict
+import simplejson
+import logging
 import os
+import StringIO
+
+from logfile_helper import formatLogFile, getDispatcherErrors
+from logfile_helper import getDispatcherLogMessages, getDispatcherLogSize
 
 from django.http import (
     HttpResponse,
@@ -60,15 +66,101 @@ 
 @BreadCrumb("Job #{pk}", parent=index, needs=['pk'])
 def job_detail(request, pk):
     job = get_object_or_404(TestJob, pk=pk)
+    job_errors = getDispatcherErrors(job.log_file)
+    job_log_messages = getDispatcherLogMessages(job.log_file)
+
+    levels = defaultdict(int)
+    for kl in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
+        levels[kl] = 0
+    for level, msg in job_log_messages:
+        levels[level] += 1
+    levels = sorted(levels.items(), key=lambda (k,v):logging._levelNames.get(k))
+
     return render_to_response(
         "lava_scheduler_app/job.html",
         {
-            'log_file_present': bool(job.log_file),
             'job': TestJob.objects.get(pk=pk),
             'show_cancel': job.status <= TestJob.RUNNING and job.can_cancel(request.user),
             'bread_crumb_trail': BreadCrumbTrail.leading_to(job_detail, pk=pk),
-        },
-        RequestContext(request))
+            'job_errors' : job_errors,
+            'job_has_error' : len(job_errors) > 0,
+            'job_log_messages' : job_log_messages,
+            'levels': levels,
+            'show_reload_page' : job.status <= TestJob.RUNNING,
+            'job_file_size' : getDispatcherLogSize(job.log_file),
+        },
+        RequestContext(request))
+
+
+def job_definition(request, pk):
+    job = get_object_or_404(TestJob, pk=pk)
+    return render_to_response(
+        "lava_scheduler_app/job_definition.html",
+        {
+            'job': job,
+        },
+        RequestContext(request))
+
+
+def job_definition_plain(request, pk):
+    job = get_object_or_404(TestJob, pk=pk)
+    response = HttpResponse(job.definition, mimetype='text/plain')
+    response['Content-Disposition'] = "attachment; filename=job_%d.json"%job.id
+    return response
+
+
+@BreadCrumb("Complete log", parent=job_detail, needs=['pk'])
+def job_log_file(request, pk):
+    job = get_object_or_404(TestJob, pk=pk)
+    content = formatLogFile(job.log_file)
+    return render_to_response(
+        "lava_scheduler_app/job_log_file.html",
+        {
+            'job': TestJob.objects.get(pk=pk),
+            'sections' : content,
+            'job_file_size' : getDispatcherLogSize(job.log_file),
+        },
+        RequestContext(request))
+
+
+def job_log_file_plain(request, pk):
+    job = get_object_or_404(TestJob, pk=pk)
+    response = HttpResponse(job.log_file, mimetype='text/plain')
+    response['Content-Disposition'] = "attachment; filename=job_%d.log"%job.id
+    return response
+
+
+def job_log_incremental(request, pk):
+    start = int(request.GET.get('start', 0))
+    job = get_object_or_404(TestJob, pk=pk)
+    log_file = job.log_file
+    log_file.seek(start)
+    new_content = log_file.read()
+    m = getDispatcherLogMessages(StringIO.StringIO(new_content))
+    response = HttpResponse(
+        simplejson.dumps(m), content_type='application/json')
+    response['X-Current-Size'] = str(start + len(new_content))
+    if job.status not in [TestJob.RUNNING, TestJob.CANCELING]:
+        response['X-Is-Finished'] = '1'
+    return response
+
+
+def job_full_log_incremental(request, pk):
+    start = int(request.GET.get('start', 0))
+    job = get_object_or_404(TestJob, pk=pk)
+    log_file = job.log_file
+    log_file.seek(start)
+    new_content = log_file.read()
+    nl_index = new_content.rfind('\n', -NEWLINE_SCAN_SIZE)
+    if nl_index >= 0:
+        new_content = new_content[:nl_index+1]
+    m = formatLogFile(StringIO.StringIO(new_content))
+    response = HttpResponse(
+        simplejson.dumps(m), content_type='application/json')
+    response['X-Current-Size'] = str(start + len(new_content))
+    if job.status not in [TestJob.RUNNING, TestJob.CANCELING]:
+        response['X-Is-Finished'] = '1'
+    return response
 
 
 LOG_CHUNK_SIZE = 512*1024
@@ -118,7 +210,7 @@ 
 
 def job_json(request, pk):
     job = get_object_or_404(TestJob, pk=pk)
-    json_text = json.dumps({
+    json_text = simplejson.dumps({
         'status': job.get_status_display(),
         'results_link': job.results_link,
         })