Merge branch 'MDL-44760-master' of git://github.com/andrewnicols/moodle
Conflicts: lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-debug.js lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button-min.js lib/editor/atto/plugins/table/yui/build/moodle-atto_table-button/moodle-atto_table-button.js lib/editor/atto/plugins/table/yui/src/button/js/button.js lib/editor/atto/yui/build/moodle-editor_atto-plugin/moodle-editor_atto-plugin-min.js
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
.atto_backcolor_button .dropdown-menu {
|
||||
min-width: inherit;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.atto_fontcolor_button .dropdown-menu {
|
||||
min-width: inherit;
|
||||
}
|
||||
Vendored
+57
-21
@@ -76,18 +76,6 @@ var COMPONENT = 'atto_table',
|
||||
'<button class="{{CSS.SUBMIT}}" type="submit">{{get_string "createtable" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
CONTEXTMENUTEMPLATE = '' +
|
||||
'<ul>' +
|
||||
'<li><a href="#" data-change="addcolumnafter">{{get_string "addcolumnafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="addrowafter">{{get_string "addrowafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowup">{{get_string "moverowup" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowdown">{{get_string "moverowdown" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnleft">{{get_string "movecolumnleft" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnright">{{get_string "movecolumnright" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deleterow">{{get_string "deleterow" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deletecolumn">{{get_string "deletecolumn" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="edittable">{{get_string "edittable" component}}</a></li>' +
|
||||
'</ul>',
|
||||
CSS = {
|
||||
CAPTION: 'caption',
|
||||
HEADERS: 'headers',
|
||||
@@ -135,6 +123,15 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_lastTarget: null,
|
||||
|
||||
/**
|
||||
* The list of menu items.
|
||||
*
|
||||
* @property _menuOptions
|
||||
* @type Object
|
||||
* @private
|
||||
*/
|
||||
_menuOptions: null,
|
||||
|
||||
initializer: function() {
|
||||
this.addButton({
|
||||
icon: 'e/table',
|
||||
@@ -569,22 +566,59 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var boundingBox;
|
||||
|
||||
if (!this._contextMenu) {
|
||||
var template = Y.Handlebars.compile(CONTEXTMENUTEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
component: COMPONENT
|
||||
}));
|
||||
this._menuOptions = [
|
||||
{
|
||||
text: M.util.get_string("addcolumnafter", COMPONENT),
|
||||
data: {
|
||||
change: "addcolumnafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("addrowafter", COMPONENT),
|
||||
data: {
|
||||
change: "addrowafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowup", COMPONENT),
|
||||
data: {
|
||||
change: "moverowup"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowdown", COMPONENT),
|
||||
data: {
|
||||
change: "moverowdown"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnleft", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnleft"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnright", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnright"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deleterow", COMPONENT),
|
||||
data: {
|
||||
change: "deleterow"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deletecolumn", COMPONENT),
|
||||
data: {
|
||||
change: "deletecolumn"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._contextMenu = new Y.M.editor_atto.Menu({
|
||||
headerText: M.util.get_string('edittable', 'atto_table'),
|
||||
bodyContent: content
|
||||
items: this._menuOptions
|
||||
});
|
||||
|
||||
// Add event handlers for table control menus.
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
boundingBox.delegate('click', this._handleTableChange, 'a', this);
|
||||
boundingBox.delegate('key', this._handleTableChange, 'down:enter,space', 'a', this);
|
||||
}
|
||||
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
|
||||
// We store the cell of the last click (the control node is transient).
|
||||
@@ -595,6 +629,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Show the context menu, and align to the current position.
|
||||
this._contextMenu.show();
|
||||
this._contextMenu.align(this.buttons.table, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
this._contextMenu.set('focusAfterHide', this.buttons[this.name]);
|
||||
|
||||
// If there are any anchors in the bounding box, focus on the first.
|
||||
if (boundingBox.one('a')) {
|
||||
@@ -612,8 +647,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
_handleTableChange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this._contextMenu.set('focusAfterHide', this.get('host').editor);
|
||||
// Hide the context menu.
|
||||
this._contextMenu.hide();
|
||||
this._contextMenu.hide(e);
|
||||
|
||||
// Make our changes.
|
||||
switch (e.target.getData('change')) {
|
||||
|
||||
Vendored
+3
-3
File diff suppressed because one or more lines are too long
Vendored
+57
-21
@@ -76,18 +76,6 @@ var COMPONENT = 'atto_table',
|
||||
'<button class="{{CSS.SUBMIT}}" type="submit">{{get_string "createtable" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
CONTEXTMENUTEMPLATE = '' +
|
||||
'<ul>' +
|
||||
'<li><a href="#" data-change="addcolumnafter">{{get_string "addcolumnafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="addrowafter">{{get_string "addrowafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowup">{{get_string "moverowup" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowdown">{{get_string "moverowdown" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnleft">{{get_string "movecolumnleft" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnright">{{get_string "movecolumnright" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deleterow">{{get_string "deleterow" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deletecolumn">{{get_string "deletecolumn" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="edittable">{{get_string "edittable" component}}</a></li>' +
|
||||
'</ul>',
|
||||
CSS = {
|
||||
CAPTION: 'caption',
|
||||
HEADERS: 'headers',
|
||||
@@ -135,6 +123,15 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_lastTarget: null,
|
||||
|
||||
/**
|
||||
* The list of menu items.
|
||||
*
|
||||
* @property _menuOptions
|
||||
* @type Object
|
||||
* @private
|
||||
*/
|
||||
_menuOptions: null,
|
||||
|
||||
initializer: function() {
|
||||
this.addButton({
|
||||
icon: 'e/table',
|
||||
@@ -569,22 +566,59 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var boundingBox;
|
||||
|
||||
if (!this._contextMenu) {
|
||||
var template = Y.Handlebars.compile(CONTEXTMENUTEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
component: COMPONENT
|
||||
}));
|
||||
this._menuOptions = [
|
||||
{
|
||||
text: M.util.get_string("addcolumnafter", COMPONENT),
|
||||
data: {
|
||||
change: "addcolumnafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("addrowafter", COMPONENT),
|
||||
data: {
|
||||
change: "addrowafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowup", COMPONENT),
|
||||
data: {
|
||||
change: "moverowup"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowdown", COMPONENT),
|
||||
data: {
|
||||
change: "moverowdown"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnleft", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnleft"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnright", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnright"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deleterow", COMPONENT),
|
||||
data: {
|
||||
change: "deleterow"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deletecolumn", COMPONENT),
|
||||
data: {
|
||||
change: "deletecolumn"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._contextMenu = new Y.M.editor_atto.Menu({
|
||||
headerText: M.util.get_string('edittable', 'atto_table'),
|
||||
bodyContent: content
|
||||
items: this._menuOptions
|
||||
});
|
||||
|
||||
// Add event handlers for table control menus.
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
boundingBox.delegate('click', this._handleTableChange, 'a', this);
|
||||
boundingBox.delegate('key', this._handleTableChange, 'down:enter,space', 'a', this);
|
||||
}
|
||||
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
|
||||
// We store the cell of the last click (the control node is transient).
|
||||
@@ -595,6 +629,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Show the context menu, and align to the current position.
|
||||
this._contextMenu.show();
|
||||
this._contextMenu.align(this.buttons.table, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
this._contextMenu.set('focusAfterHide', this.buttons[this.name]);
|
||||
|
||||
// If there are any anchors in the bounding box, focus on the first.
|
||||
if (boundingBox.one('a')) {
|
||||
@@ -612,8 +647,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
_handleTableChange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this._contextMenu.set('focusAfterHide', this.get('host').editor);
|
||||
// Hide the context menu.
|
||||
this._contextMenu.hide();
|
||||
this._contextMenu.hide(e);
|
||||
|
||||
// Make our changes.
|
||||
switch (e.target.getData('change')) {
|
||||
|
||||
+57
-21
@@ -74,18 +74,6 @@ var COMPONENT = 'atto_table',
|
||||
'<button class="{{CSS.SUBMIT}}" type="submit">{{get_string "createtable" component}}</button>' +
|
||||
'</div>' +
|
||||
'</form>',
|
||||
CONTEXTMENUTEMPLATE = '' +
|
||||
'<ul>' +
|
||||
'<li><a href="#" data-change="addcolumnafter">{{get_string "addcolumnafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="addrowafter">{{get_string "addrowafter" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowup">{{get_string "moverowup" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="moverowdown">{{get_string "moverowdown" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnleft">{{get_string "movecolumnleft" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="movecolumnright">{{get_string "movecolumnright" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deleterow">{{get_string "deleterow" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="deletecolumn">{{get_string "deletecolumn" component}}</a></li>' +
|
||||
'<li><a href="#" data-change="edittable">{{get_string "edittable" component}}</a></li>' +
|
||||
'</ul>',
|
||||
CSS = {
|
||||
CAPTION: 'caption',
|
||||
HEADERS: 'headers',
|
||||
@@ -133,6 +121,15 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
*/
|
||||
_lastTarget: null,
|
||||
|
||||
/**
|
||||
* The list of menu items.
|
||||
*
|
||||
* @property _menuOptions
|
||||
* @type Object
|
||||
* @private
|
||||
*/
|
||||
_menuOptions: null,
|
||||
|
||||
initializer: function() {
|
||||
this.addButton({
|
||||
icon: 'e/table',
|
||||
@@ -567,22 +564,59 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
var boundingBox;
|
||||
|
||||
if (!this._contextMenu) {
|
||||
var template = Y.Handlebars.compile(CONTEXTMENUTEMPLATE),
|
||||
content = Y.Node.create(template({
|
||||
elementid: this.get('host').get('elementid'),
|
||||
component: COMPONENT
|
||||
}));
|
||||
this._menuOptions = [
|
||||
{
|
||||
text: M.util.get_string("addcolumnafter", COMPONENT),
|
||||
data: {
|
||||
change: "addcolumnafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("addrowafter", COMPONENT),
|
||||
data: {
|
||||
change: "addrowafter"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowup", COMPONENT),
|
||||
data: {
|
||||
change: "moverowup"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("moverowdown", COMPONENT),
|
||||
data: {
|
||||
change: "moverowdown"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnleft", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnleft"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("movecolumnright", COMPONENT),
|
||||
data: {
|
||||
change: "movecolumnright"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deleterow", COMPONENT),
|
||||
data: {
|
||||
change: "deleterow"
|
||||
}
|
||||
}, {
|
||||
text: M.util.get_string("deletecolumn", COMPONENT),
|
||||
data: {
|
||||
change: "deletecolumn"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this._contextMenu = new Y.M.editor_atto.Menu({
|
||||
headerText: M.util.get_string('edittable', 'atto_table'),
|
||||
bodyContent: content
|
||||
items: this._menuOptions
|
||||
});
|
||||
|
||||
// Add event handlers for table control menus.
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
boundingBox.delegate('click', this._handleTableChange, 'a', this);
|
||||
boundingBox.delegate('key', this._handleTableChange, 'down:enter,space', 'a', this);
|
||||
}
|
||||
|
||||
boundingBox = this._contextMenu.get('boundingBox');
|
||||
|
||||
// We store the cell of the last click (the control node is transient).
|
||||
@@ -593,6 +627,7 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
// Show the context menu, and align to the current position.
|
||||
this._contextMenu.show();
|
||||
this._contextMenu.align(this.buttons.table, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
this._contextMenu.set('focusAfterHide', this.buttons[this.name]);
|
||||
|
||||
// If there are any anchors in the bounding box, focus on the first.
|
||||
if (boundingBox.one('a')) {
|
||||
@@ -610,8 +645,9 @@ Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.Edi
|
||||
_handleTableChange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
this._contextMenu.set('focusAfterHide', this.get('host').editor);
|
||||
// Hide the context menu.
|
||||
this._contextMenu.hide();
|
||||
this._contextMenu.hide(e);
|
||||
|
||||
// Make our changes.
|
||||
switch (e.target.getData('change')) {
|
||||
|
||||
@@ -98,15 +98,6 @@ div.editor_atto_toolbar div.atto_group {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.atto_menu {
|
||||
background: white;
|
||||
padding: 1px;
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
.atto_menu a {
|
||||
color: black;
|
||||
}
|
||||
.atto_menuentry {
|
||||
clear: left;
|
||||
}
|
||||
@@ -167,33 +158,6 @@ div.editor_atto_content:hover .atto_control {
|
||||
.yui3-menu-hidden {
|
||||
display: none;
|
||||
}
|
||||
.editor_atto_controlmenu li {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.editor_atto_controlmenu ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.editor_atto_controlmenu .moodle-dialogue-hd,
|
||||
.editor_atto_controlmenu .moodle-dialogue-ft {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.editor_atto_controlmenu ul li a:active,
|
||||
.editor_atto_controlmenu ul li a:hover {
|
||||
background-color: #0088cc;
|
||||
color: white;
|
||||
}
|
||||
.editor_atto_controlmenu ul li a {
|
||||
color: #333;
|
||||
padding: 2px;
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Get broken images back in firefox */
|
||||
.editor_atto_content img:-moz-broken {
|
||||
@@ -202,41 +166,17 @@ div.editor_atto_content:hover .atto_control {
|
||||
min-height:24px;
|
||||
}
|
||||
|
||||
/* Atto menu styling */
|
||||
.moodle-dialogue-base .editor_atto_menu .moodle-dialogue-content .moodle-dialogue-bd {
|
||||
position: absolute;
|
||||
text-align:left;
|
||||
padding: 0;
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
-webkit-box-shadow: 5px 5px 20px 0 #666666;
|
||||
-moz-box-shadow: 5px 5px 20px 0 #666666;
|
||||
box-shadow: 5px 5px 20px 0 #666666;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.editor_atto_menu .menu {
|
||||
margin: 0px;
|
||||
.editor_atto_menu .dropdown-menu > li > a {
|
||||
padding: 3px 14px;
|
||||
}
|
||||
|
||||
.editor_atto_menu .menu a {
|
||||
display: block;
|
||||
padding: 4px 1em 4px 1em;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.editor_atto_menu .menu a:hover {
|
||||
color: #ffffff;background-color: #0088cc;
|
||||
}
|
||||
.editor_atto_menu .menu a.hidden {
|
||||
display: none;
|
||||
}
|
||||
.editor_atto_menu .menu img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.editor_atto_menu .menu > li {
|
||||
display: block;
|
||||
.editor_atto_menu .open ul.dropdown-menu {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
+223
-19
@@ -25,6 +25,20 @@ YUI.add('moodle-editor_atto-menu', function (Y, NAME) {
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
var MENUDIALOGUE = '' +
|
||||
'<div class="open {{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="dropdown-menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}" {{#each data}}data-{{@key}}="{{this}}"{{/each}}>' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
/**
|
||||
* A Menu for the Atto editor used in Moodle.
|
||||
*
|
||||
@@ -43,24 +57,169 @@ Menu = function() {
|
||||
|
||||
Y.extend(Menu, M.core.dialogue, {
|
||||
|
||||
/**
|
||||
* A list of the menu handlers which have been attached here.
|
||||
*
|
||||
* @property _menuHandlers
|
||||
* @type Array
|
||||
* @private
|
||||
*/
|
||||
_menuHandlers: null,
|
||||
|
||||
initializer: function(config) {
|
||||
var body, headertext, bb;
|
||||
Menu.superclass.initializer.call(this, config);
|
||||
var headertext,
|
||||
bb;
|
||||
|
||||
this._menuHandlers = [];
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.set('bodyContent', menu);
|
||||
|
||||
bb = this.get('boundingBox');
|
||||
bb.addClass('editor_atto_controlmenu');
|
||||
|
||||
// Close the menu when clicked outside (excluding the button that opened the menu).
|
||||
body = this.bodyNode;
|
||||
bb.addClass('editor_atto_menu');
|
||||
bb.one('.moodle-dialogue-wrap')
|
||||
.removeClass('moodle-dialogue-wrap')
|
||||
.addClass('moodle-dialogue-content');
|
||||
|
||||
headertext = Y.Node.create('<h3/>')
|
||||
.addClass('accesshide')
|
||||
.setHTML(this.get('headerText'));
|
||||
body.prepend(headertext);
|
||||
this.get('bodyContent').prepend(headertext);
|
||||
|
||||
this.get('hideOn');
|
||||
// Hide the header and footer node entirely.
|
||||
this.headerNode.hide();
|
||||
this.footerNode.hide();
|
||||
|
||||
this._setupHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the Event handlers.
|
||||
*
|
||||
* @method _setupHandlers
|
||||
* @private
|
||||
*/
|
||||
_setupHandlers: function() {
|
||||
var contentBox = this.get('contentBox');
|
||||
// Handle menu item selection.
|
||||
this._menuHandlers.push(
|
||||
// Select the menu item on space, and enter.
|
||||
contentBox.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
contentBox.delegate('key', this._handleKeyboardEvent, 'down:38,40', '.dropdown-menu', this),
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
contentBox.on('focusoutside', this.hide, this),
|
||||
|
||||
// Hide the menu on left/right, and escape keys.
|
||||
contentBox.delegate('key', this.hide, 'down:37,39,esc', '.dropdown-menu', this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulate other types of menu selection.
|
||||
*
|
||||
* @method _chooseMenuItem
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
_chooseMenuItem: function(e) {
|
||||
e.target.simulate('click');
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
*
|
||||
* @method hide
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
hide: function(e) {
|
||||
if (this.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must prevent the default action (left/right/escape) because
|
||||
// there are other listeners on the toolbar which will focus on the
|
||||
// editor.
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return Menu.superclass.hide.call(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _handleKeyboardEvent
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @static
|
||||
*/
|
||||
_handleKeyboardEvent: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Y.log("Unable to find this menu item in the menu", 'debug', LOGNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
}, {
|
||||
NAME: "menu",
|
||||
ATTRS: {
|
||||
@@ -89,17 +248,6 @@ Y.Base.modifyAttrs(Menu, {
|
||||
value: 'auto'
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO - chekc that this is needed
|
||||
* The footer content.
|
||||
*
|
||||
* @attribute footerContent
|
||||
* @default ''
|
||||
*/
|
||||
footerContent: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* When to hide this menu.
|
||||
*
|
||||
@@ -116,6 +264,62 @@ Y.Base.modifyAttrs(Menu, {
|
||||
eventName: 'clickoutside'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* The default list of extra classes for this menu.
|
||||
*
|
||||
* @attribute extraClasses
|
||||
* @type Array
|
||||
* @default editor_atto_menu
|
||||
*/
|
||||
extraClasses: {
|
||||
value: [
|
||||
'editor_atto_menu'
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the responsive nature of the core dialogues.
|
||||
*
|
||||
* @attribute responsive
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
responsive: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* The default visibility of the menu.
|
||||
*
|
||||
* @attribute visible
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
visible: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether to centre the menu.
|
||||
*
|
||||
* @attribute center
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
center: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the close button.
|
||||
* @attribute closeButton
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
closeButton: {
|
||||
value: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
YUI.add("moodle-editor_atto-menu",function(e,t){Menu=function(){Menu.superclass.constructor.apply(this,arguments)},e.extend(Menu,M.core.dialogue,{initializer:function(t){var n,r,i;Menu.superclass.initializer.call(this,t),i=this.get("boundingBox"),i.addClass("editor_atto_controlmenu"),n=this.bodyNode,r=e.Node.create("<h3/>").addClass("accesshide").setHTML(this.get("headerText")),n.prepend(r),this.get("hideOn")}},{NAME:"menu",ATTRS:{headerText:{value:""}}}),e.Base.modifyAttrs(Menu,{width:{value:"auto"},footerContent:{value:""},hideOn:{value:[{eventName:"clickoutside"}]}}),e.namespace("M.editor_atto").Menu=Menu},"@VERSION@",{requires:["moodle-core-notification-dialogue","node","event","event-custom"]});
|
||||
YUI.add("moodle-editor_atto-menu",function(e,t){var n='<div class="open {{config.buttonClass}} atto_menu" style="min-width:{{config.innerOverlayWidth}};"><ul class="dropdown-menu">{{#each config.items}}<li role="presentation" class="atto_menuentry"><a href="#" role="menuitem" data-index="{{@index}}" {{#each data}}data-{{@key}}="{{this}}"{{/each}}>{{{text}}}</a></li>{{/each}}</ul></div>';Menu=function(){Menu.superclass.constructor.apply(this,arguments)},e.extend(Menu,M.core.dialogue,{_menuHandlers:null,initializer:function(t){var r,i;this._menuHandlers=[];var s=e.Handlebars.compile(n),o=e.Node.create(s({config:t}));this.set("bodyContent",o),i=this.get("boundingBox"),i.addClass("editor_atto_controlmenu"),i.addClass("editor_atto_menu"),i.one(".moodle-dialogue-wrap").removeClass("moodle-dialogue-wrap").addClass("moodle-dialogue-content"),r=e.Node.create("<h3/>").addClass("accesshide").setHTML(this.get("headerText")),this.get("bodyContent").prepend(r),this.headerNode.hide(),this.footerNode.hide(),this._setupHandlers()},_setupHandlers:function(){var e=this.get("contentBox");this._menuHandlers.push(e.delegate("key",this._chooseMenuItem,"32, enter",".atto_menuentry",this),e.delegate("key",this._handleKeyboardEvent,"down:38,40",".dropdown-menu",this),e.on("focusoutside",this.hide,this),e.delegate("key",this.hide,"down:37,39,esc",".dropdown-menu",this))},_chooseMenuItem:function(e){e.target.simulate("click"),e.preventDefault()},hide:function(e){if(this.get("preventHideMenu")===!0)return;return e&&e.preventDefault(),Menu.superclass.hide.call(this,arguments)},_handleKeyboardEvent:function(e){e.preventDefault();var t=e.currentTarget.all('a[role="menuitem"]'),n=!1,r=0,i=1,s=0,o=e.target.ancestor('a[role="menuitem"]',!0);while(!n&&r<t.size())t.item(r)===o?n=!0:r++;if(!n)return;e.keyCode===38&&(i=-1);do r+=i,r<0?r=t.size()-1:r>=t.size()&&(r=0),next=t.item(r),s++;while(s<t.size()&&next!==o&&next.hasAttribute("hidden"));next&&next.focus(),e.preventDefault(),e.stopImmediatePropagation()}},{NAME:"menu",ATTRS:{headerText:{value:""}}}),e.Base.modifyAttrs(Menu,{width:{value:"auto"},hideOn:{value:[{eventName:"clickoutside"}]},extraClasses:{value:["editor_atto_menu"]},responsive:{value:!1},visible:{value:!1},center:{value:!1},closeButton:{value:!1}}),e.namespace("M.editor_atto").Menu=Menu},"@VERSION@",{requires:["moodle-core-notification-dialogue","node","event","event-custom"]});
|
||||
|
||||
+222
-19
@@ -25,6 +25,20 @@ YUI.add('moodle-editor_atto-menu', function (Y, NAME) {
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
var MENUDIALOGUE = '' +
|
||||
'<div class="open {{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="dropdown-menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}" {{#each data}}data-{{@key}}="{{this}}"{{/each}}>' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
/**
|
||||
* A Menu for the Atto editor used in Moodle.
|
||||
*
|
||||
@@ -43,24 +57,168 @@ Menu = function() {
|
||||
|
||||
Y.extend(Menu, M.core.dialogue, {
|
||||
|
||||
/**
|
||||
* A list of the menu handlers which have been attached here.
|
||||
*
|
||||
* @property _menuHandlers
|
||||
* @type Array
|
||||
* @private
|
||||
*/
|
||||
_menuHandlers: null,
|
||||
|
||||
initializer: function(config) {
|
||||
var body, headertext, bb;
|
||||
Menu.superclass.initializer.call(this, config);
|
||||
var headertext,
|
||||
bb;
|
||||
|
||||
this._menuHandlers = [];
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.set('bodyContent', menu);
|
||||
|
||||
bb = this.get('boundingBox');
|
||||
bb.addClass('editor_atto_controlmenu');
|
||||
|
||||
// Close the menu when clicked outside (excluding the button that opened the menu).
|
||||
body = this.bodyNode;
|
||||
bb.addClass('editor_atto_menu');
|
||||
bb.one('.moodle-dialogue-wrap')
|
||||
.removeClass('moodle-dialogue-wrap')
|
||||
.addClass('moodle-dialogue-content');
|
||||
|
||||
headertext = Y.Node.create('<h3/>')
|
||||
.addClass('accesshide')
|
||||
.setHTML(this.get('headerText'));
|
||||
body.prepend(headertext);
|
||||
this.get('bodyContent').prepend(headertext);
|
||||
|
||||
this.get('hideOn');
|
||||
// Hide the header and footer node entirely.
|
||||
this.headerNode.hide();
|
||||
this.footerNode.hide();
|
||||
|
||||
this._setupHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the Event handlers.
|
||||
*
|
||||
* @method _setupHandlers
|
||||
* @private
|
||||
*/
|
||||
_setupHandlers: function() {
|
||||
var contentBox = this.get('contentBox');
|
||||
// Handle menu item selection.
|
||||
this._menuHandlers.push(
|
||||
// Select the menu item on space, and enter.
|
||||
contentBox.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
contentBox.delegate('key', this._handleKeyboardEvent, 'down:38,40', '.dropdown-menu', this),
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
contentBox.on('focusoutside', this.hide, this),
|
||||
|
||||
// Hide the menu on left/right, and escape keys.
|
||||
contentBox.delegate('key', this.hide, 'down:37,39,esc', '.dropdown-menu', this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulate other types of menu selection.
|
||||
*
|
||||
* @method _chooseMenuItem
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
_chooseMenuItem: function(e) {
|
||||
e.target.simulate('click');
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
*
|
||||
* @method hide
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
hide: function(e) {
|
||||
if (this.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must prevent the default action (left/right/escape) because
|
||||
// there are other listeners on the toolbar which will focus on the
|
||||
// editor.
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return Menu.superclass.hide.call(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _handleKeyboardEvent
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @static
|
||||
*/
|
||||
_handleKeyboardEvent: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
}, {
|
||||
NAME: "menu",
|
||||
ATTRS: {
|
||||
@@ -89,17 +247,6 @@ Y.Base.modifyAttrs(Menu, {
|
||||
value: 'auto'
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO - chekc that this is needed
|
||||
* The footer content.
|
||||
*
|
||||
* @attribute footerContent
|
||||
* @default ''
|
||||
*/
|
||||
footerContent: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* When to hide this menu.
|
||||
*
|
||||
@@ -116,6 +263,62 @@ Y.Base.modifyAttrs(Menu, {
|
||||
eventName: 'clickoutside'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* The default list of extra classes for this menu.
|
||||
*
|
||||
* @attribute extraClasses
|
||||
* @type Array
|
||||
* @default editor_atto_menu
|
||||
*/
|
||||
extraClasses: {
|
||||
value: [
|
||||
'editor_atto_menu'
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the responsive nature of the core dialogues.
|
||||
*
|
||||
* @attribute responsive
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
responsive: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* The default visibility of the menu.
|
||||
*
|
||||
* @attribute visible
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
visible: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether to centre the menu.
|
||||
*
|
||||
* @attribute center
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
center: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the close button.
|
||||
* @attribute closeButton
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
closeButton: {
|
||||
value: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+25
-133
@@ -178,20 +178,7 @@ var MENUTEMPLATE = '' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" '+
|
||||
'style="background-color:{{config.menuColor}};" src="{{config.iconurl}}" />' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" src="{{image_url "t/expanded" "moodle"}}"/>' +
|
||||
'</button>',
|
||||
MENUDIALOGUE = '' +
|
||||
'<div class="{{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}">' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
'</button>';
|
||||
|
||||
var DISABLED = 'disabled',
|
||||
HIGHLIGHT = 'highlight',
|
||||
@@ -340,6 +327,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -495,6 +483,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -529,7 +518,7 @@ EditorPluginButtons.prototype = {
|
||||
// Add the standard click handler to the menu.
|
||||
this._buttonHandlers.push(
|
||||
this.toolbar.delegate('click', this._showToolbarMenu, '.' + buttonClass, this, config),
|
||||
this.toolbar.delegate('key', this._showToolbarMenu, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
this.toolbar.delegate('key', this._showToolbarMenuAndFocus, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
);
|
||||
|
||||
// Add the button reference to the buttons array for later reference.
|
||||
@@ -575,45 +564,15 @@ EditorPluginButtons.prototype = {
|
||||
}
|
||||
config.overlayWidth = parseInt(config.overlayWidth, 10) + 'em';
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.menus[config.buttonClass] = new Y.M.editor_atto.Menu(config);
|
||||
|
||||
// Create the dialogue.
|
||||
this.menus[config.buttonClass] = new M.core.dialogue({
|
||||
bodyContent: menu,
|
||||
width: null,
|
||||
visible: false,
|
||||
center: false,
|
||||
closeButton: false,
|
||||
responsive: false,
|
||||
extraClasses: ['editor_atto_menu']
|
||||
});
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Hide the header node entirely.
|
||||
menuDialogue.headerNode.hide();
|
||||
|
||||
// Handle menu item selection.
|
||||
this._buttonHandlers.push(
|
||||
menu.delegate('click', this._chooseMenuItem, '.atto_menuentry a', this, config, menuDialogue),
|
||||
|
||||
// Select the menu item on space, and enter.
|
||||
menu.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this, config, menuDialogue),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
menu.delegate('key', this._menuKeyboardNavigation, 'down:38,40', '.menu', this),
|
||||
|
||||
// Hide the menu on left/right.
|
||||
menu.delegate('key', this._hideMenu, 'down:37,39', '.menu', this, menuDialogue)
|
||||
);
|
||||
this.menus[config.buttonClass].get('contentBox').delegate('click',
|
||||
this._chooseMenuItem, '.atto_menuentry a', this, config);
|
||||
}
|
||||
|
||||
// Ensure that we focus on this button next time.
|
||||
var creatorButton = this.buttons[config.buttonName];
|
||||
creatorButton.focus();
|
||||
this.get('host')._setTabFocus(creatorButton);
|
||||
|
||||
// Get a reference to the menu dialogue.
|
||||
@@ -621,36 +580,31 @@ EditorPluginButtons.prototype = {
|
||||
|
||||
// Focus on the button by default after hiding this menu.
|
||||
menuDialogue.set('focusAfterHide', creatorButton);
|
||||
menuDialogue.on('focusAfterHide', function(e) {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// Display the menu.
|
||||
menuDialogue.show();
|
||||
|
||||
// Position it next to the button which opened it.
|
||||
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
|
||||
// And focus on the first element in the menu.
|
||||
menuDialogue.get('boundingBox').one('a').focus();
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
this._menuHideHandlers.push(
|
||||
menuDialogue.get('boundingBox').on('focusoutside', this._hideMenu, this, menuDialogue)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
* Display a toolbar menu and focus upon the first item.
|
||||
*
|
||||
* @method _hideMenu
|
||||
* @method _showToolbarMenuAndFocus
|
||||
* @param {EventFacade} e
|
||||
* @param {M.core.dialogue} menuDialogue The Dialogue to hide.
|
||||
* @param {object} config The configuration for the whole toolbar.
|
||||
* @param {Number} [config.overlayWidth=14] The width of the menu
|
||||
* @private
|
||||
*/
|
||||
_hideMenu: function(e, menuDialogue) {
|
||||
if (menuDialogue.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
menuDialogue.hide();
|
||||
new Y.EventHandle(this._menuHideHandlers).detach();
|
||||
_showToolbarMenuAndFocus: function(e, config) {
|
||||
this._showToolbarMenu(e, config);
|
||||
|
||||
// Focus on the first element in the menu.
|
||||
this.menus[config.buttonClass].get('boundingBox').one('a').focus();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -663,12 +617,14 @@ EditorPluginButtons.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_chooseMenuItem: function(e, config, menuDialogue) {
|
||||
// Get the index from the clicked anchor.
|
||||
// Get the index from the clicked anchor.
|
||||
var index = e.target.ancestor('a', true).getData('index'),
|
||||
|
||||
// And the normalized callback configuration.
|
||||
buttonConfig = this._normalizeCallback(config.items[index], config.globalItemConfig);
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Prevent the dialogue to be closed because of some browser weirdness.
|
||||
menuDialogue.set('preventHideMenu', true);
|
||||
|
||||
@@ -678,9 +634,10 @@ EditorPluginButtons.prototype = {
|
||||
// Cancel the hide menu prevention.
|
||||
menuDialogue.set('preventHideMenu', false);
|
||||
|
||||
console.log('Menu item chosen');
|
||||
// Set the focus after hide so that focus is returned to the editor and changes are made correctly.
|
||||
menuDialogue.set('focusAfterHide', this.get('host').editor);
|
||||
this._hideMenu(e, menuDialogue);
|
||||
menuDialogue.hide(e);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -982,71 +939,6 @@ EditorPluginButtons.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _menuKeyboardNavigation
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @private
|
||||
*/
|
||||
_menuKeyboardNavigation: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Y.log("Unable to find this menu item in the menu", 'debug', LOGNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the default meta key to use with keyboard events.
|
||||
*
|
||||
|
||||
+2
-2
File diff suppressed because one or more lines are too long
+25
-132
@@ -178,20 +178,7 @@ var MENUTEMPLATE = '' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" '+
|
||||
'style="background-color:{{config.menuColor}};" src="{{config.iconurl}}" />' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" src="{{image_url "t/expanded" "moodle"}}"/>' +
|
||||
'</button>',
|
||||
MENUDIALOGUE = '' +
|
||||
'<div class="{{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}">' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
'</button>';
|
||||
|
||||
var DISABLED = 'disabled',
|
||||
HIGHLIGHT = 'highlight',
|
||||
@@ -340,6 +327,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -493,6 +481,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -527,7 +516,7 @@ EditorPluginButtons.prototype = {
|
||||
// Add the standard click handler to the menu.
|
||||
this._buttonHandlers.push(
|
||||
this.toolbar.delegate('click', this._showToolbarMenu, '.' + buttonClass, this, config),
|
||||
this.toolbar.delegate('key', this._showToolbarMenu, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
this.toolbar.delegate('key', this._showToolbarMenuAndFocus, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
);
|
||||
|
||||
// Add the button reference to the buttons array for later reference.
|
||||
@@ -573,45 +562,15 @@ EditorPluginButtons.prototype = {
|
||||
}
|
||||
config.overlayWidth = parseInt(config.overlayWidth, 10) + 'em';
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.menus[config.buttonClass] = new Y.M.editor_atto.Menu(config);
|
||||
|
||||
// Create the dialogue.
|
||||
this.menus[config.buttonClass] = new M.core.dialogue({
|
||||
bodyContent: menu,
|
||||
width: null,
|
||||
visible: false,
|
||||
center: false,
|
||||
closeButton: false,
|
||||
responsive: false,
|
||||
extraClasses: ['editor_atto_menu']
|
||||
});
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Hide the header node entirely.
|
||||
menuDialogue.headerNode.hide();
|
||||
|
||||
// Handle menu item selection.
|
||||
this._buttonHandlers.push(
|
||||
menu.delegate('click', this._chooseMenuItem, '.atto_menuentry a', this, config, menuDialogue),
|
||||
|
||||
// Select the menu item on space, and enter.
|
||||
menu.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this, config, menuDialogue),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
menu.delegate('key', this._menuKeyboardNavigation, 'down:38,40', '.menu', this),
|
||||
|
||||
// Hide the menu on left/right.
|
||||
menu.delegate('key', this._hideMenu, 'down:37,39', '.menu', this, menuDialogue)
|
||||
);
|
||||
this.menus[config.buttonClass].get('contentBox').delegate('click',
|
||||
this._chooseMenuItem, '.atto_menuentry a', this, config);
|
||||
}
|
||||
|
||||
// Ensure that we focus on this button next time.
|
||||
var creatorButton = this.buttons[config.buttonName];
|
||||
creatorButton.focus();
|
||||
this.get('host')._setTabFocus(creatorButton);
|
||||
|
||||
// Get a reference to the menu dialogue.
|
||||
@@ -619,36 +578,31 @@ EditorPluginButtons.prototype = {
|
||||
|
||||
// Focus on the button by default after hiding this menu.
|
||||
menuDialogue.set('focusAfterHide', creatorButton);
|
||||
menuDialogue.on('focusAfterHide', function(e) {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// Display the menu.
|
||||
menuDialogue.show();
|
||||
|
||||
// Position it next to the button which opened it.
|
||||
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
|
||||
// And focus on the first element in the menu.
|
||||
menuDialogue.get('boundingBox').one('a').focus();
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
this._menuHideHandlers.push(
|
||||
menuDialogue.get('boundingBox').on('focusoutside', this._hideMenu, this, menuDialogue)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
* Display a toolbar menu and focus upon the first item.
|
||||
*
|
||||
* @method _hideMenu
|
||||
* @method _showToolbarMenuAndFocus
|
||||
* @param {EventFacade} e
|
||||
* @param {M.core.dialogue} menuDialogue The Dialogue to hide.
|
||||
* @param {object} config The configuration for the whole toolbar.
|
||||
* @param {Number} [config.overlayWidth=14] The width of the menu
|
||||
* @private
|
||||
*/
|
||||
_hideMenu: function(e, menuDialogue) {
|
||||
if (menuDialogue.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
menuDialogue.hide();
|
||||
new Y.EventHandle(this._menuHideHandlers).detach();
|
||||
_showToolbarMenuAndFocus: function(e, config) {
|
||||
this._showToolbarMenu(e, config);
|
||||
|
||||
// Focus on the first element in the menu.
|
||||
this.menus[config.buttonClass].get('boundingBox').one('a').focus();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -661,12 +615,14 @@ EditorPluginButtons.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_chooseMenuItem: function(e, config, menuDialogue) {
|
||||
// Get the index from the clicked anchor.
|
||||
// Get the index from the clicked anchor.
|
||||
var index = e.target.ancestor('a', true).getData('index'),
|
||||
|
||||
// And the normalized callback configuration.
|
||||
buttonConfig = this._normalizeCallback(config.items[index], config.globalItemConfig);
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Prevent the dialogue to be closed because of some browser weirdness.
|
||||
menuDialogue.set('preventHideMenu', true);
|
||||
|
||||
@@ -676,9 +632,10 @@ EditorPluginButtons.prototype = {
|
||||
// Cancel the hide menu prevention.
|
||||
menuDialogue.set('preventHideMenu', false);
|
||||
|
||||
console.log('Menu item chosen');
|
||||
// Set the focus after hide so that focus is returned to the editor and changes are made correctly.
|
||||
menuDialogue.set('focusAfterHide', this.get('host').editor);
|
||||
this._hideMenu(e, menuDialogue);
|
||||
menuDialogue.hide(e);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -978,70 +935,6 @@ EditorPluginButtons.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _menuKeyboardNavigation
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @private
|
||||
*/
|
||||
_menuKeyboardNavigation: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the default meta key to use with keyboard events.
|
||||
*
|
||||
|
||||
+25
-133
@@ -35,20 +35,7 @@ var MENUTEMPLATE = '' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" '+
|
||||
'style="background-color:{{config.menuColor}};" src="{{config.iconurl}}" />' +
|
||||
'<img class="icon" aria-hidden="true" role="presentation" width="16" height="16" src="{{image_url "t/expanded" "moodle"}}"/>' +
|
||||
'</button>',
|
||||
MENUDIALOGUE = '' +
|
||||
'<div class="{{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}">' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
'</button>';
|
||||
|
||||
var DISABLED = 'disabled',
|
||||
HIGHLIGHT = 'highlight',
|
||||
@@ -197,6 +184,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -352,6 +340,7 @@ EditorPluginButtons.prototype = {
|
||||
} else {
|
||||
buttonClass = buttonClass + '_' + config.buttonName;
|
||||
}
|
||||
config.buttonClass = buttonClass;
|
||||
|
||||
// Normalize icon configuration.
|
||||
config = this._normalizeIcon(config);
|
||||
@@ -386,7 +375,7 @@ EditorPluginButtons.prototype = {
|
||||
// Add the standard click handler to the menu.
|
||||
this._buttonHandlers.push(
|
||||
this.toolbar.delegate('click', this._showToolbarMenu, '.' + buttonClass, this, config),
|
||||
this.toolbar.delegate('key', this._showToolbarMenu, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
this.toolbar.delegate('key', this._showToolbarMenuAndFocus, '40, 32, enter', '.' + buttonClass, this, config)
|
||||
);
|
||||
|
||||
// Add the button reference to the buttons array for later reference.
|
||||
@@ -432,45 +421,15 @@ EditorPluginButtons.prototype = {
|
||||
}
|
||||
config.overlayWidth = parseInt(config.overlayWidth, 10) + 'em';
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.menus[config.buttonClass] = new Y.M.editor_atto.Menu(config);
|
||||
|
||||
// Create the dialogue.
|
||||
this.menus[config.buttonClass] = new M.core.dialogue({
|
||||
bodyContent: menu,
|
||||
width: null,
|
||||
visible: false,
|
||||
center: false,
|
||||
closeButton: false,
|
||||
responsive: false,
|
||||
extraClasses: ['editor_atto_menu']
|
||||
});
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Hide the header node entirely.
|
||||
menuDialogue.headerNode.hide();
|
||||
|
||||
// Handle menu item selection.
|
||||
this._buttonHandlers.push(
|
||||
menu.delegate('click', this._chooseMenuItem, '.atto_menuentry a', this, config, menuDialogue),
|
||||
|
||||
// Select the menu item on space, and enter.
|
||||
menu.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this, config, menuDialogue),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
menu.delegate('key', this._menuKeyboardNavigation, 'down:38,40', '.menu', this),
|
||||
|
||||
// Hide the menu on left/right.
|
||||
menu.delegate('key', this._hideMenu, 'down:37,39', '.menu', this, menuDialogue)
|
||||
);
|
||||
this.menus[config.buttonClass].get('contentBox').delegate('click',
|
||||
this._chooseMenuItem, '.atto_menuentry a', this, config);
|
||||
}
|
||||
|
||||
// Ensure that we focus on this button next time.
|
||||
var creatorButton = this.buttons[config.buttonName];
|
||||
creatorButton.focus();
|
||||
this.get('host')._setTabFocus(creatorButton);
|
||||
|
||||
// Get a reference to the menu dialogue.
|
||||
@@ -478,36 +437,31 @@ EditorPluginButtons.prototype = {
|
||||
|
||||
// Focus on the button by default after hiding this menu.
|
||||
menuDialogue.set('focusAfterHide', creatorButton);
|
||||
menuDialogue.on('focusAfterHide', function(e) {
|
||||
console.log(e);
|
||||
});
|
||||
|
||||
// Display the menu.
|
||||
menuDialogue.show();
|
||||
|
||||
// Position it next to the button which opened it.
|
||||
menuDialogue.align(this.buttons[config.buttonName], [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
|
||||
|
||||
// And focus on the first element in the menu.
|
||||
menuDialogue.get('boundingBox').one('a').focus();
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
this._menuHideHandlers.push(
|
||||
menuDialogue.get('boundingBox').on('focusoutside', this._hideMenu, this, menuDialogue)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
* Display a toolbar menu and focus upon the first item.
|
||||
*
|
||||
* @method _hideMenu
|
||||
* @method _showToolbarMenuAndFocus
|
||||
* @param {EventFacade} e
|
||||
* @param {M.core.dialogue} menuDialogue The Dialogue to hide.
|
||||
* @param {object} config The configuration for the whole toolbar.
|
||||
* @param {Number} [config.overlayWidth=14] The width of the menu
|
||||
* @private
|
||||
*/
|
||||
_hideMenu: function(e, menuDialogue) {
|
||||
if (menuDialogue.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
menuDialogue.hide();
|
||||
new Y.EventHandle(this._menuHideHandlers).detach();
|
||||
_showToolbarMenuAndFocus: function(e, config) {
|
||||
this._showToolbarMenu(e, config);
|
||||
|
||||
// Focus on the first element in the menu.
|
||||
this.menus[config.buttonClass].get('boundingBox').one('a').focus();
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -520,12 +474,14 @@ EditorPluginButtons.prototype = {
|
||||
* @private
|
||||
*/
|
||||
_chooseMenuItem: function(e, config, menuDialogue) {
|
||||
// Get the index from the clicked anchor.
|
||||
// Get the index from the clicked anchor.
|
||||
var index = e.target.ancestor('a', true).getData('index'),
|
||||
|
||||
// And the normalized callback configuration.
|
||||
buttonConfig = this._normalizeCallback(config.items[index], config.globalItemConfig);
|
||||
|
||||
menuDialogue = this.menus[config.buttonClass];
|
||||
|
||||
// Prevent the dialogue to be closed because of some browser weirdness.
|
||||
menuDialogue.set('preventHideMenu', true);
|
||||
|
||||
@@ -535,9 +491,10 @@ EditorPluginButtons.prototype = {
|
||||
// Cancel the hide menu prevention.
|
||||
menuDialogue.set('preventHideMenu', false);
|
||||
|
||||
console.log('Menu item chosen');
|
||||
// Set the focus after hide so that focus is returned to the editor and changes are made correctly.
|
||||
menuDialogue.set('focusAfterHide', this.get('host').editor);
|
||||
this._hideMenu(e, menuDialogue);
|
||||
menuDialogue.hide(e);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -839,71 +796,6 @@ EditorPluginButtons.prototype = {
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _menuKeyboardNavigation
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @private
|
||||
*/
|
||||
_menuKeyboardNavigation: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Y.log("Unable to find this menu item in the menu", 'debug', LOGNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Get the default meta key to use with keyboard events.
|
||||
*
|
||||
|
||||
+223
-19
@@ -23,6 +23,20 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
var MENUDIALOGUE = '' +
|
||||
'<div class="open {{config.buttonClass}} atto_menu" ' +
|
||||
'style="min-width:{{config.innerOverlayWidth}};">' +
|
||||
'<ul class="dropdown-menu">' +
|
||||
'{{#each config.items}}' +
|
||||
'<li role="presentation" class="atto_menuentry">' +
|
||||
'<a href="#" role="menuitem" data-index="{{@index}}" {{#each data}}data-{{@key}}="{{this}}"{{/each}}>' +
|
||||
'{{{text}}}' +
|
||||
'</a>' +
|
||||
'</li>' +
|
||||
'{{/each}}' +
|
||||
'</ul>' +
|
||||
'</div>';
|
||||
|
||||
/**
|
||||
* A Menu for the Atto editor used in Moodle.
|
||||
*
|
||||
@@ -41,24 +55,169 @@ Menu = function() {
|
||||
|
||||
Y.extend(Menu, M.core.dialogue, {
|
||||
|
||||
/**
|
||||
* A list of the menu handlers which have been attached here.
|
||||
*
|
||||
* @property _menuHandlers
|
||||
* @type Array
|
||||
* @private
|
||||
*/
|
||||
_menuHandlers: null,
|
||||
|
||||
initializer: function(config) {
|
||||
var body, headertext, bb;
|
||||
Menu.superclass.initializer.call(this, config);
|
||||
var headertext,
|
||||
bb;
|
||||
|
||||
this._menuHandlers = [];
|
||||
|
||||
// Create the actual button.
|
||||
var template = Y.Handlebars.compile(MENUDIALOGUE),
|
||||
menu = Y.Node.create(template({
|
||||
config: config
|
||||
}));
|
||||
this.set('bodyContent', menu);
|
||||
|
||||
bb = this.get('boundingBox');
|
||||
bb.addClass('editor_atto_controlmenu');
|
||||
|
||||
// Close the menu when clicked outside (excluding the button that opened the menu).
|
||||
body = this.bodyNode;
|
||||
bb.addClass('editor_atto_menu');
|
||||
bb.one('.moodle-dialogue-wrap')
|
||||
.removeClass('moodle-dialogue-wrap')
|
||||
.addClass('moodle-dialogue-content');
|
||||
|
||||
headertext = Y.Node.create('<h3/>')
|
||||
.addClass('accesshide')
|
||||
.setHTML(this.get('headerText'));
|
||||
body.prepend(headertext);
|
||||
this.get('bodyContent').prepend(headertext);
|
||||
|
||||
this.get('hideOn');
|
||||
// Hide the header and footer node entirely.
|
||||
this.headerNode.hide();
|
||||
this.footerNode.hide();
|
||||
|
||||
this._setupHandlers();
|
||||
},
|
||||
|
||||
/**
|
||||
* Setup the Event handlers.
|
||||
*
|
||||
* @method _setupHandlers
|
||||
* @private
|
||||
*/
|
||||
_setupHandlers: function() {
|
||||
var contentBox = this.get('contentBox');
|
||||
// Handle menu item selection.
|
||||
this._menuHandlers.push(
|
||||
// Select the menu item on space, and enter.
|
||||
contentBox.delegate('key', this._chooseMenuItem, '32, enter', '.atto_menuentry', this),
|
||||
|
||||
// Move up and down the menu on up/down.
|
||||
contentBox.delegate('key', this._handleKeyboardEvent, 'down:38,40', '.dropdown-menu', this),
|
||||
|
||||
// Hide the menu when clicking outside of it.
|
||||
contentBox.on('focusoutside', this.hide, this),
|
||||
|
||||
// Hide the menu on left/right, and escape keys.
|
||||
contentBox.delegate('key', this.hide, 'down:37,39,esc', '.dropdown-menu', this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Simulate other types of menu selection.
|
||||
*
|
||||
* @method _chooseMenuItem
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
_chooseMenuItem: function(e) {
|
||||
e.target.simulate('click');
|
||||
e.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide a menu, removing all of the event handlers which trigger the hide.
|
||||
*
|
||||
* @method hide
|
||||
* @param {EventFacade} e
|
||||
*/
|
||||
hide: function(e) {
|
||||
if (this.get('preventHideMenu') === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We must prevent the default action (left/right/escape) because
|
||||
// there are other listeners on the toolbar which will focus on the
|
||||
// editor.
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return Menu.superclass.hide.call(this, arguments);
|
||||
},
|
||||
|
||||
/**
|
||||
* Implement arrow-key navigation for the items in a toolbar menu.
|
||||
*
|
||||
* @method _handleKeyboardEvent
|
||||
* @param {EventFacade} e The keyboard event.
|
||||
* @static
|
||||
*/
|
||||
_handleKeyboardEvent: function(e) {
|
||||
// Prevent the default browser behaviour.
|
||||
e.preventDefault();
|
||||
|
||||
// Get a list of all buttons in the menu.
|
||||
var buttons = e.currentTarget.all('a[role="menuitem"]');
|
||||
|
||||
// On cursor moves we loops through the buttons.
|
||||
var found = false,
|
||||
index = 0,
|
||||
direction = 1,
|
||||
checkCount = 0,
|
||||
current = e.target.ancestor('a[role="menuitem"]', true);
|
||||
|
||||
// Determine which button is currently selected.
|
||||
while (!found && index < buttons.size()) {
|
||||
if (buttons.item(index) === current) {
|
||||
found = true;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Y.log("Unable to find this menu item in the menu", 'debug', LOGNAME);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.keyCode === 38) {
|
||||
// Moving up so reverse the direction.
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
// Try to find the next
|
||||
do {
|
||||
index += direction;
|
||||
if (index < 0) {
|
||||
index = buttons.size() - 1;
|
||||
} else if (index >= buttons.size()) {
|
||||
// Handle wrapping.
|
||||
index = 0;
|
||||
}
|
||||
next = buttons.item(index);
|
||||
|
||||
// Add a counter to ensure we don't get stuck in a loop if there's only one visible menu item.
|
||||
checkCount++;
|
||||
// Loop while:
|
||||
// * we are not in a loop and have not already checked every button; and
|
||||
// * we are on a different button; and
|
||||
// * the next menu item is not hidden.
|
||||
} while (checkCount < buttons.size() && next !== current && next.hasAttribute('hidden'));
|
||||
|
||||
if (next) {
|
||||
next.focus();
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
}, {
|
||||
NAME: "menu",
|
||||
ATTRS: {
|
||||
@@ -87,17 +246,6 @@ Y.Base.modifyAttrs(Menu, {
|
||||
value: 'auto'
|
||||
},
|
||||
|
||||
/**
|
||||
* TODO - chekc that this is needed
|
||||
* The footer content.
|
||||
*
|
||||
* @attribute footerContent
|
||||
* @default ''
|
||||
*/
|
||||
footerContent: {
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* When to hide this menu.
|
||||
*
|
||||
@@ -114,6 +262,62 @@ Y.Base.modifyAttrs(Menu, {
|
||||
eventName: 'clickoutside'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* The default list of extra classes for this menu.
|
||||
*
|
||||
* @attribute extraClasses
|
||||
* @type Array
|
||||
* @default editor_atto_menu
|
||||
*/
|
||||
extraClasses: {
|
||||
value: [
|
||||
'editor_atto_menu'
|
||||
]
|
||||
},
|
||||
|
||||
/**
|
||||
* Override the responsive nature of the core dialogues.
|
||||
*
|
||||
* @attribute responsive
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
responsive: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* The default visibility of the menu.
|
||||
*
|
||||
* @attribute visible
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
visible: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether to centre the menu.
|
||||
*
|
||||
* @attribute center
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
center: {
|
||||
value: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the close button.
|
||||
* @attribute closeButton
|
||||
* @type boolean
|
||||
* @default false
|
||||
*/
|
||||
closeButton: {
|
||||
value: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1804,3 +1804,70 @@ body.lockscroll {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Dropdown Menu styling */
|
||||
.dropdown-menu {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
*border-right-width: 2px;
|
||||
*border-bottom-width: 2px;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-webkit-background-clip: padding-box;
|
||||
-moz-background-clip: padding;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
text-decoration: none;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
}
|
||||
.dropdown-menu > .disabled > a:hover,
|
||||
.dropdown-menu > .disabled > a:focus {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
cursor: default;
|
||||
}
|
||||
.open {
|
||||
*z-index: 1000;
|
||||
}
|
||||
.open > .dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.editor_atto_menu .moodle-dialogue-content {
|
||||
padding: 0px;
|
||||
border: inherit;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user