Browse Source

HUE-213, HUE-214. Add ability to update the query string of the current path without reloading the window. (214) Add ability to load portions of a tree-view table via ajax.

Putting these in the same commit as they both share the same test.
Aaron Newton 15 years ago
parent
commit
e1c0245624

+ 71 - 48
apps/jframegallery/src/jframegallery/templates/html-table.treeview.ajax.mako

@@ -1,3 +1,6 @@
+<%!
+  import urllib
+%>
 ## Licensed to Cloudera, Inc. under one
 ## or more contributor license agreements.  See the NOTICE file
 ## distributed with this work for additional information
@@ -15,55 +18,75 @@
 ## limitations under the License.
 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
 <html>
-	<head>
-		<title>Process Viewer</title>
-	</head>
-	<body>
-        
-<a href="${request_path}?show_all=true">Show everything</a>
-<table data-filters="HtmlTable" class="treeView">
+  <head>
+    <title>Process Viewer</title>
+    <style>
+    .table-depth-0>td:first-child { padding-left: 10px; }
+    .table-depth-1>td:first-child { padding-left: 25px; }
+    .table-depth-2>td:first-child { padding-left: 40px; }
+    .table-depth-3>td:first-child { padding-left: 55px; }
+    .table-depth-4>td:first-child { padding-left: 70px; }
+    .table-depth-5>td:first-child { padding-left: 85px; }
+    .table-depth-6>td:first-child { padding-left: 100px; }
+    .table-depth-7>td:first-child { padding-left: 115px; }
+    .table-depth-8>td:first-child { padding-left: 130px; }
+    .table-depth-9>td:first-child { padding-left: 145px; }
+    .table-depth-10>td:first-child { padding-left: 160px; }
+    .table-depth-11>td:first-child { padding-left: 175px; }
+    </style>
+  </head>
+  <body>
+    <div class="jframe_padded"> 
+      <p><a href="${request_path}?show_all=true">Show everything</a> || <a href="${request_path}">back to top</a></p>
+      <table data-filters="HtmlTable" class="selectable treeView" style="border: 1px solid #999; width: 98%">
 
-<%def name="r(node, depth, path)">
-  <tr class="table-folder table-depth-${depth}">
-  % if node.children:
-    <td class="expand">${node.pid}</td>
-  % else:
-    <td>${node.pid}</td>
-  % endif
-  <td>
-  % if path in open_paths:
-    <a href="${remove(path)}">collapse</a>
-  % elif node.children:
-    <a href="${add(path)}">expand</a>
-  % endif
+      <%def name="create_row(node, depth, path)">
+        <%
+          expanded = ""
+          if path in open_paths:
+            expanded = "table-expanded"
+        %>
+        <tr class="table-folder table-depth-${depth} ${expanded} pstree-${node.pid}">
+          <td style="max-width:400px">
+            % if path in open_paths:
+              <a href="${remove(path)}" class="ccs-hidden">collapse</a>
+            % elif node.children:
+              <a href="${add(path)}" class="ccs-hidden">expand</a>
+            % endif
 
-  <a href="${request_path}?subtree=${node.pid}">subset</a>
-  </td>
-  <td>${node.user}</td>
-  <td>${node.cputime}</td>
-  <td>${node.command}</td>
-  </tr>
-  % if path in open_paths or show_all:
-    % for child in node.children:
-      ${r(child, depth+1, path+"/"+str(child.pid))}
-    % endfor
-  % endif
-</%def>
+            % if node.children:
+              <a href="${request_path}?subtree=${node.pid}&depth=${int(depth)+1}&sleep=1" class="expand"
+                data-spinner-target=".pstree-${node.pid}"
+                data-livepath-toggle="${urllib.urlencode([('paths', node.path)])}"
+                data-ajax-replace=".pstree-${node.pid}-target" data-ajax-filter="tbody tr">subset</a>
+            % endif
+      
+            <div style="overflow:hidden; white-space:nowrap;">${node.command}</div></td>
+          <td>${node.pid}</td>
+          <td>${node.user}</td>
+          <td>${node.cputime}</td>
+        </tr>
+        % if path in open_paths or show_all:
+          % for child in node.children:
+            ${r(child, depth+1, path+"/"+str(child.pid))}
+          % endfor
+        % elif not show_all and len(node.children) > 0:
+          <tr style="display:none" class="html-table-tree-ignore pstree-${node.pid}-target"><td colspan="4"></td></tr>
+        % endif
+      </%def>
 
-<thead>
-<th>pid</th>
-<th>links</th>
-<th>user</th>
-<th>cputime</th>
-<th>command</th>
-</thead>
-<tbody>
-% for top in tops:
- ${r(top, 0, "/" + str(top.pid))}
-% endfor
-</tbody>
-</table>
-
-
-        </body>
+      <thead>
+      <th>command</th>
+      <th>pid</th>
+      <th>user</th>
+      <th>cputime</th>
+      </thead>
+      <tbody>
+      % for top in tops:
+       ${create_row(top, depth, "/" + str(top.pid))}
+      % endfor
+      </tbody>
+      </table>
+    </div>
+  </body>
 </html>

+ 25 - 13
apps/jframegallery/src/jframegallery/views.py

@@ -137,12 +137,16 @@ def pstree(request):
     show_all: expand the entire tree
     paths: slash-separated paths that are expanded
       (can be specified multiple times)
-  
+
   """
   import subprocess
   import urllib
   import re
 
+  if request.REQUEST.get('sleep'):
+    sleep = int(request.REQUEST.get('sleep'))
+    time.sleep(sleep)
+
   # Call ps
   p = subprocess.Popen(args=["ps", "-axwwo", "user,pid,ppid,pgid,cputime,command"], stdout=subprocess.PIPE)
 
@@ -170,33 +174,41 @@ def pstree(request):
       children[ps.ppid] = [ps]
 
   # Utility method to create the tree
-  def fill(root):
+  def fill(root, current_path):
+    root.path = current_path + str(root.pid)
     root.children = children.get(root.pid, [])
     for child in root.children:
-      fill(child)
+      fill(child, root.path + "/")
+
 
+  # Start with init and create the tree
+  assert len(children[0]) == 1
+  top = children[0][0]
+  fill(top, "/")
+  tops = [top]
+
+  # If we're only interested in a subtree, pick that out explicitly
   if subtree_top:
-    fill(subtree_top)
     tops = subtree_top.children
-  else:
-    # Start with init and create the tree
-    assert len(children[0]) == 1
-    top = children[0][0]
-    fill(top)
-    tops = [top]
 
   # Methods to manipulate the extant paths list; used by the template.
   def add(p):
     paths = list(request.GET.getlist("paths")) # make a copy
     paths.append(p)
-    return request.path + "?" + "&".join(urllib.urlencode([("paths", x)]) for x in paths)
+    query = [urllib.urlencode([("paths", x)]) for x in paths]
+    if subtree:
+      query.append('subtree=' + str(subtree))
+    return request.path + "?" + "&".join(query)
   def remove(p):
     paths = list(request.GET.getlist("paths")) # make a copy
     paths.remove(p)
-    return request.path + "?" + "&".join(urllib.urlencode([("paths", x)]) for x in paths)
+    query = [urllib.urlencode([("paths", x)]) for x in paths]
+    if subtree:
+      query.append('subtree=' + str(subtree))
+    return request.path + "?" + "&".join(query)
 
   paths = request.GET.getlist("paths")
   return render("html-table.treeview.ajax.mako", request, dict(
     tops=tops, show_all=request.GET.get("show_all"), 
     open_paths=paths, request_path=request.path,
-    add=add, remove=remove))
+    add=add, remove=remove, depth=request.GET.get('depth', 0)))

+ 57 - 0
desktop/core/static/js/Source/BehaviorFilters/Behavior.HtmlTableLiveTreeKeyboard.js

@@ -0,0 +1,57 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you 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.
+/*
+---
+description: Adds keyboard support for live loading trees - integrates key navigation with the livepath linker.
+provides: [Behavior.HtmlTableLiveTreeKeyboard]
+requires: [Widgets/Behavior.HtmlTable]
+script: Behavior.HtmlTableLiveTreeKeyboard.js
+...
+*/
+(function(){
+
+var checkLinkerForLivePath = function(anchor, methods){
+	['[data-livepath-toggle]', '[data-livepath-add]', '[data-livepath-replace]'].some(function(selector){
+		if (anchor.match(selector)) {
+			methods.invokeLinker(selector, anchor, { stop: $empty, preventDefault: $empty, stopPropagation: $empty});
+			return true;
+		}
+	});
+};
+
+Behavior.addGlobalPlugin('HtmlTable', 'HtmlTableLiveTreeKeyboard', function(element, methods){
+	if (!element.hasClass('treeView')) return;
+	var table = element.retrieve('HtmlTable');
+	table.addEvent('expandSection', function(row){
+		var anchor = row.getElement('.expand');
+		if (!row.retrieve('livetree:loaded')) {
+			row.store('livetree:loaded', true);
+			if (anchor) methods.callClick({ stop: $empty, preventDefault: $empty, stopPropagation: $empty}, anchor, true);
+		} else if (anchor) {
+			checkLinkerForLivePath(anchor, methods);
+		}
+	}.bind(this));
+	table.addEvent('closeSection', function(row){
+		var anchor = row.getElement('.expand');
+		row.store('livetree:loaded', true);
+		if (anchor) checkLinkerForLivePath(anchor, methods);
+	});
+	$(table).addEvent('click:relay(.expand)', function(event, link){
+		event.stop();
+	});
+});
+
+})();

+ 41 - 0
desktop/core/static/js/Source/BehaviorFilters/Behavior.HtmlTableUpdate.js

@@ -0,0 +1,41 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you 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.
+/*
+---
+description: Updates HtmlTable states when the JFrame contents are updated (via Ajax/partial refresh).
+provides: [Behavior.HtmlTableUpdate]
+requires: [Widgets/Behavior.HtmlTable]
+script: Behavior.HtmlTableUpdate.js
+...
+*/
+
+Behavior.addGlobalPlugin('HtmlTable', 'HtmlTableUpdate', function(element, methods){
+	var table = element.retrieve('HtmlTable');
+	var refresh = function(data, options){
+		var refresh;
+		data.elements.each(function(element){
+			if (!refresh && ['td','th','tbody','tr','thead','tfoot'].contains(element.get('tag')) && $(table).hasChild(element)) {
+				refresh = true;
+			}
+		});
+		if (refresh) table.refresh();
+		$(table).getElements('.table-expanded a.expand').addClass('jframe_ignore');
+	};
+	methods.addEvent('update', refresh);
+	this.markForCleanup(element, function(){
+		methods.removeEvent('update', refresh);
+	});
+});

+ 65 - 43
desktop/core/static/js/Source/CCS/CCS.JFrame.js

@@ -43,7 +43,9 @@ requires:
  - /Behavior.HtmlTableCheckSelected
  - /Behavior.HtmlTableChromeHack
  - /Behavior.HtmlTableKeyboard
+ - /Behavior.HtmlTableLiveTreeKeyboard
  - /Behavior.HtmlTableMultiSelectMenu
+ - /Behavior.HtmlTableUpdate
  - /Behavior.MultiChecks
  - /Behavior.PostEditor
  - /Behavior.SelectWithOther
@@ -139,6 +141,9 @@ CCS.JFrame = new Class({
 			}.bind(this),
 			unregisterKeyboard: function(keyboard){
 				this.keyboard.drop(keyboard);
+			}.bind(this),
+			callClick: function(){
+				this.callClick.apply(this, arguments);
 			}.bind(this)
 		});
 		this.addEvent('resize', this.behavior.resize.bind(this.behavior));
@@ -200,7 +205,7 @@ CCS.JFrame = new Class({
 		if (this.delegatedTo.contains(target)) return;
 		this.delegatedTo.push(target);
 		
-		var handler = function(e, elem, url, target){
+		var handler = function(e, elem, url, options){
 			if (elem.get('tag') == 'a') e.preventDefault();
 			if (!this._checkLinkers(e, elem)) {
 				// If it's an anchor link, do scrolling
@@ -215,13 +220,17 @@ CCS.JFrame = new Class({
 				var path = url ? url.toString() : '';
 				if (!path) return;
 
-				this.load({
+				options = $merge({
 					requestPath: path
-				});
+				}, options);
+				var spinnerTarget = elem.get('data', 'spinner-target');
+				if (spinnerTarget) spinnerTarget = $(this).getElement(spinnerTarget);
+				options.spinnerTarget = spinnerTarget;
+				this.load(options);
 
 			}
 		}.bind(this);
-		this.callClick = function(e, elem, force){
+		this.callClick = function(e, elem, force, options){
 			//allow links to force jframe to nerf them
 			//this is required for doubleclick support
 			//as otherwise there's no way to prevent this default jframe handler per link
@@ -241,10 +250,10 @@ CCS.JFrame = new Class({
 					// by this class with the current design.
 					elem.set('target', '_blank');
 				} else {
-					handler(e, elem, url);
+					handler(e, elem, url, options);
 				}
 			} else {
-				handler(e, elem);
+				handler(e, elem, false, options);
 			}
 		}.bind(this);
 		target.addEvent('click:relay(' + this.options.clickRelays + ')', this.callClick);
@@ -313,6 +322,7 @@ CCS.JFrame = new Class({
 		target: the target where the content was loaded
 		toolbar: the toolbar elements (see below),
 		footer: the footer elements (see below)
+		everything else: used to pass along options to filters, renderers, etc.
 		
 	views:
 		The content of a JFrame request is searched for the first element with the class "view". 
@@ -344,6 +354,12 @@ CCS.JFrame = new Class({
 	*/
 	renderContent: function(options){
 		var content = {};
+		var filter = function(elements, selector){
+			if (!elements.length) return elements;
+			var first = elements[0];
+			var holder = new Element('div').adopt(elements);
+			return first.getParent().getElements(selector);
+		};
 		if ($(options.content)) {
 			//if the content is an element, cast it into an Elements array
 			content.elements = $$($(options.content));
@@ -355,7 +371,7 @@ CCS.JFrame = new Class({
 			//cast it into an Elements array in case it's just a vanilla array
 			content.elements = $$(options.content);
 		}
-		
+		if (options.filter) content.elements = filter(content.elements, options.filter);
 		//determine view and view element
 		var view,
 		    viewElement = content.elements.filter('.view')[0] || content.elements.getElement('.view')[0];
@@ -365,20 +381,22 @@ CCS.JFrame = new Class({
 			content.view = view;
 			content.viewElement = viewElement;
 		}
-		this._applyRenderers(content, options);
+		content.options = options;
+		this._applyRenderers(content);
 	},
 
 	/*
 		fill: fills a given target with the appropriate content
 		target - (*element*) the target to fill with content
 		content - (*object*) an object with the following properties:
-		js - (*string*) any the inline javascript to evalutate,
-		links - (*elements array*) css links to be injected into the target
-		elements - (*elements array*) elements to inject into the target (i.e. the actual content)
-		title - (*string*) the title of the content
-		view - (*string*; optional) if defined, the view of the content
-		viewElement - (*element*; optional) if defined, the element for the view
-		behavior - (*behavior object*; optional) if defined, the behavior instance to use
+			options - (*object*) the options object that created the request; see renderContent
+			js - (*string*) any the inline javascript to evalutate,
+			links - (*elements array*) css links to be injected into the target
+			elements - (*elements array*) elements to inject into the target (i.e. the actual content)
+			title - (*string*) the title of the content
+			view - (*string*; optional) if defined, the view of the content
+			viewElement - (*element*; optional) if defined, the element for the view
+			behavior - (*behavior object*; optional) if defined, the behavior instance to use
 		
 	*/
 
@@ -463,26 +481,26 @@ CCS.JFrame = new Class({
 	addBehaviors: function(obj, overwrite){
 		this.behavior.addFilters(obj, overwrite);
 	},
-        /* 
-                add a new behavior plugin
-                filterName - (*string*) the name of the filter this plugin is for (no spaces or commas; preferably CamelCase)
-                name - (*string*) the name of the plugin (no spaces or commas; preferably CamelCase)
-                fn - (*string*) the attachment function for the plugin
-                overwrite - (*boolean*) if true, will overwite any pre-existing filter if one is present
-        */
-
-        addBehaviorPlugin: function(filterName, name, fn, overwrite) {
-                this.behavior.addPlugin(filterName, name, fn, overwrite);
-        },
-        /*
-                add a group of behavior plugins
-                obj - (*object*) an object containing objects containing the filter name, the plugin name, and the attachment function for the plugin
-                overwrite - (*boolean*) if true, will overwrite any pre-existing filter if one is present
-        */
-
-        addBehaviorPlugins: function(obj, overwrite){
-                this.behavior.addPlugins(obj, overwrite);
-        },
+	/*
+		add a new behavior plugin
+		filterName - (*string*) the name of the filter this plugin is for (no spaces or commas; preferably CamelCase)
+		name - (*string*) the name of the plugin (no spaces or commas; preferably CamelCase)
+		fn - (*string*) the attachment function for the plugin
+		overwrite - (*boolean*) if true, will overwite any pre-existing filter if one is present
+	*/
+
+	addBehaviorPlugin: function(filterName, name, fn, overwrite) {
+		this.behavior.addPlugin(filterName, name, fn, overwrite);
+	},
+	/*
+		add a group of behavior plugins
+		obj - (*object*) an object containing objects containing the filter name, the plugin name, and the attachment function for the plugin
+		overwrite - (*boolean*) if true, will overwrite any pre-existing filter if one is present
+	*/
+
+	addBehaviorPlugins: function(obj, overwrite){
+		this.behavior.addPlugins(obj, overwrite);
+	},
 
 	/*
 		apply a specific behavior to an element
@@ -824,6 +842,7 @@ CCS.JFrame = new Class({
 	/*
 		_applyRenderers: renders content into the target
 		content - (*object*) an object with the following props:
+			options - (*object*) the object that created the request
 			html - (*string*) the source html, if it was present
 			js - (*string*) any the inline javascript to evalutate,
 			links - (*elements array*) css links and style tags to be injected into the target
@@ -840,22 +859,22 @@ CCS.JFrame = new Class({
 		and filters, linkers, etc. as usual.
 	*/
 
-	_applyRenderers: function(content, options){
+	_applyRenderers: function(content){
 		var rendered;
-		this.fireEvent('beforeRenderer', [content, options]);
+		this.fireEvent('beforeRenderer', [content, content.options]);
 		//loop through all the renderers
 		for (name in this._renderers) {
 			var renderer = this._renderers[name];
 			dbug.conditional(function(){
-				rendered = renderer.call(this, content, options);
+				rendered = renderer.call(this, content);
 			}.bind(this), function(e) {
 				dbug.error('renderer failed: name %s, error: ', e);
 			});
 			if (rendered) break;
 		}
 		//if no renderers returned true, then call the default one
-		if (!rendered) this._defaultRenderer(content, options);
-		this.fireEvent('afterRenderer', [content, options]);
+		if (!rendered) this._defaultRenderer(content);
+		this.fireEvent('afterRenderer', [content, content.options]);
 	},
 
 	/*
@@ -871,15 +890,16 @@ CCS.JFrame = new Class({
 		as well as assigns the toolbar to the callback object for JBrowser to do with it what it will.
 		Finally, it calls the callback in the options (if specified) and fires the loadComplete event.
 	*/
-	_defaultRenderer: function(content, options){
+	_defaultRenderer: function(content){
+		var options = content.options;
 		//store the path as the current one
-		this.currentPath = options.responsePath || this.currentPath;
+		if (!options.retainPath) this.currentPath = options.responsePath || this.currentPath;
 		//grab the target
 		var target = options.target ? $$(options.target)[0] || this.content : this.content;
 		this._resetOverflow(target);
 
 		//if we're injecting into the main content body, cleanup and scrollto the top
-		if (target == this.content) {
+		if (target == this.content && !options.noScroll) {
 			this.scroller.toTop();
 			this._sweep(target);
 		}
@@ -908,6 +928,8 @@ CCS.JFrame = new Class({
 		//define the callback data
 		var data = {
 			content: options.content,
+			elements: content.elements,
+			requestOptions: content.options,
 			responsePath: options.responsePath,
 			title: content.title || options.title || options.responsePath,
 			userData: options.userData,

+ 82 - 0
desktop/core/static/js/Source/JFrameLinkers/CCS.JFrame.LivePath.js

@@ -0,0 +1,82 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you 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.
+/*
+---
+description: Provides functionality for links that update the window path on the fly.
+provides: [CCS.JFrame.LivePath]
+requires: [/CCS.JFrame]
+script: CCS.JFrame.LivePath.js
+...
+*/
+
+(function(){
+
+	CCS.JFrame.addGlobalLinkers({
+
+		'[data-livepath-toggle]': function(event, link){
+			this.currentPath = updateLivePath(link, this.currentPath);
+		},
+		//these are a bit silly, but mootools 1.2 selector engine doesn't support
+		//complex selectors for element.match, so we have to do a bit of repetition here
+		'[data-livepath-add]': function(event, link){
+			this.currentPath = updateLivePath(link, this.currentPath);
+		},
+		'[data-livepath-remove]': function(event, link){
+			this.currentPath = updateLivePath(link, this.currentPath);
+		}
+
+	});
+
+	var updateLivePath = function(link, currentPath){
+		var setData = function(uri, paths, action){
+			var okToRemove = action == "toggle" || action == "remove";
+			var okToAdd = action == "toggle" || action == "add";
+			for (path in paths){
+				var state = uri.getData(path);
+				if (!state) {
+					uri.setData(path, paths[path]);
+				} else if ($type(state) == "string") {
+					if (state == paths[path] && okToRemove)
+						uri.setData(path, null);
+					else if (okToAdd)
+						uri.setData(path, [state, paths[path]]);
+				} else if ($type(state) == "array") {
+					if (state.contains(path) && okToRemove) state.erase(path);
+					else if (okToAdd) state.push(paths[path]);
+
+					if (state.length) uri.setData(path, state);
+					else uri.clearData(path);
+				}
+			}
+		};
+		var toggle = link.get('data', 'livepath-toggle'),
+		    add = link.get('data', 'livepath-add'),
+		    remove = link.get('data', 'livepath-remove'),
+		    uri = new URI(currentPath);
+		if (toggle) {
+			toggle = toggle.parseQueryString();
+			setData(uri, toggle, 'toggle');
+		} else if (add) {
+			add = add.parseQueryString();
+			setData(uri, add, 'add');
+		} else if (remove) {
+			remove = remove.parseQueryString();
+			setData(uri, remove, 'toggle');
+		}
+		return uri.toString();
+	};
+
+})();

+ 8 - 7
desktop/core/static/js/Source/JFrameRenderers/CCS.JFrame.Alert.js

@@ -24,7 +24,8 @@ script: CCS.JFrame.Alert.js
 // limitations under the License.
 CCS.JFrame.addGlobalRenderers({
 
-	alert: function(content, options) {
+	alert: function(content) {
+		var options = content.options;
 		//if the contents have an element with .alert_popup *at the root*
 		//then display those contents in an alert
 		var popup = content.elements.filter('.alert_popup')[0];
@@ -34,11 +35,11 @@ CCS.JFrame.addGlobalRenderers({
 		var jframe_renders = popup.hasClass('jframe_renders');
 
 		var target = new Element('div', {'class': 'jframe_alert'}).hide().inject($(this));
-                var popupBehavior = new Behavior({
-                        onError: function(){
-                                dbug.warn.apply(dbug, arguments);
-                        }
-                });
+		var popupBehavior = new Behavior({
+			onError: function(){
+				dbug.warn.apply(dbug, arguments);
+			}
+		});
 		var fillAndShow = function() {
 			if (!jframe_renders) {
 				//if we aren't rendering the jframe, fill the popup
@@ -77,7 +78,7 @@ CCS.JFrame.addGlobalRenderers({
 						}.bind(this));
 					}
 				}
-                                alert.addEvent('destroy', function() {popupBehavior.cleanup(target);});
+				alert.addEvent('destroy', function() {popupBehavior.cleanup(target);});
 				alert.position().show();
 				
 				//if jframe is rendering we remove this event which we're going to add a few lines down

+ 3 - 1
desktop/core/static/js/Source/JFrameRenderers/CCS.JFrame.PartialRefresh.js

@@ -28,7 +28,9 @@ script: CCS.JFrame.PartialRefresh.js
 
 	CCS.JFrame.addGlobalRenderers({
 
-		partialRefresh: function(content, options){
+		partialRefresh: function(content){
+			var options = content.options;
+			if (options && options.ignorePartialRefresh) return;
 			var jState = getJState(this);
 			//get the partial containers; containers that have elements in them to be partially refreshed
 			var partialContainers = content.elements.filter('.partial_refresh');

+ 3 - 1
desktop/core/static/js/Source/JFrameRenderers/CCS.JFrame.Prompt.js

@@ -24,7 +24,9 @@ script: CCS.JFrame.Prompt.js
 // limitations under the License.
 CCS.JFrame.addGlobalRenderers({
 
-	prompt: function(content, options) {
+	prompt: function(content) {
+		var options = content.options;
+		
 		//if the contents have an element with .prompt_popup *at the root*
 		//then display those contents in a prompt, submitting the form (if present)
 		//when the user clicks "ok"

+ 3 - 0
desktop/core/static/js/package.yml

@@ -38,6 +38,7 @@ sources: [
   Source/JFrameLinkers/CCS.JFrame.Chooser.js,
   Source/JFrameLinkers/CCS.JFrame.ConfirmAndPost.js,
   Source/JFrameLinkers/CCS.JFrame.FakeRefresh.js,
+  Source/JFrameLinkers/CCS.JFrame.LivePath.js,
   Source/JFrameLinkers/CCS.JFrame.Nav.js,
   Source/JFrameLinkers/CCS.JFrame.NoScroll.js,
   Source/JFrameLinkers/CCS.JFrame.PromptAndPost.js,
@@ -57,7 +58,9 @@ sources: [
   Source/BehaviorFilters/Behavior.HtmlTableCheckSelected.js,
   Source/BehaviorFilters/Behavior.HtmlTableChromeHack.js,
   Source/BehaviorFilters/Behavior.HtmlTableKeyboard.js,
+  Source/BehaviorFilters/Behavior.HtmlTableLiveTreeKeyboard.js,
   Source/BehaviorFilters/Behavior.HtmlTableMultiSelectMenu.js,
+  Source/BehaviorFilters/Behavior.HtmlTableUpdate.js,
   Source/BehaviorFilters/Behavior.MultiChecks.js,
   Source/BehaviorFilters/Behavior.PostEditor.js,
   Source/BehaviorFilters/Behavior.SelectWithOther.js,