text: /*******************************************************************************
   * @license
   * Copyright (c) 2010, 2011 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials are made 
   * available under the terms of the Eclipse Public License v1.0 
   * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
   * License v1.0 (http://www.eclipse.org/org/documents/edl-v10.html). 
   * 
   * Contributors: IBM Corporation - initial API and implementation
   ******************************************************************************/
   /*global define document window*/
  
  define(['i18n!orion/nls/messages', 'dojo', 'orion/util', 'orion/section', 'orion/commands', 'orion/URITemplate'], function(messages, dojo, mUtil, mSection, mCommands, URITemplate) {
  
  	/**
  	 * Constructs a new Outliner with the given options.
  	 * @name orion.outliner.Outliner
  	 * @class An Outliner is a visual component that renders an itemized overview of a resource and acts as 
  	 * a selection provider on that resource. The itemized overview is obtained from the {@link orion.outliner.OutlineService}.
  	 * @param {Object} options The options object
  	 * @param {Object} options.parent The parent DOM element to put this outliner inside
  	 * @param {orion.serviceRegistry.ServiceRegistry} options.serviceRegistry The service registry.
  	 * @param {orion.commands.CommandService} options.commandService
  	 * @param {Service of type orion.outliner.OutlineService} options.outlineService The outline service to use.
  	 * @param {orion.selection.Selection} [options.selectionService] If provided, the 
  	 * selection service will be notified on outline selection rather than using anchor tag hrefs.
  	 */
  	function Outliner(options) {
  		this._init(options);
  	}
  	Outliner.prototype = /** @lends orion.outliner.Outliner.prototype */ {
  		_init: function(options) {
  			var parent = options.parent;
  			if (typeof(parent) === "string") { //$NON-NLS-0$
  				parent = dojo.byId(parent);
  			}
  			if (!parent) { throw "no parent"; } //$NON-NLS-0$
  			if (!options.outlineService) {throw "no outline service"; } //$NON-NLS-0$
  			this._parent = parent;
  			this._serviceRegistry = options.serviceRegistry;
  			this._outlineService = options.outlineService;
  			this._commandService = options.commandService;
  			this._selectionService = options.selectionService;
  			var self = this;
  			dojo.when(this._outlineService, function(service) {
  				service.addEventListener("outline", function(outlineModel, title, providerId) { //$NON-NLS-0$
  					self.providerId = providerId;
  					self._renderHeadingAndMenu(self.outlineProviders);
  					self._renderOutline.apply(self, Array.prototype.slice.call(arguments));
  				});
  			});
  			
  			var switchOutlineCommand = new mCommands.Command({
  				name: messages["Switch"],
  				tooltip: messages["Switch the type of outliner used"],
  				id: "eclipse.edit.outline.switch", //$NON-NLS-0$
  				visibleWhen: function(item) {
  					return true;
  				},
  				choiceCallback: dojo.hitch(this, this._menuCallback)});
  			this._commandService.addCommand(switchOutlineCommand);
  		},
  		outlineChanged: function(outlinerService, title, contents) {
  			var self = this;
  			outlinerService.getOutline(contents, title).then(function(outlineModel) {
  				self._renderOutline(outlineModel, title);
  			});
  		},
  		/**
  		 * Clients can connect to this function to be notified of user choice of outline provider.
  		 */
  		setSelectedProvider: function(/**ServiceReference*/ provider) {
  			this.providerId = provider.getProperty("id"); //$NON-NLS-0$
  			this.providerName = provider.getProperty("name"); //$NON-NLS-0$
  		},
  		setOutlineProviders: function(providers) {
  			this.outlineProviders = providers;
  			this._renderHeadingAndMenu(this.outlineProviders);
  		},
  		_renderOutline: function(outlineModel, title) {
  			var contentParent = dojo.byId("outlineSectionContent"); //$NON-NLS-0$
  			if (!contentParent) {
  				this._renderHeadingAndMenu();
  			}
  			outlineModel = outlineModel instanceof Array ? outlineModel : [outlineModel];
  			if (outlineModel) {
  				if (this.outlineNode) {
  					dojo.empty(this.outlineNode);
  				} else {
  					this.outlineNode = dojo.create("ul", {className: "outline"}); //$NON-NLS-1$ //$NON-NLS-0$
  				}
  				for (var i=0; i < outlineModel.length; i++) {
  					this._renderElement(this.outlineNode, outlineModel[i], title);
  				}
  				dojo.place(this.outlineNode, "outlineSectionContent", "last"); //$NON-NLS-1$ //$NON-NLS-0$
  			}
  		},
  		_menuCallback: function() {
  			var choices = [];
  			for (var i=0; i < this.outlineProviders.length; i++) {
  				var provider = this.outlineProviders[i],
  				    name = provider.getProperty("name") || (provider.name + provider.serviceId) || "undefined", //$NON-NLS-1$ //$NON-NLS-0$
  				    prefix = (provider.getProperty("id") === this.providerId) ? "&bull; " : ""; //$NON-NLS-1$ //$NON-NLS-0$
  				choices.push({
  					name: prefix + name,
  					callback: dojo.hitch(this, this.setSelectedProvider, provider)});
  			}
  			return choices;
  		},
  		_renderHeadingAndMenu: function(/**ServiceReference*/ outlineProviders) {
  			if (!this.outlineSection) {
  				this.outlineSection = new mSection.Section(this._parent, {
  					id: "outlinerHeading", //$NON-NLS-0$
  					title: messages["Outliner"],
  					content: '<div id="outlineSectionContent"></div>', //$NON-NLS-0$
  					useAuxStyle: true
  				});
  				this._commandService.registerCommandContribution(this.outlineSection.selectionNode.id, "eclipse.edit.outline.switch", 1); //$NON-NLS-0$
  			}
  			dojo.empty(this.outlineSection.selectionNode.id);
  			if (outlineProviders.length > 1) {
  				this._commandService.renderCommands(this.outlineSection.selectionNode.id, this.outlineSection.selectionNode.id, {}, this, "button"); //$NON-NLS-0$
  			}
  		},
  		_renderElement: function(/**DOMNode*/ parentNode, /**Object*/ element, title) {
  			if (!element) {
  				return;
  			}
  			var elementNode = dojo.create("li", null, parentNode, "last"); //$NON-NLS-1$ //$NON-NLS-0$
  			if (element.className) {
  				dojo.addClass(elementNode, element.className);
  			}
  			if (element.href) {
  				this._createLink(element.label, element.href, elementNode);
  			} else if (element.line || element.column || element.start) {
  				var start = element.start || null,
  				    end = element.end || null,
  				    line = element.line || null,
  				    offset = element.column || null,
  				    text = element.text || null,
  				    href = new URITemplate("#{,resource,params*}").expand({resource: title, params: element}); //$NON-NLS-0$
  				this._createLink(element.label, href, elementNode);
  			} else if (element.label) {
  				dojo.place(document.createTextNode(element.label), elementNode, "only"); //$NON-NLS-0$
  			}
  			var children = element.children;
  			if (children) {
  				var newParent = dojo.create("ul", null, elementNode, "last"); //$NON-NLS-1$ //$NON-NLS-0$
  				for (var i = 0; i < children.length; i++) {
  					this._renderElement(newParent, children[i], title);
  				}
  			}
  		},
  		/** @returns {DOMNode} */
  		_createLink: function(text, href, parentNode) {
  			var link = dojo.create("a", null, parentNode, "last"); //$NON-NLS-1$ //$NON-NLS-0$
  			// if there is no selection service, we rely on normal link following
  			if (!this._selectionService) {
  				link.href = href;
  			} else {
  				dojo.style(link, "cursor", "pointer"); //$NON-NLS-1$ //$NON-NLS-0$
  			}
  			dojo.addClass(link, "navlinkonpage"); //$NON-NLS-0$
  			dojo.place(document.createTextNode(text), link);
  			// if a selection service has been specified, we will use it for link selection.
  			// Otherwise we assume following the href in the anchor tag is enough.
  			if (this._selectionService) {
  				var selectionService = this._selectionService;
  				var url = href;
  				dojo.connect(link, "onclick", link, function(event) { //$NON-NLS-0$
  					if (mUtil.openInNewWindow(event)) {
  						mUtil.followLink(url, event);
  					} else {
  						selectionService.setSelections(url);
  					}
  				});
  			}
  			return link;
  		}
  	};
  	Outliner.prototype.constructor = Outliner;
  	
  	/**
  	 * Constructs a new outline service. Clients should obtain an outline service by requesting
  	 * the service <code>orion.edit.outline</code> from the service registry. This service constructor
  	 * is only intended to be used by page service registry initialization code.
  	 * @name orion.outliner.OutlineService
  	 * @class <code>OutlineService</code> dispatches an event when an outline for a resource is available.
  	 * Clients may listen to the service's <code>outline</code> event to receive notification when this occurs.
  	 * @param {orion.serviceregistry.ServiceRegistry} options.serviceRegistry The service registry to use for obtaining
  	 * outline providers.
  	 * @param {orion.preferences.PreferencesService} options.preferences The preferences service to use.
  	 */
  	function OutlineService(options) {
  		this._serviceRegistry = options.serviceRegistry;
  		this._preferences = options.preferences;
  		this._serviceRegistration = this._serviceRegistry.registerService("orion.edit.outline", this); //$NON-NLS-0$
  		this._outlinePref = this._preferences.getPreferences("/edit/outline"); //$NON-NLS-0$
  		this._provider = new dojo.Deferred();
  	}
  	OutlineService.prototype = /** @lends orion.outliner.OutlineService.prototype */ {
  		setOutlineProviders: function(/**ServiceReference[]*/ providers) {
  			this.providers = providers;
  			// Check pref to see if user has chosen a preferred outline provider
  			var self = this;
  			dojo.when(this._outlinePref, function(pref) {
  				var provider;
  				for (var i=0; i < providers.length; i++) {
  					provider = providers[i];
  					if (pref.get("outlineProvider") === providers[i].getProperty("id")) { //$NON-NLS-1$ //$NON-NLS-0$
  						break;
  					}
  				}
  				if (provider) {
  					self.setProvider(provider);
  				}
  			});
  		},
  		setProvider: function(/**ServiceReference*/ provider) {
  			if (this._provider.fired !== -1) {
  				this._provider = new dojo.Deferred();
  			}
  			this._provider.callback(provider);
  			var id = provider.getProperty("id"); //$NON-NLS-0$
  			if (id) {
  				this._outlinePref.then(function(pref) {
  					pref.put("outlineProvider", id); //$NON-NLS-0$
  				});
  			}
  		},
  		/** @returns {dojo.Deferred} */
  		getProvider: function() {
  			return this._provider;
  		},
  		emitOutline: function(contents, title, providerId) {
  			var self = this;
  			dojo.when(this.getProvider(), function(provider) {
  				self._serviceRegistry.getService(provider).getOutline(contents, title).then(function(outline) {
  					self._serviceRegistration.dispatchEvent("outline", outline, title, provider.getProperty("id")); //$NON-NLS-1$ //$NON-NLS-0$
  				});
  			});
  		}
  	};
  	OutlineService.prototype.constructor = OutlineService;
  	
  	//return module exports
  	return {
  		Outliner: Outliner,
  		OutlineService: OutlineService
  	};
  });
  
  