Menu.js 3.56 KB
/**
 * Menu.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * Creates a new menu.
 *
 * @-x-less Menu.less
 * @class tinymce.ui.Menu
 * @extends tinymce.ui.FloatPanel
 */
define("tinymce/ui/Menu", [
	"tinymce/ui/FloatPanel",
	"tinymce/ui/MenuItem",
	"tinymce/ui/Throbber",
	"tinymce/util/Tools"
], function(FloatPanel, MenuItem, Throbber, Tools) {
	"use strict";

	return FloatPanel.extend({
		Defaults: {
			defaultType: 'menuitem',
			border: 1,
			layout: 'stack',
			role: 'application',
			bodyRole: 'menu',
			ariaRoot: true
		},

		/**
		 * Constructs a instance with the specified settings.
		 *
		 * @constructor
		 * @param {Object} settings Name/value object with settings.
		 */
		init: function(settings) {
			var self = this;

			settings.autohide = true;
			settings.constrainToViewport = true;

			if (typeof settings.items === 'function') {
				settings.itemsFactory = settings.items;
				settings.items = [];
			}

			if (settings.itemDefaults) {
				var items = settings.items, i = items.length;

				while (i--) {
					items[i] = Tools.extend({}, settings.itemDefaults, items[i]);
				}
			}

			self._super(settings);
			self.classes.add('menu');
		},

		/**
		 * Repaints the control after a layout operation.
		 *
		 * @method repaint
		 */
		repaint: function() {
			this.classes.toggle('menu-align', true);

			this._super();

			this.getEl().style.height = '';
			this.getEl('body').style.height = '';

			return this;
		},

		/**
		 * Hides/closes the menu.
		 *
		 * @method cancel
		 */
		cancel: function() {
			var self = this;

			self.hideAll();
			self.fire('select');
		},

		/**
		 * Loads new items from the factory items function.
		 *
		 * @method load
		 */
		load: function() {
			var self = this, time, factory;

			function hideThrobber() {
				if (self.throbber) {
					self.throbber.hide();
					self.throbber = null;
				}
			}

			factory = self.settings.itemsFactory;
			if (!factory) {
				return;
			}

			if (!self.throbber) {
				self.throbber = new Throbber(self.getEl('body'), true);

				if (self.items().length === 0) {
					self.throbber.show();
					self.fire('loading');
				} else {
					self.throbber.show(100, function() {
						self.items().remove();
						self.fire('loading');
					});
				}

				self.on('hide close', hideThrobber);
			}

			self.requestTime = time = new Date().getTime();

			self.settings.itemsFactory(function(items) {
				if (items.length === 0) {
					self.hide();
					return;
				}

				if (self.requestTime !== time) {
					return;
				}

				self.getEl().style.width = '';
				self.getEl('body').style.width = '';

				hideThrobber();
				self.items().remove();
				self.getEl('body').innerHTML = '';

				self.add(items);
				self.renderNew();
				self.fire('loaded');
			});
		},

		/**
		 * Hide menu and all sub menus.
		 *
		 * @method hideAll
		 */
		hideAll: function() {
			var self = this;

			this.find('menuitem').exec('hideMenu');

			return self._super();
		},

		/**
		 * Invoked before the menu is rendered.
		 *
		 * @method preRender
		 */
		preRender: function() {
			var self = this;

			self.items().each(function(ctrl) {
				var settings = ctrl.settings;

				if (settings.icon || settings.image || settings.selectable) {
					self._hasIcons = true;
					return false;
				}
			});

			if (self.settings.itemsFactory) {
				self.on('postrender', function() {
					if (self.settings.itemsFactory) {
						self.load();
					}
				});
			}

			return self._super();
		}
	});
});