diff --git a/src/ui/yui/config/yui-modules.js b/src/ui/yui/config/yui-modules.js index 7c1fca1ac8..1cf4f6d1bc 100644 --- a/src/ui/yui/config/yui-modules.js +++ b/src/ui/yui/config/yui-modules.js @@ -55,7 +55,7 @@ 'button-a': { group: 'AlloyEditor', path: 'buttons/button-a.js', - requires: ['button-base', 'event-valuechange', 'node-focusmanager'] + requires: ['button-base', 'event-valuechange'] }, 'button-h1': { @@ -133,7 +133,7 @@ 'toolbar-base': { group: 'AlloyEditor', path: 'toolbars/toolbar-base.js', - requires: ['plugin', 'node-base', 'transition'] + requires: ['plugin', 'node-base', 'node-focusmanager', 'transition'] }, 'toolbar-position': { @@ -145,19 +145,13 @@ 'toolbar-add': { group: 'AlloyEditor', path: 'toolbars/toolbar-add.js', - requires: ['widget-base', 'widget-position', 'widget-position-constrain', 'widget-position-align', 'toolbar-base'] + requires: ['toolbar-base', 'toolbar-position', 'widget-base', 'widget-position', 'widget-position-constrain', 'widget-position-align'] }, 'toolbar-styles': { group: 'AlloyEditor', path: 'toolbars/toolbar-styles.js', - requires: ['toolbar-base', 'widget-base', 'widget-position', 'widget-position-constrain'] - }, - - 'toolbar-image': { - group: 'AlloyEditor', - path: 'toolbars/toolbar-image.js', - requires: ['dom-screen', 'widget-base', 'widget-position', 'widget-position-constrain', 'toolbar-base'] + requires: ['toolbar-base', 'toolbar-position', 'widget-base', 'widget-position', 'widget-position-constrain', 'widget-position-align'] }, 'selector-patch': { diff --git a/src/ui/yui/demo/index.html b/src/ui/yui/demo/index.html index f2a45e5850..137d31bd39 100755 --- a/src/ui/yui/demo/index.html +++ b/src/ui/yui/demo/index.html @@ -89,11 +89,12 @@
toolbarsHide
event if none of the toolbars or their child nodes is the element user is
+ * Fires widgetsHide
event if none of the toolbars or their child nodes is the element user is
* currently interacting.
*
* @method _onDocInteract
@@ -193,29 +235,26 @@ YUI.add('alloy-editor', function(Y) {
* @param {EventFacade} event EventFacade object
*/
_onDocInteract: function(event) {
- var editorInstance,
- result,
+ var result,
srcNode;
srcNode = this.get('srcNode');
result = (srcNode === event.target) || (srcNode.contains(event.target));
- editorInstance = CKEDITOR.instances[srcNode.get('id')];
-
- result = result || Y.some(editorInstance.config.toolbars, function(toolbar) {
- return toolbar.ownsNode(event.target);
+ result = result || Y.some(this._widgets, function(widget) {
+ return widget.ownsNode(event.target);
});
if (!result) {
- this._editor.fire('toolbarsHide');
+ this._editor.fire('widgetsHide');
}
},
/**
* Handles key events in the editor:
- * - ALT + F10: focus the toolbar
- * - ESC: hide visible toolbars
+ * - ALT + F10: focus the widget
+ * - ESC: hide visible widgets
*
* @method _onEditorKey
* @param {EventFacade} event Event that triggered when user pressed a key inside the editor.
@@ -223,60 +262,58 @@ YUI.add('alloy-editor', function(Y) {
*/
_onEditorKey: function(event) {
if (event.altKey && event.keyCode === KEY_F10) {
- this._focusVisibleToolbar();
+ this._focusVisibleWidget();
} else if (event.keyCode === KEY_ESC) {
- this._hideToolbars();
+ this._hideWidgets();
}
},
/**
- * Handles activating a toolbar.
+ * Handles activating a widget.
*
* @method _onToolbarActive
* @protected
*/
- _onToolbarActive: function(event) {
- this._activeToolbar = event.data;
+ _onWidgetActive: function(event) {
+ this._activeWidget = event.data;
},
/**
- * Handles key events in the toolbar:
- * - TAB: focus next toolbar
+ * Handles key events in the widget:
+ * - TAB: focus next widget
* - ESC: focus the editor
*
- * @method _onToolbarKey
+ * @method _onWidgetKey
* @param {Event} event keyboard event
* @protected
*/
- _onToolbarKey: function(event) {
+ _onWidgetKey: function(event) {
if (event.data.keyCode === KEY_TAB) {
event.data.preventDefault();
- this._focusNextToolbar();
+ this._focusNextWidget();
} else if (event.data.keyCode === KEY_ESC) {
- this._activeToolbar.blur();
- this._activeToolbar = null;
- this._focusedToolbar = null;
+ this._activeWidget.blur();
+ this._activeWidget = null;
+ this._focusedWidget = null;
- this._hideToolbars();
+ this._hideWidgets();
}
},
/**
- * Checks if a toolbar was focused by keyboard,
+ * Checks if a widget was focused by keyboard,
* and if so, returns the focus to it.
*
- * @method _returnFocusToToolbar
+ * @method _returnFocusToWidget
* @protected
*/
- _returnFocusToToolbar: function() {
- var toolbar = this._focusedToolbar;
+ _returnFocusToWidget: function() {
+ var widget = this._focusedWidget;
- if (toolbar) {
- var currentButton = toolbar.getActiveButton();
-
- this._focusVisibleToolbar(currentButton);
+ if (widget) {
+ widget.focus(widget.getActiveButton());
}
},
@@ -294,15 +331,15 @@ YUI.add('alloy-editor', function(Y) {
},
/**
- * Validates toolbars attribute. May be empty string or null, which means the current instance of AlloyEdtor
- * shouldn't have any toolbars, or Object, which properties are the desired toolbars.
+ * Validates widgets attribute. May be empty string or null, which means the current instance of AlloyEdtor
+ * shouldn't have any widgets, or Object, which properties are the desired widgets.
*
- * @method _validateToolbars
+ * @method _validateWidgets
* @protected
* @param {Any} value The value which should be validated.
* @return {Boolean} True if the value was accepted, false otherwise.
*/
- _validateToolbars: function(value) {
+ _validateWidgets: function(value) {
return (value === '' || Lang.isObject(value) || Lang.isNull(value));
}
}, {
@@ -339,13 +376,13 @@ YUI.add('alloy-editor', function(Y) {
* make AlloyEditor to work properly.
*
* @attribute extraPlugins
- * @default 'uicore,selectionregion,dropimages,placeholder,linktooltip,uiloader'
+ * @default 'uicore,selectionregion,dropimages,placeholder,linktooltip'
* @writeOnce
* @type {String}
*/
extraPlugins: {
validator: Lang.isString,
- value: 'uicore,selectionregion,dropimages,placeholder,linktooltip,uiloader',
+ value: 'uicore,selectionregion,dropimages,placeholder,linktooltip',
writeOnce: true
},
@@ -412,44 +449,72 @@ YUI.add('alloy-editor', function(Y) {
},
/**
- * Specifies the Toolbars of the current editor instance. The value should be an object
- * with one or more properties. Each of these will represent a toolbar. The value of these properties
- * can be string or Object. If String, the value of the string should represent the buttons. If object,
- * one of these properties should be 'buttons', which will represent the buttons of this Toolbar and
- * all the other properties will be different configuration parameters of the Toolbar. Example:
+ * Specifies the Widgets of the current editor instance. The value should be an array
+ * with one or more items. Each of these will represent a widget. The value of these items
+ * can be an instance of the widget or a configuration Object. If object, it should contain an 'fn'
+ * property with the constructor of the Widget and optionally a 'cfg' object to be passed to the
+ * class on instantiation. Example:
+ *
*
- * toolbars: {
- * table: { // this would be a toolbar configuration, which specifies both Toolbar attributes and its buttons
- * buttons: ['button1', button2],
- * width: 25
+ * widgets: [
+ * {
+ * fn: Y.ToolbarStyles,
+ * cfg: {
+ * visible: false
+ * }
* },
- * add: ['image', 'code'], // here we specify only the buttons of this toolbar and leave the other options unmodified.
- * image: ['left', 'right'],
- * styles: ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter']
- * }
+ * {
+ * fn: Y.ToolbarAdd
+ * },
+ * new Y.CustomWidget()
+ * ]
*
*
+ * Widgets may take part in the focus rotation for accessibility purposes. In general, the should expose
+ * the following methods:
+ *
+ * - focus() To gain focus on the widget
+ * - setEditor(editor) To receive the native editor instance on already created Widgets. When passing the
+ * wiget constructor, an 'editor' attribute with the nativeEditor will be passed down in the config object.
+ *
* @default
- * toolbars {
- * add: ['image'],
- * image: ['left', 'right'],
- * styles: ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter']
- * }
- * @attribute toolbars
- * @type Object
+ * widgets: [
+ * {
+ * fn: Y.ToolbarStyles,
+ * cfg: {
+ * visible: false
+ * }
+ * },
+ * {
+ * fn: Y.ToolbarAdd,
+ * cfg: {
+ * visible: false
+ * }
+ * }
+ * ]
+ * @type Array
*/
- toolbars: {
- validator: '_validateToolbars',
- value: {
- add: ['image'],
- image: ['left', 'right'],
- styles: ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter']
- }
+ widgets: {
+ validator: '_validateWidgets',
+ value: [
+ {
+ fn: Y.ToolbarStyles,
+ cfg: {
+ visible: false
+ }
+ },
+ {
+ fn: Y.ToolbarAdd,
+ cfg: {
+ visible: false
+ }
+ }
+ ]
}
}
});
Y.AlloyEditor = AlloyEditor;
}, '', {
- requires: ['base-build', 'node-base']
+ requires: ['base-build', 'node-base', 'toolbar-add', 'toolbar-styles']
});
\ No newline at end of file
diff --git a/src/ui/yui/src/assets/css/alloy-editor-core.css b/src/ui/yui/src/assets/css/alloy-editor-core.css
index 38017b43d5..f7b09e8000 100644
--- a/src/ui/yui/src/assets/css/alloy-editor-core.css
+++ b/src/ui/yui/src/assets/css/alloy-editor-core.css
@@ -1,3 +1,4 @@
+/* line 1, ../sass/alloy-editor-core.scss */
.yui3-toolbarstyles-hidden, .yui3-toolbaradd-hidden, .yui3-toolbaraddtrigger-hidden, .yui3-toolbarimage-hidden, .yui3-linktooltip-hidden {
display: none;
}
@@ -7,9 +8,7 @@
z-index: 1;
}
-.alloy-editor-toolbar {
- padding: 10px;
-}
+/* line 11, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar .alloy-editor-button {
height: 24px;
margin: 0 10px;
@@ -25,9 +24,7 @@
outline: 2px solid #44b6e0 !important;
}
-.alloy-editor-toolbar-styles .link-wrapper .alloy-editor-button {
- margin-left: 10px;
-}
+/* line 20, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container {
display: inline-block;
padding: 2px;
@@ -54,15 +51,7 @@
position: relative;
}
-.alloy-editor-toolbar-add-trigger {
- padding: 0;
-}
-.alloy-editor-toolbar-add-trigger .alloy-editor-button {
- height: 24px;
- padding: 5px;
- width: 24px;
-}
-
+/* line 48, ../sass/alloy-editor-core.scss */
.alloy-editor-toolbar-add .alloy-editor-button {
font-size: 20px;
}
diff --git a/src/ui/yui/src/assets/css/skin/dark/alloy-editor-skin.css b/src/ui/yui/src/assets/css/skin/dark/alloy-editor-skin.css
index d1ef8d9c4e..e5e89152b7 100644
--- a/src/ui/yui/src/assets/css/skin/dark/alloy-editor-skin.css
+++ b/src/ui/yui/src/assets/css/skin/dark/alloy-editor-skin.css
@@ -66,17 +66,13 @@
}
.alloy-editor-toolbar .alloy-editor-button {
- -webkit-box-shadow: none;
-moz-box-shadow: none;
+ -webkit-box-shadow: none;
box-shadow: none;
- -webkit-border-radius: 0;
- -moz-border-radius: 0;
- -ms-border-radius: 0;
- -o-border-radius: 0;
- border-radius: 0;
-webkit-transition: color 0.1s ease-out;
-moz-transition: color 0.1s ease-out;
-o-transition: color 0.1s ease-out;
+ -webkit-transition: color 0.1s ease-out;
transition: color 0.1s ease-out;
background-color: transparent;
background-image: none;
@@ -87,8 +83,8 @@
color: #44b6e0;
}
.alloy-editor-toolbar .alloy-editor-button.yui3-button-selected, .alloy-editor-toolbar .alloy-editor-button.yui3-button-active, .alloy-editor-toolbar .alloy-editor-button.yui3-button:active {
- -webkit-box-shadow: none;
-moz-box-shadow: none;
+ -webkit-box-shadow: none;
box-shadow: none;
background-color: #2f323d;
color: #44b6e0;
@@ -105,46 +101,57 @@
color: #fff;
}
+/* line 54, ../../../sass/skin/dark/alloy-editor-skin.scss */
.alloy-editor-toolbar-styles .link-wrapper {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
- -ms-border-radius: 4px;
- -o-border-radius: 4px;
+ -webkit-border-radius: 4px;
border-radius: 4px;
background-color: #2f323d;
margin-left: 5px;
}
+/* line 60, ../../../sass/skin/dark/alloy-editor-skin.scss */
.alloy-editor-toolbar-styles .link-wrapper input {
border: none;
}
+/* line 64, ../../../sass/skin/dark/alloy-editor-skin.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container {
background-color: #fff;
color: #2f232d;
}
+/* line 70, ../../../sass/skin/dark/alloy-editor-skin.scss */
.alloy-editor-toolbar-styles .link-wrapper .input-container .input-clear {
color: #b1b1b1;
}
+/* line 76, ../../../sass/skin/dark/alloy-editor-skin.scss */
+.alloy-editor-toolbar-styles .link-wrapper .input-close-container .close-link {
+ color: #27be0e;
+}
+/* line 79, ../../../sass/skin/dark/alloy-editor-skin.scss */
+.alloy-editor-toolbar-styles .link-wrapper .input-close-container .close-link.yui3-button[disabled], .alloy-editor-toolbar-styles .link-wrapper .input-close-container .close-link.yui3-button-disabled {
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ color: #fff;
+ opacity: 1;
+}
.alloy-editor-toolbar-add-trigger {
- -webkit-box-shadow: none;
-moz-box-shadow: none;
+ -webkit-box-shadow: none;
box-shadow: none;
background-color: transparent;
}
.alloy-editor-toolbar-add-trigger .alloy-editor-button {
- -webkit-border-radius: 13px;
-moz-border-radius: 13px;
- -ms-border-radius: 13px;
- -o-border-radius: 13px;
+ -webkit-border-radius: 13px;
border-radius: 13px;
background-color: #2f323d;
}
.alloy-editor-tooltip-link {
- -webkit-border-radius: 4px;
-moz-border-radius: 4px;
- -ms-border-radius: 4px;
- -o-border-radius: 4px;
+ -webkit-border-radius: 4px;
border-radius: 4px;
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=95);
opacity: 0.95;
@@ -162,6 +169,12 @@
background-color: #ffffe0;
}
+/* line 120, ../../../sass/skin/dark/alloy-editor-skin.scss */
+.alloy-editor-toolbar .alloy-editor-button.focus {
+ color: #44b6e0;
+}
+
+/* line 124, ../../../sass/skin/dark/alloy-editor-skin.scss */
.alloy-editor-twitter-link {
background-color: #f8f8f8;
}
diff --git a/src/ui/yui/src/assets/sass/alloy-editor-core.scss b/src/ui/yui/src/assets/sass/alloy-editor-core.scss
index e53d30f489..b3da3c9479 100644
--- a/src/ui/yui/src/assets/sass/alloy-editor-core.scss
+++ b/src/ui/yui/src/assets/sass/alloy-editor-core.scss
@@ -1,4 +1,4 @@
-.yui3-toolbarstyles-hidden, .yui3-toolbaradd-hidden, .yui3-toolbaraddtrigger-hidden, .yui3-toolbarimage-hidden, .yui3-linktooltip-hidden {
+.yui3-toolbarstyles-hidden, .yui3-toolbaradd-hidden, .yui3-toolbaraddtrigger-hidden, .yui3-linktooltip-hidden {
display: none;
}
@@ -29,6 +29,14 @@
}
.alloy-editor-toolbar-styles {
+ .alloy-editor-toolbar-buttons {
+ display: none;
+
+ &.active {
+ display: block;
+ }
+ }
+
.link-wrapper {
.alloy-editor-button {
margin-left: 10px;
diff --git a/src/ui/yui/src/assets/sass/skin/dark/alloy-editor-skin.scss b/src/ui/yui/src/assets/sass/skin/dark/alloy-editor-skin.scss
index c72f9aab78..7c2730e5f6 100644
--- a/src/ui/yui/src/assets/sass/skin/dark/alloy-editor-skin.scss
+++ b/src/ui/yui/src/assets/sass/skin/dark/alloy-editor-skin.scss
@@ -60,7 +60,7 @@ $prefixes: ("-webkit-", "-moz-", "-o-", "");
}
}
-.alloy-editor-toolbar-styles {
+.alloy-editor-toolbar-context {
.link-wrapper {
@include border-radius(4px);
background-color: #2f323d;
diff --git a/src/ui/yui/src/toolbars/toolbar-add.js b/src/ui/yui/src/toolbars/toolbar-add.js
index e9370521f3..7f59145758 100644
--- a/src/ui/yui/src/toolbars/toolbar-add.js
+++ b/src/ui/yui/src/toolbars/toolbar-add.js
@@ -2,6 +2,7 @@ YUI.add('toolbar-add', function(Y) {
'use strict';
var Lang = Y.Lang,
+ YArray = Y.Array,
YNode = Y.Node,
ARROW_BOX_CLASSES = [
@@ -33,8 +34,6 @@ YUI.add('toolbar-add', function(Y) {
editorNode = Y.one(this.get('editor').element.$);
this._editorDOMNode = editorNode.getDOMNode();
-
- this._toolbars = {};
},
/**
@@ -52,8 +51,8 @@ YUI.add('toolbar-add', function(Y) {
this._triggerButton.on('click', this._showToolbarAddContent, this);
this.on('visibleChange', this._onVisibleChange, this);
- editor.on('toolbarsReady', this._onToolbarsReady, this);
- editor.on('toolbarsHide', this._onToolbarsHide, this);
+ editor.on('widgetsReady', this._onWidgetsReady, this);
+ editor.on('widgetsHide', this._onWidgetsHide, this);
},
/**
@@ -89,6 +88,8 @@ YUI.add('toolbar-add', function(Y) {
var buttonsContainer = this.get('buttonsContainer');
if (this.get('visible')) {
+ buttonsContainer.focusManager.refresh();
+
buttonsContainer.focusManager.focus(0);
} else {
this._triggerButton.focus();
@@ -97,6 +98,29 @@ YUI.add('toolbar-add', function(Y) {
return true;
},
+ /**
+ * Resolves all required modules based on the current toolbar configuration.
+ *
+ * @method getModules
+ * @return {Array} An Array with all the module names required by
+ * the current toolbar configuration.
+ */
+ getModules: function() {
+ var i,
+ modules,
+ buttonsConfig;
+
+ modules = [];
+
+ buttonsConfig = this.get('buttons');
+
+ for (i in buttonsConfig) {
+ modules.push('button-' + this._getButtonName(buttonsConfig[i]));
+ }
+
+ return modules;
+ },
+
/**
* Returns true if the passed node is a child node of the toolbar, false otherwise.
*
@@ -167,11 +191,12 @@ YUI.add('toolbar-add', function(Y) {
* @protected
*/
_hideEditorToolbars: function() {
- var editorToolbars;
+ var editorWidgets;
- editorToolbars = this._toolbars;
+ editorWidgets = this._widgets;
- Y.Object.each(editorToolbars, function(item) {
+ Y.Array.each(editorWidgets, function(item) {
+ // TODO Should distinguish if it's actually a Toolbar
if (this !== item) {
item.hide();
}
@@ -219,9 +244,9 @@ YUI.add('toolbar-add', function(Y) {
/**
* Hides the toolbar and the trigger in case of toolbarsHide
event.
*
- * @method _onToolbarsHide
+ * @method _onWidgetsHide
*/
- _onToolbarsHide: function() {
+ _onWidgetsHide: function() {
this.hide();
this._trigger.hide();
@@ -234,8 +259,8 @@ YUI.add('toolbar-add', function(Y) {
* @protected
* @param {EventFacade} event Event that triggered when all editor toolbars are initialized.
*/
- _onToolbarsReady: function(event) {
- this._toolbars = event.data.toolbars;
+ _onWidgetsReady: function(event) {
+ this._widgets = event.data.widgets;
},
/**
@@ -266,6 +291,11 @@ YUI.add('toolbar-add', function(Y) {
contentBox.appendChild(buttonsContainer);
+ YArray.each(
+ instance.get('buttons'),
+ Y.bind('_appendButton', instance, buttonsContainer)
+ );
+
instance._buttonsContainer = buttonsContainer;
},
@@ -317,8 +347,6 @@ YUI.add('toolbar-add', function(Y) {
this._trigger.hide();
- this._editorNode.focus();
-
this.focus();
this.get('editor').fire('toolbarActive', this);
diff --git a/src/ui/yui/src/toolbars/toolbar-base.js b/src/ui/yui/src/toolbars/toolbar-base.js
index 306b3c6fec..661ff97d59 100644
--- a/src/ui/yui/src/toolbars/toolbar-base.js
+++ b/src/ui/yui/src/toolbars/toolbar-base.js
@@ -32,27 +32,6 @@ YUI.add('toolbar-base', function(Y) {
instance._editorNode = Y.one(editor.element.$);
- YArray.each(
- instance.get('buttons'),
- function(item) {
- var buttonName,
- cfg,
- instanceName;
-
- buttonName = Lang.isObject(item) ? item.name : item;
-
- instanceName = instance._getButtonInstanceName(buttonName);
-
- cfg = Lang.isObject(item) ? item.cfg : null;
-
- instance.plug(Y[instanceName], cfg);
-
- // Each button will fire actionPerformed when user interacts with it. Here we will
- // re-fire this event to the other buttons so they will be able to update their UI too.
- instance[Y[instanceName].NS].after('actionPerformed', instance._afterActionPerformed, instance);
- }
- );
-
instance.after('render', instance._afterRender, instance);
instance.after('visibleChange', instance._afterVisibleChange, instance);
@@ -86,6 +65,8 @@ YUI.add('toolbar-base', function(Y) {
visible = this.get('visible');
if (visible) {
+ buttonsContainer.focusManager.refresh();
+
buttonsContainer.focusManager.focus(index);
}
@@ -127,7 +108,7 @@ YUI.add('toolbar-base', function(Y) {
buttonsContainer.plug(Y.Plugin.NodeFocusManager, {
activeDescendant: 0,
circular: true,
- descendants: 'button',
+ descendants: 'button:visible',
focusClass: 'focus',
keys: {
next: 'down:' + KEY_ARROW_RIGHT,
@@ -159,6 +140,38 @@ YUI.add('toolbar-base', function(Y) {
});
},
+ /**
+ * Adds a button to a given container inside the toolbar.
+ *
+ * @method _appendButton
+ * @protected
+ * @param {Node} buttonsContainer Container for the buttons
+ * @param {String|Object} item String or object describing the button
+ */
+ _appendButton: function(buttonsContainer, item) {
+ var instance = this,
+ buttonName,
+ cfg,
+ defaultCfg,
+ instanceName;
+
+ defaultCfg = {
+ container: buttonsContainer
+ };
+
+ buttonName = Lang.isObject(item) ? item.name : item;
+
+ instanceName = instance._getButtonInstanceName(buttonName);
+
+ cfg = Lang.isObject(item) ? item.cfg : null;
+
+ instance.plug(Y[instanceName], Y.merge(defaultCfg, cfg));
+
+ // Each button will fire actionPerformed when user interacts with it. Here we will
+ // re-fire this event to the other buttons so they will be able to update their UI too.
+ instance[Y[instanceName].NS].after('actionPerformed', instance._afterActionPerformed, instance);
+ },
+
/**
* Applies transition specified via {{#crossLink "ToolbarBase/transition:attribute"}}{{/crossLink}} attribute.
*
@@ -260,6 +273,25 @@ YUI.add('toolbar-base', function(Y) {
return 'Button' + buttonName.substring(0, 1).toUpperCase() + buttonName.substring(1);
},
+ /**
+ * Resolves the name of a button module passed through configuration.
+ *
+ * @method _getButtonName
+ * @protected
+ * @param {String|Object} button A string representing the button or an object
+ * with a name attribute.
+ * @return {String} The name of the button.
+ */
+ _getButtonName: function(button) {
+ var buttonName = button;
+
+ if (typeof button !== 'string') {
+ buttonName = button.name;
+ }
+
+ return buttonName
+ },
+
/**
* Detects if the current line is empty
*
@@ -386,5 +418,5 @@ YUI.add('toolbar-base', function(Y) {
Y.ToolbarBase = ToolbarBase;
}, '', {
- requires: ['node-focusmanager', 'node-base', 'plugin']
+ requires: ['node-focusmanager', 'node', 'plugin', 'transition']
});
diff --git a/src/ui/yui/src/toolbars/toolbar-image.js b/src/ui/yui/src/toolbars/toolbar-image.js
deleted file mode 100644
index fb36fe8c6c..0000000000
--- a/src/ui/yui/src/toolbars/toolbar-image.js
+++ /dev/null
@@ -1,204 +0,0 @@
-YUI.add('toolbar-image', function(Y) {
- 'use strict';
-
- var Lang = Y.Lang,
- YNode = Y.Node,
-
- /**
- * The ToolbarImage class hosts the buttons for aligning and manipulating an image.
- *
- * @class ToolbarImage
- */
- ToolbarImage = Y.Base.create('toolbarimage', Y.Widget, [Y.WidgetPosition, Y.WidgetPositionConstrain, Y.ToolbarBase, Y.ToolbarPosition], {
- /**
- * Creates the container where buttons, attached to the instance of Toolbar should render.
- *
- * @method renderUI
- * @protected
- */
- renderUI: function() {
- var instance = this,
- buttonsContainer,
- contentBox;
-
- buttonsContainer = YNode.create(instance.TPL_BUTTONS_CONTAINER);
-
- this.get('boundingBox').addClass('arrow-box arrow-box-bottom');
-
- contentBox = this.get('contentBox');
-
- contentBox.addClass('btn-toolbar');
-
- contentBox.appendChild(buttonsContainer);
-
- instance._buttonsContainer = buttonsContainer;
- },
-
- /**
- * Attaches listeners to actionPerformed
and toolbarsHide
events.
- *
- * @method bindUI
- * @protected
- */
- bindUI: function() {
- this.on('actionPerformed', this._onActionPerformed, this);
-
- this.get('editor').on('toolbarsHide', this._onToolbarsHide, this);
- },
-
- /**
- * Calculates and sets the position of the toolbar.
- *
- * @method showAtPoint
- * @param {Number} left The left offset in page coordinates.
- * @param {Number} top The top offset in page coordinates.
- * @param {Number} direction The direction of the selection. Can be one of these:
- * 1. CKEDITOR.SELECTION_TOP_TO_BOTTOM
- * 2. CKEDITOR.SELECTION_BOTTOM_TO_TOP
- */
- showAtPoint: function(left, top, direction) {
- var xy,
- visible;
-
- visible = this.get('visible');
-
- if (!visible) {
- this.show();
- }
-
- xy = this._getToolbarXYPoint(left, top, direction);
-
- this._moveToPoint(this.getConstrainedXY(xy), direction, {
- visible: visible
- });
- },
-
- /**
- * After changing the attributes of the image, updates the position of the Toolbar.
- *
- * @method _onActionPerformed
- * @protected
- */
- _onActionPerformed: function() {
- var editor,
- element;
-
- editor = this.get('editor');
-
- element = editor.getSelection().getSelectedElement();
-
- this._updateUI(element);
- },
-
- /**
- * Once after user interacts with the editor, shows or hides the Toolbar.
- * The Toolbar will be hidden if the currently selected element is not an image.
- *
- * @method _onEditorInteraction
- * @protected
- * @param {EventFacade} event Event that triggered when user interacted with the editor.
- */
- _onEditorInteraction: function(event) {
- var element,
- name,
- selectionData;
-
- selectionData = event.data.selectionData;
-
- element = selectionData.element;
-
- name = element ? element.getName() : null;
-
- if (name === 'img') {
- this._updateUI(element);
- } else {
- this.hide();
- }
- },
-
- /**
- * Hides the toolbar in case of toolbarsHide
event.
- *
- * @method _onToolbarsHide
- */
- _onToolbarsHide: function() {
- this.hide();
- },
-
- /**
- * Moves the Toolbar to specified position.
- *
- * @method _updateUI
- * @protected
- * @param {CKEDITOR.dom.element} element The selected image element from the editor.
- */
- _updateUI: function(element) {
- var region;
-
- if (element) {
- region = Y.DOM.region(element.$);
-
- this.showAtPoint(region.left + (region.right - region.left) / 2, region.top,
- CKEDITOR.SELECTION_BOTTOM_TO_TOP);
-
- this.fire('positionChange', this);
-
- this.get('editor').fire('toolbarActive', this);
- }
- },
-
- BOUNDING_TEMPLATE: '',
-
- CONTENT_TEMPLATE: '',
-
- TPL_BUTTONS_CONTAINER: ''
- }, {
- ATTRS: {
- /**
- * Specifies the buttons, which will be attached to the current instance of the toolbar.
- * A button configuration can be simple string with the name of the button, or an object
- * with properties, like this:
- *
- * buttons: ['left']
- *
- * or:
- *
- * buttons: [
- * {
- * name: 'left',
- * cfg: {
- * zIndex: 1024,
- * property2: 1024
- * }
- * }
- * ]
- *
- *
- * @attribute buttons
- * @default ['left', 'right']
- * @type Array
- */
- buttons: {
- validator: Lang.isArray,
- value: ['left', 'right']
- },
-
- /**
- * Specifies whether the toolbar show be constrained to some node or to the viewport.
- *
- * @attribute constrain
- * @default true (will be constrained to the viewport)
- * @type Boolean
- */
- constrain: {
- validator: Lang.isBoolean,
- value: true
- }
- }
- });
-
- Y.ToolbarImage = ToolbarImage;
-}, '', {
- requires: ['dom-screen', 'widget-base', 'widget-position', 'widget-position-constrain', 'toolbar-base', 'toolbar-position']
-});
diff --git a/src/ui/yui/src/toolbars/toolbar-position.js b/src/ui/yui/src/toolbars/toolbar-position.js
index c1a2ee861f..55731f5add 100644
--- a/src/ui/yui/src/toolbars/toolbar-position.js
+++ b/src/ui/yui/src/toolbars/toolbar-position.js
@@ -71,6 +71,25 @@ YUI.add('toolbar-position', function(Y) {
};
},
+ _getModules: function() {
+ var i,
+ j,
+ modules,
+ selectionsConfig;
+
+ modules = [];
+
+ selectionsConfig = this.get('selections');
+
+ for (i in selectionsConfig) {
+ for (j = selectionsConfig[i].buttons.length - 1; j >= 0; j--) { // put button modules
+ modules.push('button-' + this._getButtonName(selectionsConfig[i].buttons[j]));
+ }
+ }
+
+ return modules;
+ },
+
/**
* Returns the position of the Toolbar taking in consideration the
* {{#crossLink "ToolbarStyles/gutter:attribute"}}{{/crossLink}} attribute.
diff --git a/src/ui/yui/src/toolbars/toolbar-styles.js b/src/ui/yui/src/toolbars/toolbar-styles.js
index 279b52d84d..1b822c7345 100644
--- a/src/ui/yui/src/toolbars/toolbar-styles.js
+++ b/src/ui/yui/src/toolbars/toolbar-styles.js
@@ -2,10 +2,15 @@ YUI.add('toolbar-styles', function(Y) {
'use strict';
var Lang = Y.Lang,
- YNode = Y.Node,
+ YArray = Y.Array,
+
+ STR_SELECTION_TEXT = 'text',
+ STR_SELECTION_IMAGE = 'image',
+
+ SELECTION_TYPES = {},
/**
- * The ToolbarStyles class hosts the buttons for styling text selection.
+ * The ToolbarStyles class hosts the buttons for handling editor selection selections.
*
* @class ToolbarStyles
*/
@@ -19,13 +24,37 @@ YUI.add('toolbar-styles', function(Y) {
renderUI: function() {
var instance = this,
buttonsContainer,
+ selectionContainer,
contentBox;
- buttonsContainer = YNode.create(instance.TPL_BUTTONS_CONTAINER);
+ this.get('boundingBox').addClass('arrow-box arrow-box-bottom');
contentBox = this.get('contentBox');
- contentBox.appendChild(buttonsContainer);
+ contentBox.addClass('btn-toolbar');
+
+ buttonsContainer = contentBox.one('.selections');
+
+ YArray.each(
+ instance._getSelectionsConfig(),
+ function(selection) {
+ selectionContainer = Y.Node.create(
+ Y.Lang.sub(
+ instance.TPL_BUTTONS_CONTAINER,
+ {
+ selectionType: selection.type
+ }
+ )
+ );
+
+ buttonsContainer.appendChild(selectionContainer);
+
+ YArray.each(
+ selection.buttons,
+ Y.bind('_appendButton', instance, selectionContainer)
+ );
+ }
+ );
instance._buttonsContainer = buttonsContainer;
},
@@ -40,14 +69,43 @@ YUI.add('toolbar-styles', function(Y) {
this.get('editor').on('toolbarsHide', this._onToolbarsHide, this);
},
+ /**
+ * Resolves all required modules based on the current toolbar configuration.
+ *
+ * @method getModules
+ * @return {Array} An Array with all the module names required by
+ * the current toolbar configuration.
+ */
+ getModules: function() {
+ var self = this,
+ modules;
+
+ modules = [];
+
+ YArray.each(
+ this._getSelectionsConfig(),
+ function(selection) {
+ YArray.each(
+ selection.buttons,
+ function(button) {
+ modules.push('button-' + self._getButtonName(button));
+ }
+ );
+ }
+ );
+
+ return modules;
+ },
+
/**
* Calculates and sets the position of the toolbar.
*
* @method showAtPoint
- * @param {Number} left The left offset in page coordinates where Toolbar should be shown.
- * @param {Number} top The right offset in page coordinates where Toolbar should be shown.
- * @param {Number} direction The direction of the selection. May be one of the following:
- * CKEDITOR.SELECTION_BOTTOM_TO_TOP or CKEDITOR.SELECTION_TOP_TO_BOTTOM
+ * @param {Number} left The left offset in page coordinates.
+ * @param {Number} top The top offset in page coordinates.
+ * @param {Number} direction The direction of the selection. Can be one of these:
+ * 1. CKEDITOR.SELECTION_TOP_TO_BOTTOM
+ * 2. CKEDITOR.SELECTION_BOTTOM_TO_TOP
*/
showAtPoint: function(left, top, direction) {
var boundingBox,
@@ -75,6 +133,43 @@ YUI.add('toolbar-styles', function(Y) {
});
},
+ /**
+ * Resolves the different shortcuts available for the selections configuration and
+ * produces a final configuration array with all the required options.
+ *
+ * @method _getSelectionsConfig
+ * @protected
+ * @return {Array} An array with the selections final configuration for the toolbar.
+ */
+ _getSelectionsConfig: function() {
+ var defaultSelection,
+ selectionConfig,
+ selectionsConfig = [];
+
+ if (!this._selectionsConfig) {
+ YArray.each(
+ this.get('selections'),
+ function(selection) {
+ if (Lang.isString(selection)) {
+ selectionConfig = SELECTION_TYPES[selection];
+ } else {
+ defaultSelection = SELECTION_TYPES[selection.type];
+
+ selectionConfig = defaultSelection ? Y.merge(defaultSelection, selection) : selection;
+ }
+
+ if (selectionConfig) {
+ selectionsConfig.push(selectionConfig);
+ }
+ }
+ );
+
+ this._selectionsConfig = selectionsConfig;
+ }
+
+ return this._selectionsConfig;
+ },
+
/**
* Calculates the most appropriate position where the Toolbar should be displayed and shows it.
*
@@ -85,32 +180,49 @@ YUI.add('toolbar-styles', function(Y) {
* information.
*/
_onEditorInteraction: function(event) {
- var editor,
+ var self = this,
+ editor,
+ contentBox,
+ nativeEvent,
position,
- selectionData,
- selectionEmpty,
- nativeEvent;
+ matchedSelection,
+ selectionData;
editor = this.get('editor');
- selectionEmpty = editor.isSelectionEmpty();
-
selectionData = event.data.selectionData;
nativeEvent = event.data.nativeEvent;
- if (!selectionData.element && selectionData.region && !selectionEmpty) {
- position = this._calculatePosition(selectionData, {
- x: nativeEvent.pageX,
- y: nativeEvent.pageY
- });
+ matchedSelection = YArray.some(
+ this._getSelectionsConfig(),
+ function(selection) {
+ if (selection.test(selectionData, editor)) {
+ contentBox = self.get('contentBox');
- this.showAtPoint(position.x, position.y, position.direction);
+ contentBox.all('.alloy-editor-toolbar-buttons').removeClass('active');
- this.fire('positionChange', this);
+ contentBox.one('.alloy-editor-toolbar-buttons[data-selection=' + selection.type + ']').addClass('active');
- editor.fire('toolbarActive', this);
- } else {
+ position = self._calculatePosition(selectionData, {
+ x: nativeEvent.pageX,
+ y: nativeEvent.pageY
+ });
+
+ self.showAtPoint(position.x, position.y, position.direction);
+
+ self.fire('positionChange');
+
+ editor.fire('toolbarActive', self);
+
+ return true;
+ }
+
+ return false;
+ }
+ );
+
+ if (!matchedSelection) {
this.hide();
}
},
@@ -124,41 +236,56 @@ YUI.add('toolbar-styles', function(Y) {
this.hide();
},
- BOUNDING_TEMPLATE: ' ',
+ BOUNDING_TEMPLATE: '',
- CONTENT_TEMPLATE: '',
+ CONTENT_TEMPLATE: ' ',
- TPL_BUTTONS_CONTAINER: ''
+ TPL_BUTTONS_CONTAINER: ''
}, {
ATTRS: {
- /**
- * Specifies the buttons, which will be attached to the current instance of the toolbar.
- * A button configuration can be simple string with the name of the button, or an object
- * with properties, like this:
+ /*
+ * Specifies the selections that can be handled by the current instance of the toolbar.
+ * A Selection configuration is an object like
+ *
+ * {
+ * type: 'text',
+ * buttons: ['strong', 'em', 'u', 'h1', 'h2'],
+ * test: function(selectionData, editor) { ... }
+ * }
+ *
+ *
+ * A button configuration can be a simple string with the type of the selection. In this case,
+ * the toolbar will use the default buttons and test given for that type of selection, like this:
*
- * buttons: ['strong']
+ * selections: ['image', 'text']
*
- * or:
+ *
+ * In addition, it's possible to pass only partials of a Selection configuration. In this case,
+ * the rest of properties will be inherited from the default selection type configuration:
*
- * buttons: [
+ * selections: [
+ * {
+ * type: 'image',
+ * buttons: ['left']
+ * },
* {
- * name: 'strong',
- * cfg: {
- * zIndex: 1024,
- * property2: 1024
- * }
+ * type: 'text',
+ * test: function() { // custom logic }
* }
* ]
*
*
- * @attribute buttons
- * @default ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter']
+ * In the example, when a selection of type 'image' is done, only the 'left' button will be shown, and
+ * the decission of wether a selection is of type 'text' or not, will be handled by the custom provided
+ * code, but will show the default selection buttons.
+ *
+ * @attribute selections
+ * @default ['image', 'text']
* @type Array
*/
- buttons: {
+ selections: {
validator: Lang.isArray,
- value: ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter']
+ value: [STR_SELECTION_IMAGE, STR_SELECTION_TEXT]
},
/**
@@ -175,6 +302,22 @@ YUI.add('toolbar-styles', function(Y) {
}
});
+ SELECTION_TYPES[STR_SELECTION_IMAGE] = {
+ type: STR_SELECTION_IMAGE,
+ buttons: ['left', 'right'],
+ test: function(selectionData, editor) {
+ return (selectionData.element && selectionData.element.getName() === 'img');
+ }
+ };
+
+ SELECTION_TYPES[STR_SELECTION_TEXT] = {
+ type: STR_SELECTION_TEXT,
+ buttons: ['strong', 'em', 'u', 'h1', 'h2', 'a', 'twitter'],
+ test: function(selectionData, editor) {
+ return (!selectionData.element && selectionData.region && !editor.isSelectionEmpty());
+ }
+ };
+
Y.ToolbarStyles = ToolbarStyles;
}, '', {
requires: ['toolbar-base', 'toolbar-position', 'widget-base', 'widget-position', 'widget-position-constrain']