Browse Source

HUE-137. Make double click delegators more stable and prevent any links with no href from breaking the desktop

* fixing double click handler to be more fault tolerant; also make it use JFrame.callClick to ensure linkers are used as expected.
* made more jFrame gallery examples (essentially test cases)
* made ajax requests to / display an error
* JFrame requests no longer eval inline-js in the response (they were never supposed to)
* JFrame now actually passes along the error state of the response as determined by inspecting the source for an error container
Aaron Newton 15 năm trước cách đây
mục cha
commit
0b84495b62

+ 1 - 1
apps/beeswax/static/js/Source/Beeswax/CCS.Beeswax.js

@@ -58,7 +58,7 @@ ART.Sheet.define('splitview.bw-editor', {
 						hTable = table.retrieve('HtmlTable');
 						hTable = table.retrieve('HtmlTable');
 						var selected = hTable.selectedRows[0];
 						var selected = hTable.selectedRows[0];
 						if (!selected) return;
 						if (!selected) return;
-						CCS.JFrame.doubleClickHandler(this.jframe, selected);
+						CCS.JFrame.doubleClickHandler(this.jframe, e, selected);
 					}.bind(this),
 					}.bind(this),
 					description: 'Open the selected item.'
 					description: 'Open the selected item.'
 				}
 				}

+ 3 - 3
apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileBrowser.js

@@ -165,7 +165,7 @@ ART.Sheet.define('window.filechooser.browser', {
 						hTable = table.retrieve('HtmlTable');
 						hTable = table.retrieve('HtmlTable');
 						var selected = hTable.selectedRows[0];
 						var selected = hTable.selectedRows[0];
 						if (!selected) return;
 						if (!selected) return;
-						CCS.JFrame.doubleClickHandler(this.jframe, selected);
+						CCS.JFrame.doubleClickHandler(this.jframe, e, selected);
 					}.bind(this),
 					}.bind(this),
 					description: 'Open the selected item.'
 					description: 'Open the selected item.'
 				},
 				},
@@ -324,7 +324,7 @@ ART.Sheet.define('window.filechooser.browser', {
 						if (selectInfo == null) return;
 						if (selectInfo == null) return;
 						var selectData = selectInfo.data;
 						var selectData = selectInfo.data;
 						var selected = selectInfo.elem;
 						var selected = selectInfo.elem;
-						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, selected);
+						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, e, selected);
 						else if (selectData.type == 'file') this.getOk().fireEvent('press');
 						else if (selectData.type == 'file') this.getOk().fireEvent('press');
 					}.bind(this)
 					}.bind(this)
 				},
 				},
@@ -387,7 +387,7 @@ ART.Sheet.define('window.filechooser.browser', {
 						var selected = hTable.selectedRows[0];
 						var selected = hTable.selectedRows[0];
 						if (!selected) return;
 						if (!selected) return;
 						var selectData = selected.get('data', 'filedata', true);
 						var selectData = selected.get('data', 'filedata', true);
-						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, selected);
+						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, e, selected);
 						//Leaving this code here for when overwrite does work.
 						//Leaving this code here for when overwrite does work.
 						/*else if (selectData.type == 'file') {
 						/*else if (selectData.type == 'file') {
 							locationInput.set('value', selectData.path.split('/').getLast());
 							locationInput.set('value', selectData.path.split('/').getLast());

+ 0 - 696
apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileBrowser.js.orig

@@ -1,696 +0,0 @@
-// 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: File Browser
-provides: [CCS.FileBrowser, CCS.FileChooser, CCS.FileSaver]
-requires: [ccs-shared/CCS.JBrowser, fancyupload/FancyUpload3.Attach, More/URI, More/Fx.Reveal, Widgets/Element.Data]
-script: CCS.FileBrowser.js
-
-...
-*/
-ART.Sheet.define('window.filebrowser.browser', {
-	'min-width': 620
-});
-
-ART.Sheet.define('window.filebrowser.alert content', {
-	'padding': 20,
-	'font-size': 14,
-	'text-align': 'center'
-}, 'css');
-
-ART.Sheet.define('window.filechooser.browser.alert', {
-	'header-height': 54,
-	'min-height': 240,
-	'min-width': 300
-});
-ART.Sheet.define('window.filechooser.browser.alert content', {
-	'padding': 0
-}, 'css');
-
-ART.Sheet.define('window.filechooser.browser', {
-	'header-height': 54,
-	'min-height': 240,
-	'min-width' : 300,
-	'footer-height':30 
-});
-
-(function(){
-
-	var filebrowser = {
-	
-		options: {
-			//used for ART styling (see above styles)
-			className: 'art filebrowser browser logo_header',
-			//the prefixed path to the actions
-			//this value is prepended with "/"
-			//and is given the action as a suffix
-			//for example: /filebrowser/view
-			filesystem: 'filebrowser',
-			//either 'view' or 'chooser'
-			//'view' = the main browser; a standard table of files
-			//'chooser' = the same, but customized for the file chooser
-			view: 'view',
-			//filter: either 'file', 'dir', or 'any'
-			//used to enforce choices available in the chooser
-			//ignored in the filebrowser
-			filter: 'any',
-			windowTitler: function(title) {
-				return title + " :: File Browser";
-			}
-		},
-
-		initialize: function(path, options) {
-			this.parent(path, options);
-			this.addEvent('load', this.setup.bind(this));
-			this.setupHistory();
-			this.addLinkers();
-			this.removeOkEvents();
-			this.addSaverInfo();
-			this.addItemClickEvents();
-			//when an alert is visible, hide the upload swiff
-			//as it sometimes gets "stuck" to the mouse
-			this.addEvents({
-				alert: function(){
-					if (this.uploader) this.uploader.uploader.box.hide();
-				},
-				endAlert: function(){
-					if (this.uploader) this.uploader.uploader.box.show();
-				}
-			});
-			
-			this.jframe.addFilter('rename', function(container) {
-				if (!container.get('html').contains('dest_path')) return;
-				var src = container.getElement('input[name=src_path]');
-				var dest = container.getElement('input[name=dest_path]');
-				if (!src || !dest) return;
-				dest.set('value', src.get('value').split('/').getLast());
-				dest.select.delay(500, dest);
-			});
-		},
-
-		//configures history for file browser
-		setupHistory: function(){
-			//I don't do anything by default
-			//chooser doesn't have a history
-			//browser does
-		},
-
-		//removes ok events for filechooser
-		removeOkEvents: function(){
-			//do nothing by default
-			//filechooser manipulates ok events
-		},
-
-		//adds item click events
-		addItemClickEvents: function(){
-			//do nothing by default
-			//filechooser adds delegated events
-		},
-
-		//adds fields for saver
-		addSaverInfo: function(){
-			//do nothing by default
-			//filechooser will add fields
-		},
-
-		//add linkers to jFrame for filebrowser
-		addLinkers: function(){
-			this.jframe.addLinkers({
-				'a.fb-move': function(e, link) {
-					e.stop();
-					var toMove = link.get('href').toURI().get('data', true).src_path.split('/').getLast();
-					CCS.saveFile(this, toMove, this.getDirPath(), "Move " + toMove, function(data){
-						var uri = link.get('href').toURI();
-						var params = uri.get('data', true);
-						this.jframe.load({
-							requestPath: link.get('href').split('?')[0],
-							method: 'post',
-							data: {
-								src_path: params.src_path,
-								dest_path: data.path,
-								next: params.next
-							}
-						});
-					}.bind(this), {
-						filesystem: this.options.filesystem,
-						filter: 'dir'
-					});
-				}.bind(this)
-			});
-		},
-
-		//shortcuts for dirlist; the only view we have (in theory)
-		addFBShortcuts: function(){
-			this.jframe.addShortcuts({
-				'Open Selected': {
-					keys: 'enter',
-					shortcut: 'enter',
-					handler: function(e){
-						var table = $(this).getElement('[data-filters*=HtmlTable]');
-						if (!table) return;
-						hTable = table.retrieve('HtmlTable');
-						var selected = hTable.selectedRows[0];
-						if (!selected) return;
-						CCS.JFrame.doubleClickHandler(this.jframe, selected);
-					}.bind(this),
-					description: 'Open the selected item.'
-				},
-			//TODO: Make Keyboard shortcuts more cognizant of the focus being on an input field
-				'Remove Selected': {
-					keys: 'keydown:backspace',
-					shortcut: 'backspace',
-					handler: function(e){
-<<<<<<< HEAD:apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileBrowser.js
-	    			if ($(e.target).match('input, textarea')) return;
-=======
-	    if ($(e.target).match('input, textarea')) return;
->>>>>>> 3b01b00... HUE-83. Save As... dialog is no longer working.:apps/filebrowser/src/filebrowser/static/js/Source/FileBrowser/CCS.FileBrowser.js
-						e.stop();
-						var table = this.getDirList().retrieve('HtmlTable');
-						//the view only allows one item to be selected at a time currently
-						//but selectedRows is an array, so just always use the first one.
-						if (table.selectedRows[0]) this.jframe.callClick(e, table.selectedRows[0].getElement('a.fb-default-rm'));
-					}.bind(this),
-					description: 'Remove the selected file/folder.'
-				}
-			});
-		},
-
-		makeUploader: function(dest){
-			if (Browser.Plugins.Flash.build) {
-				this.uploader = new CCS.FileBrowser.Uploader(dest, this.jframe, {
-					//the DOM element where we're going to display our results
-					list: $(this).getElement('.fb-upload-list'),
-					listContainer: $(this).getElement('.fb-uploader'),
-					button: $(this).getElement('.fb-upload'),
-					uploaderOptions: {
-						//call this url when we upload a file
-						url: '/' + this.options.filesystem + '/upload_flash',
-						container: this.toolbar
-					}
-				});
-			} else {
-				$(this).getElement('.fb-upload').set('target', '_blank');
-			}
-		},
-
-		//when jframe loads, set up the content
-		setup: function(view){
-			if (!this._shortcutsAdded) {
-				this.addFBShortcuts();
-				this._shortcutsAdded = true;
-			}
-			//make the uploader
-			(function(){
-				//note we have a very short delay here; the DOM needs a moment to be there or else you sometimes
-				//get the error "obj.CallFunction is not a function" which means that the JS can't communicate with
-				//the swf file
-				this.makeUploader($(this).getElement('.fb-upload').get('href').toURI().get('data').dest);
-			}).delay(10, this);
-		},
-
-		//returns the file list element
-		getDirList: function(){
-			return $(this).getElement('.fb-file-list');
-		},
-
-		//returns the path relative to the current file system
-		getDirPath: function(){
-			return this.jframe.currentPath.split("?")[0].replace('/' + this.options.filesystem + '/' + this.options.view, '');
-		}
-
-
-	};
-	
-	/*
-		the filebrowser class
-		arguments
-		* path - the base path from the file system; defaults to '/'
-		* options - see options defined w/ notes above in the filebrowser object
-	*/
-	
-	CCS.FileBrowser = new Class(
-		$merge({
-			Extends: CCS.JBrowser
-		}, filebrowser, {
-			//configures history for file browser
-			setupHistory: function(){
-				this.history.setOptions({
-					editable: true,
-					pathFilter: function(path) {
-						return path.replace('/' + this.options.filesystem + '/' + this.options.view, '').split('?')[0];
-					}.bind(this),
-					showPath: false
-				});
-				this.history.addEvent('selectManual', function(path) {
-					this.jframe.load({
-						requestPath: '/filebrowser/view' + path
-					});
-				}.bind(this));
-			}
-		})
-	);
-	
-	/*
-		the file chooser class; not really meant to be used as a standalone
-		see CCS.chooseFile below
-		The exception is when an application requires the file chooser to launch;
-		then there isn't a JBrowser instance already, so use the constructor
-		
-		arguments:
-		* same as FileBrowser (path, options)
-	*/
-	ART.Sheet.define('window.art.browser.filechooser', {
-		'content-border-bottom-color': hsb(0, 0, 60),
-		'min-width': 600
-	});
-	
-	var FileChooser = new Class(
-		$merge({
-			Extends: CCS.JBrowser.Confirm
-		}, filebrowser, {
-			options: {
-				autosize: false,
-				className: 'art filechooser filebrowser browser alert confirm'
-			},
-			removeOkEvents: function() {
-				//remove the closeWin class; this selector is designed to close the window
-				$(this.getOk()).removeClass('closeWin');
-				//remove any click handlers
-				this.getOk().removeEvents('press');
-			},
-			getOk: function() {
-				return this.alertButtons[1];
-			},
-			getSelected: function(table) {
-				var selectInfo = {'data': null, 'elem' : null};
-				hTable = table.retrieve('HtmlTable');
-				var selected = hTable.selectedRows[0];
-				selectInfo.data = selected.get('data', 'filedata', true);
-				if (!selected) return null;
-				else selectInfo.elem = selected;
-				return selectInfo;
-			}
-		})
-	);
-
-	
-	CCS.FileChooser = new Class({
-		Extends: FileChooser,
-		options: {
-			windowTitler: function() {
-				return this.caption || "Choose a File";
-			}
-		},
-		addFBShortcuts: function() {
-			this.jframe.addShortcuts({
-				'Select Highlighted': {
-					keys: 'enter',
-					shortcut: 'enter',
-					handler: function(e){
-						var table = $(this).getElement('[data-filters*=HtmlTable]');
-						if (!table) return;
-						var selectInfo = this.getSelected(table);
-						if (selectInfo == null) return;
-						var selectData = selectInfo.data;
-						var selected = selectInfo.elem;
-						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, selected);
-						else if (selectData.type == 'file') this.getOk().fireEvent('press');
-					}.bind(this)
-				},
-				'Close': {
-					keys: 'escape',
-					shortcut: 'escape',
-					handler: function(e) {
-						Keyboard.stop(e);
-						this.hide();
-					}
-				}
-			});
-		}
-
-	});
-
-	CCS.FileSaver = new Class({		 
-		Extends: FileChooser,
-		options: {
-			windowTitler: function() {
-				return this.caption || "Choose Where To Save This File";
-			}.bind(this)
-		},
-		addSaverInfo: function() {
-			var locationInput = new Element("input", {
-				'type' : 'text',
-				'class': 'fs-locationInput',
-				'value': this.options.fileToMove || ""
-			});
-			var locationDiv = new Element("div", {
-				'class': 'fs-location',
-				'html' : 'File Name:'
-			});
-			locationInput.inject(locationDiv);
-			locationDiv.inject($(this.footer), 'top');
-			this.locationInput = locationInput;
-		},
-		addItemClickEvents: function(){
-			$(this).addEvent('click:relay(.fb-item-row)', function(event, clicked) {
-				var targetData = clicked.get('data', 'filedata', true);
-				var locationInput = $(this).getElement('.fs-locationInput');
-				if (targetData.type === 'file') {
-					locationInput.set('value', targetData.path.split('/').getLast());
-				} else if (targetData.type === 'dir') {
-					locationInput.set('value', this.options.fileToMove);
-				}
-
-			}.bind(this));
-		},
-		addFBShortcuts: function(){
-			this.jframe.addShortcuts({
-			'Select Highlighted': {
-				keys: 'enter',
-				shortcut: 'enter',
-				handler: function(e){
-					if (document.activeElement != this.locationInput) { 
-						var table = $(this).getElement('[data-filters*=HtmlTable]');
-						if (!table) return;
-						hTable = table.retrieve('HtmlTable');
-						var selected = hTable.selectedRows[0];
-						if (!selected) return;
-						var selectData = selected.get('data', 'filedata', true);
-						if (selectData.type == 'dir') CCS.JFrame.doubleClickHandler(this.jframe, selected);
-						//Leaving this code here for when overwrite does work.
-						/*else if (selectData.type == 'file') {
-							locationInput.set('value', selectData.path.split('/').getLast());
-							this.jframe.confirm("Warning!", "This will overwrite " + selectData.path + ".	 Are you sure you want to do this ?",	 function() {
-							this.getOk().fireEvent('press');
-						}.bind(this), {});
-							confirmDialog.show();
-						}*/
-						} else {
-							this.getOk().fireEvent('press');
-						}
-					}.bind(this),
-				description: 'Select the highlighted row or folder.'
-				}
-			});
-
-		}
-	});
-
-	/*
-	Brings up a file chooser for a window
-	arguments:
-	* fsPath - the root path from the file system; defaults to '/'
-	* cap - the caption of the window; defaults to "Choose a File"
-	* fn - the callback to execute when the user choose a file
-	* options - options passed to CCS.FileChooser
-		* plus:
-		* filter: (string) either 'file', 'dir', or 'any' to limit choices the user can make
-	*/
-	CCS.chooseFile = function(jbrowser, fsPath, cap, fn, opt){
-		return jbrowser.alert(cap, '', fn || $empty, opt, function(caption, content, callback, options) {
-			$extend(options || {}, {
-				caption: caption || "Choose a File",
-				className: 'art filechooser filebrowser browser alert',
-				resizable: true
-			});
-			var chooser = new CCS.FileChooser("/filebrowser/view" + fsPath, options);
-			chooser.jframe.addEvent('loadComplete', function() {
-				this.setCaption(chooser.getDirPath() + " :: " + caption);
-			}.bind(chooser));
-			chooser.getOk().addEvent('press', function(){
-				var selected = $(chooser).getElement('.table-tr-selected');
-				var error = function(){
-					var msg = "Please choose a directory.";
-					if (options.filter == "file") msg = "Please choose a file.";
-					chooser.alert(caption || "Choose a File", msg);
-					chooser.fireEvent('badSelection', selected);
-				};
-				if (selected) {
-					if (selected.hasClass('not-selectable')) {
-						error();
-						return;
-					}
-					callback($(chooser).getElement('.table-tr-selected').get('data', 'filedata', true));
-					chooser.hide();
-				} else if (options.filter == "file") {
-					error();
-				} else {
-					callback({
-						path: chooser.getDirPath(),
-						type: 'dir'
-					});
-					chooser.hide();
-				}
-				
-			});
-			return chooser;
-		});
-	};
-	CCS.saveFile = function(jbrowser, toMove, fsPath, cap, fn, opt){
-		return jbrowser.alert(cap, '', fn || $empty, opt, function(caption, content, callback, options) {
-				$extend(options || {}, {
-					caption: caption || "Choose a File",
-					className: 'art filechooser filebrowser browser alert',
-					resizable: true,
-					fileToMove: toMove
-				});
-				var saver = new CCS.FileSaver("/filebrowser/view" + fsPath, options);
-				saver.jframe.addEvent('loadComplete', function() {
-					this.setCaption(saver.getDirPath() + " :: " + caption);
-				}.bind(saver));
-				var locationInput = $(saver).getElement('.fs-locationInput');
-				saver.getOk().addEvent('press', function(){
-				//Get selected row of filebrowser table
-				var selected = $(saver).getElement('.table-tr-selected');
-				var selectedPath, isDirSelected = false;
-				// If there is a row selected create a selected path based on that row
-				// If not create a selected path based on the current displayed directory
-				if(selected) {
-					var selectedData = selected.get('data', 'filedata', true);
-					isDirSelected = selectedData.type == 'dir';
-					selectedPath = selectedData.path;
-				} else {
-					selectedPath = saver.getDirPath();
-				}
-				var error = function(){
-					var msg = "Please choose a directory.";
-					if (options.filter == "file") msg = "Please choose a file.";
-					saver.alert(caption || "Choose a File", msg);
-					saver.fireEvent('badSelection', selected);
-				};
-				//if a directory is selected, move to the selected directory
-				var inputPath = (isDirSelected ? selectedPath + "/" : "") + locationInput.get('value');
-				var returnPath; 
-				//An input path belonging with a slash is assumed to be an absolute path.
-				if (inputPath[0] == '/') returnPath = inputPath;
-				else returnPath = saver.getDirPath() + '/' + inputPath;
-				callback({
-					path: returnPath,
-					type: 'dir'
-				});
-				saver.hide();
-				});
-			return saver;
-		});
-	};
-})();
-
-//have to hack the FancyUpload class a little because it has a hard-coded path in it
-FancyUpload3.Attach.File2 = new Class({
-	Extends: FancyUpload3.Attach.File,
-	render: function(){
-		var result = this.parent();
-		this.ui.progress.element.src = "/static/js/ThirdParty/digitarald-fancyupload/assets/progress-bar/bar.gif";
-		return result;
-	},
-
-	// To handle errors, we parse out the JSON response.  The stock uploader
-	// uses the HTTP status and error string, which is hard to customize 
-	// in Django.
-	onComplete: function() {
-		response_obj = JSON.decode(this.response.text, true);
-		if (response_obj && response_obj.error) {
-			this.response.error = response_obj.error;
-		}
-		this.parent.apply(this, arguments);
-	}
-});
-
-
-CCS.FileBrowser.Uploader = new Class({
-
-	Implements: Options,
-
-	options: {
-		//the DOM element that is hidden that contains the result list
-		//listContainer: null,
-
-		//the DOM element where we're going to display our results
-		//list,
-
-		//the button that prompts the user to select a local file
-		//button: null
-		uploaderOptions: {
-			//call this url when we upload a file
-			//url: null,
-			//container: null
-			path: '/static/js/ThirdParty/digitarald-fancyupload/source/Swiff.Uploader.swf',
-			//this is the key for the file in the POST params
-			fieldName: 'file',
-			method: 'post',
-			appendCookieData: true
-		}
-	},
-
-	initialize: function(dest, jframe, options){
-		this.dest = dest || '/';
-		this.jframe = jframe;
-		this.setOptions(options);
-		this.makeSwf();
-	},
-
-	makeSwf: function(){
-		/**
-		 * Uploader instance
-		 */
-		//create an instance of uploader, attach our click behavior to the "upload" button in the toolbar
-		var list = $(this.options.list);
-		var button = (this.options.button);
-		this.uploader = new FancyUpload3.Attach(list, button, $merge({
-			data: {
-				//dest is the destination path (directory) for the file
-				dest: this.dest
-			},
-			fileClass: FancyUpload3.Attach.File2,
-
-			//when we select an invalid file, show an error
-			onSelectFail: function(files) {
-				files.each(function(file) {
-					new Element('li', {
-						'class': 'file-invalid',
-						events: {
-							click: function() {
-								this.destroy();
-							}
-						}
-					}).adopt(
-						new Element('span', {html: file.validationErrorMessage || file.validationError})
-					).inject(this.list, 'bottom');
-				}, this); 
-			},
-
-			//when we succeed, highlight the element and then, after a moment, transition it out
-			//of view
-			onFileSuccess: function(file) {
-				file.ui.element.highlight('#e6efc2');
-				(function(){
-					file.ui.element.nix();
-				}).delay(300);
-			},
-
-			//when the file failes to upload, display the error and a link to retry
-			onFileError: function(file) {
-				file.ui.cancel.set('html', 'Retry').removeEvents().addEvent('click', function() {
-					file.requeue();
-					return false;
-				});
-
-				new Element('span', {
-					html: file.errorMessage,
-					'class': 'file-error'
-				}).inject(file.ui.cancel, 'after');
-			},
-
-			onFileRequeue: function(file) {
-				file.ui.element.getElement('.file-error').destroy();
-
-				file.ui.cancel.set('html', 'Cancel').removeEvents().addEvent('click', function() {
-					file.remove();
-					return false;
-				});
-
-				this.start();
-			},
-
-			//when all files are complete and there are no errors,
-			//hide the upload display and refresh the file browser
-			onComplete: function() {
-				if (list.getElement('span.file-error')) return;
-				this.hide();
-				this.jframe.refresh();
-			}.bind(this),
-
-			onStop: function(){
-				this.jframe.refresh();
-			}.bind(this),
-
-			//show the file upload display when we start uploading
-			onBeforeStart: this.show.bind(this)
-
-		}, this.options.uploaderOptions));
-		//when we resize jframe, reposition the upload swf
-		var reposition = this.uploader.reposition.bind(this.uploader);
-		this.jframe.addEvent('resize', reposition);
-		this.jframe.markForCleanup(function(){
-			//remove this event on unload
-			this.jframe.removeEvent('resize', reposition);
-		}.bind(this));
-		
-		$(this.jframe).getElements('.fb-cancel-upload').addEvent('click', function(e) {
-			//cancel upload stops the uploader and hides the display of files uploading and refreshes
-			e.stop();
-			if (this.uploader) this.uploader.stop();
-			this.hide();
-			this.jframe.refresh();
-		}.bind(this));
-	},
-
-	//display the upload indicator area
-	show: function(){
-		var list = $(this.options.listContainer);
-		var events = {
-			resize: function(x, y) {
-				//when we resize, reposition the mask and the upload display
-				this.uploadMask.position();
-				this.uploader.position();
-			}.bind(this)
-		};
-		this.jframe.addEvents(events);
-		//if there isn't an uploadMask, create one
-		if (!this.uploadMask) {
-			this.uploadMask = new Mask($(this.jframe), {
-				hideOnClick: true,
-				onHide: function() {
-					//hide the uploader display
-					list.hide();
-					//remove the resize monitor
-					this.jframe.removeEvents(events);
-				}.bind(this)
-			});
-		}
-		//show the mask
-		this.uploadMask.show();
-		//show the upload list
-		list.show().position({ relativeTo: $(this.jframe)});
-	},
-
-	//hide the upload display and the mask
-	hide: function(){
-		if (this.uploadMask) this.uploadMask.hide();
-	}
-});

+ 14 - 0
apps/jframegallery/src/jframegallery/templates/double.click-delegator.html

@@ -24,5 +24,19 @@ limitations under the License.
 		<div data-dblclick-delegate="{'dblclick_loads':'.dblme'}" style="width: 200px; height: 100px; padding: 40px; background: #ccc; border: 1px solid #000; margin: 10px auto;">
 		<div data-dblclick-delegate="{'dblclick_loads':'.dblme'}" style="width: 200px; height: 100px; padding: 40px; background: #ccc; border: 1px solid #000; margin: 10px auto;">
 			<a class="dblme" href="/jframegallery/post-load.alert.popup.html">I show an alert if you double click anywhere inside the box around me.</a>
 			<a class="dblme" href="/jframegallery/post-load.alert.popup.html">I show an alert if you double click anywhere inside the box around me.</a>
 		</div>
 		</div>
+
+		<div data-dblclick-delegate="{'dblclick_loads':'.dblme'}" style="width: 200px; height: 100px; padding: 40px; background: #ccc; border: 1px solid #000; margin: 10px auto;">
+			I'm a div with a double click delegate specified, but no link matching, so I don't do anything.
+		</div>
+
+		<div data-dblclick-delegate="{'dblclick_loads':'.dblme'}" style="width: 200px; height: 100px; padding: 40px; background: #ccc; border: 1px solid #000; margin: 10px auto;">
+			<a class="dblme">I'm an anchor with no href, so clicking me or double clicking my container should do nothing.</a>
+		</div>
+
+		<div data-dblclick-delegate="{'dblclick_loads':'.dblme'}" style="width: 200px; height: 100px; padding: 40px; background: #ccc; border: 1px solid #000; margin: 10px auto;">
+			<a class="dblme ccs-nav_back">I'm a link with no href, but I have the ccs class name ccs-nav_back, so I navigate back in the app window's history. Double clicking my container should send you back to the JFrame gallery.</a>
+		</div>
+
+
 	</body>
 	</body>
 </html>
 </html>

+ 1 - 0
apps/jframegallery/src/jframegallery/templates/errors.html

@@ -23,6 +23,7 @@ limitations under the License.
 	<body>
 	<body>
           <ul>
           <ul>
             Clicking on any of these should trigger an error popup.
             Clicking on any of these should trigger an error popup.
+            <li><a id=e_1 href="/">A request for /</a> </li>
             <li><a id=e_1 href="/jframegallery/error_404">Explicit 404</a> </li>
             <li><a id=e_1 href="/jframegallery/error_404">Explicit 404</a> </li>
             <li><a id=e_2 href="/jframegallery/error_message_exception">Explict message exception</a> </li>
             <li><a id=e_2 href="/jframegallery/error_message_exception">Explict message exception</a> </li>
             <li><a id=e_3 href="/jframegallery/error_popup_exception">Explict popup exception</a> </li>
             <li><a id=e_3 href="/jframegallery/error_popup_exception">Explict popup exception</a> </li>

+ 3 - 0
desktop/core/src/desktop/templates/index.mako

@@ -255,6 +255,9 @@
     <div id="ccs-loading">Launching Hue</div>
     <div id="ccs-loading">Launching Hue</div>
     <a id="ccs-feedback" href="${feedback_url}" target="_blank"><img src="/static/art/feedback-tab.png" width="76" height="26"/></a>
     <a id="ccs-feedback" href="${feedback_url}" target="_blank"><img src="/static/art/feedback-tab.png" width="76" height="26"/></a>
   </div>
   </div>
+  <div class="alert_popup ccs-error-popup">
+    Warning, an AJAX request was made for the Hue desktop which cannot be loaded into an application window. Typically this means that a link clicked has no <em>href</em> value. Please notify the application's author.
+  </div>
 
 
     <script>
     <script>
       if (Browser.Engine.trident) $(document.body).addClass('IEroot');
       if (Browser.Engine.trident) $(document.body).addClass('IEroot');

+ 3 - 0
desktop/core/static/css/desktop.css

@@ -14,6 +14,9 @@ body {
 	padding: 0px;
 	padding: 0px;
 	font-size: 12px;
 	font-size: 12px;
 }
 }
+body>.ccs-error-popup {
+	display: none;
+}
 #bg {
 #bg {
 	width: 100%;
 	width: 100%;
 	position: absolute;
 	position: absolute;

+ 4 - 3
desktop/core/static/js/Source/CCS/CCS.JFrame.js

@@ -100,9 +100,10 @@ CCS.JFrame = new Class({
 		//the selector to match clicks against for delegation; defaults to only links
 		//the selector to match clicks against for delegation; defaults to only links
 		clickRelays: 'a',
 		clickRelays: 'a',
 		//given the response and response text, this method determines if there's been a serverside error
 		//given the response and response text, this method determines if there's been a serverside error
-		errorDetector: function(requestInstance, repsonseText) {
+		errorDetector: function(requestInstance, responseText) {
 			//flag this as an error
 			//flag this as an error
 			return repsonseText.contains('ccs-error-popup');
 			return repsonseText.contains('ccs-error-popup');
+			return responseText.contains('ccs-error-popup');
 		},
 		},
 		getScroller: function(){
 		getScroller: function(){
 			return this.element;
 			return this.element;
@@ -345,7 +346,6 @@ CCS.JFrame = new Class({
 			content.viewElement = viewElement;
 			content.viewElement = viewElement;
 		}
 		}
 		this._applyRenderers(content, options);
 		this._applyRenderers(content, options);
-
 	},
 	},
 
 
 	/*
 	/*
@@ -636,6 +636,7 @@ CCS.JFrame = new Class({
 			spinnerTarget: this.options.spinnerTarget || this.element,
 			spinnerTarget: this.options.spinnerTarget || this.element,
 			spinnerOptions: { fxOptions: {duration: 200} },
 			spinnerOptions: { fxOptions: {duration: 200} },
 			onFailure: this.error.bind(this),
 			onFailure: this.error.bind(this),
+			evalScripts: false,
 			onRequest: function(){
 			onRequest: function(){
 				if (this._request) this._request.cancel();
 				if (this._request) this._request.cancel();
 				this._request = request;
 				this._request = request;
@@ -663,7 +664,7 @@ CCS.JFrame = new Class({
 	_requestSuccessHandler: function(request, html, options) {
 	_requestSuccessHandler: function(request, html, options) {
 		var error, blankWindowWithError;
 		var error, blankWindowWithError;
 		if (this._checkForEmptyErrorState(request, html)) {
 		if (this._checkForEmptyErrorState(request, html)) {
-			serverSideError = true;
+			error = true;
 			if (!this.loadedOnce) blankWindowWithError = true;
 			if (!this.loadedOnce) blankWindowWithError = true;
 		}
 		}
 		var responsePath = request.getHeader('X-Hue-JFrame-Path');
 		var responsePath = request.getHeader('X-Hue-JFrame-Path');

+ 14 - 14
desktop/core/static/js/Source/CCS/CCS.Request.js

@@ -40,23 +40,23 @@ script: CCS.Request.js
 				this.genericErrorAlert(data);
 				this.genericErrorAlert(data);
 			},
 			},
 			onFailure: function() {
 			onFailure: function() {
-                                var msg;
+				var msg;
 				if(this.status == 0) {
 				if(this.status == 0) {
-                                        msg = "The Hue server can not be reached. (Is the server running ?)";
+					msg = "The Hue server can not be reached. (Is the server running ?)";
 				} else if (Browser.Engine.trident && (this.status == 12030 || this.status == 12031)) {
 				} else if (Browser.Engine.trident && (this.status == 12030 || this.status == 12031)) {
-                                        
-                                        //In IE, the attempt to get the profile upon logout often results in a status
-                                        //error 12030 or 12031.  The request completes successfully, just that IE is 
-                                        //unhappy with the response.  This condition catches that condition and passes it
-                                        //to handleLoginRequired(), which is what should happen.
-                                        this.handleLoginRequired();
-                                        return;
-                                } else {
-                                        msg = "Error " + this.status + " retrieving <a target='_blank' href='" + this.options.url + "'>link</a>";
+					
+					//In IE, the attempt to get the profile upon logout often results in a status
+					//error 12030 or 12031.  The request completes successfully, just that IE is 
+					//unhappy with the response.  This condition catches that condition and passes it
+					//to handleLoginRequired(), which is what should happen.
+					this.handleLoginRequired();
+					return;
+				} else {
+					msg = "Error " + this.status + " retrieving <a target='_blank' href='" + this.options.url + "'>link</a>";
 				}
 				}
-                                this.genericErrorAlert({ 
-                                        message: msg
-                                });
+				this.genericErrorAlert({ 
+					message: msg
+				});
 			}
 			}
 		},
 		},
 
 

+ 3 - 12
desktop/core/static/js/Source/JFrameFilters/CCS.JFrame.DoubleClickDelegate.js

@@ -23,7 +23,7 @@ script: CCS.JFrame.DoubleClickDelegate.js
 ...
 ...
 */
 */
 
 
-CCS.JFrame.doubleClickHandler = function(jframe, delegate){
+CCS.JFrame.doubleClickHandler = function(jframe, event, delegate){
 	var getLink = function(delegate){
 	var getLink = function(delegate){
 		//retrieve configuration from the css property JSON
 		//retrieve configuration from the css property JSON
 		var data = delegate.get('data', 'dblclick-delegate', true);
 		var data = delegate.get('data', 'dblclick-delegate', true);
@@ -35,16 +35,7 @@ CCS.JFrame.doubleClickHandler = function(jframe, delegate){
 	};
 	};
 	
 	
 	var link = getLink(delegate);
 	var link = getLink(delegate);
-	if (link) {
-		if (link.get('target')) {
-			CCS.Desktop.launch(link.get('target'), link.get('href'));
-		} else {
-			jframe.load({
-				requestPath: link.get('href')
-			});
-		}
-	}
-
+	if (link) jframe.callClick(event, link);
 };
 };
 
 
 CCS.JFrame.addGlobalFilters({
 CCS.JFrame.addGlobalFilters({
@@ -54,7 +45,7 @@ CCS.JFrame.addGlobalFilters({
 
 
 		//define our handler
 		//define our handler
 		var handler = function(e, delegate){
 		var handler = function(e, delegate){
-			CCS.JFrame.doubleClickHandler(this, delegate);
+			CCS.JFrame.doubleClickHandler(this, e, delegate);
 		}.bind(this);
 		}.bind(this);
 		//add this behavior to the delegate
 		//add this behavior to the delegate
 		container.addEvent('dblclick:relay([data-dblclick-delegate])', handler);
 		container.addEvent('dblclick:relay([data-dblclick-delegate])', handler);