MDL-39814 frontend: improved display of activities and the menu

This commit is contained in:
Sam Hemelryk
2013-07-30 16:14:56 +12:00
parent e282c679e3
commit b59f2e3bef
21 changed files with 1327 additions and 783 deletions
+11 -11
View File
@@ -1808,7 +1808,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('update' => $mod->id)),
$str->edittitle,
null,
array('class' => 'editing_title', 'title' => $str->edittitle),
array('class' => 'editing_title', 'title' => $str->edittitle, 'data-action' => 'edittitle'),
new pix_icon('t/editstring', $str->edittitle, 'moodle', array('class' => 'iconsmall visibleifjs', 'title' => ''))
);
}
@@ -1831,7 +1831,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '-1')),
$str->moveleft,
null,
array('class' => 'editing_moveleft ' . $hiddenclass, 'title' => $str->moveleft),
array('class' => 'editing_moveleft ' . $hiddenclass, 'title' => $str->moveleft, 'data-action' => 'moveleft'),
new pix_icon($leftarrow, $str->moveleft, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$hiddenclass = 'hidden';
@@ -1842,7 +1842,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('id' => $mod->id, 'indent' => '1')),
$str->moveright,
null,
array('class' => 'editing_moveright ' . $hiddenclass, 'title' => $str->moveright),
array('class' => 'editing_moveright ' . $hiddenclass, 'title' => $str->moveright, 'data-action' => 'moveright'),
new pix_icon($rightarrow, $str->moveright, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1853,7 +1853,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('copy' => $mod->id)),
$str->move,
null,
array('class' => 'editing_move status', 'title' => $str->move),
array('class' => 'editing_move status', 'title' => $str->move, 'data-action' => 'move'),
new pix_icon('t/move', $str->move, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1864,7 +1864,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('update' => $mod->id)),
$str->update,
null,
array('class' => 'editing_update', 'title' => $str->update),
array('class' => 'editing_update', 'title' => $str->update, 'data-action' => 'update'),
new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1877,7 +1877,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('duplicate' => $mod->id)),
$str->duplicate,
null,
array('class' => 'editing_duplicate', 'title' => $str->duplicate),
array('class' => 'editing_duplicate', 'title' => $str->duplicate, 'data-action' => 'duplicate'),
new pix_icon('t/copy', $str->duplicate, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1888,7 +1888,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('delete' => $mod->id)),
$str->delete,
null,
array('class' => 'editing_delete', 'title' => $str->delete),
array('class' => 'editing_delete', 'title' => $str->delete, 'data-action' => 'delete'),
new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
@@ -1900,7 +1900,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('hide' => $mod->id)),
$str->hide,
null,
array('class' => 'editing_hide', 'title' => $str->hide),
array('class' => 'editing_hide', 'title' => $str->hide, 'data-action' => 'hide'),
new pix_icon('t/hide', $str->hide, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$actions['hide']->add_class('status');
@@ -1909,7 +1909,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('show' => $mod->id)),
$str->show,
null,
array('class' => 'editing_show', 'title' => $str->show),
array('class' => 'editing_show', 'title' => $str->show, 'data-action' => 'show'),
new pix_icon('t/show', $str->show, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
$actions['show']->add_class('status');
@@ -1947,7 +1947,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url($baseurl, array('id' => $mod->id, 'groupmode' => $groupmode)),
$grouptitle,
null,
array('class' => 'editing_'. $actionname, 'title' => $grouptitle),
array('class' => 'editing_'. $actionname, 'title' => $grouptitle, 'data-action' => $actionname, 'data-groupmode' => $groupmode),
new pix_icon($groupimage, $grouptitle, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
} else {
@@ -1962,7 +1962,7 @@ function course_get_cm_edit_actions(cm_info $mod, $indent = -1, $sr = null) {
new moodle_url('/admin/roles/assign.php', array('contextid' => $modcontext->id)),
$str->assign,
null,
array('class' => 'editing_assign', 'title' => $str->assign),
array('class' => 'editing_assign', 'title' => $str->assign, 'data-action' => 'assignroles'),
new pix_icon('t/assignroles', $str->assign, 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
}
+34 -28
View File
@@ -325,28 +325,40 @@ class core_course_renderer extends plugin_renderer_base {
*
* @see course_get_cm_edit_actions()
*
* @param array $actions array of action_link objects
* @param action_link[] $actions Array of action_link objects
* @param cm_info $mod The module we are displaying actions for.
* @param array $displayoptions additional display options:
* ownerselector => A JS/CSS selector that can be used to find an cm node.
* If specified the owning node will be given the class 'action-menu-shown' when the action
* menu is being displayed.
* @return string
*/
public function course_section_cm_edit_actions($actions) {
$this->page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
$output = html_writer::start_tag('span', array('class' => 'actionmenu'));
$output .= html_writer::start_tag('span', array('class' => 'statuses'));
public function course_section_cm_edit_actions($actions, cm_info $mod = null, $displayoptions = array()) {
if (empty($actions)) {
return '';
}
if (isset($displayoptions['ownerselector'])) {
$ownerselector = $displayoptions['ownerselector'];
} else if ($mod) {
$ownerselector = '#module-'.$mod->id;
} else {
debugging('You should upgrade your call to '.__FUNCTION__.' and provide $mod', DEBUG_DEVELOPER);
$ownerselector = 'li.activity';
}
$menu = new action_menu();
$menu->set_owner_selector($ownerselector);
foreach ($actions as $action) {
$action->add_class('cm-edit-action');
if ($action->has_class('status')) {
$output .= $this->output->render($action);
$menu->add_primary_action($action);
} else {
$menu->add_secondary_action($action);
}
}
$output .= html_writer::end_tag('span');
$output .= html_writer::start_tag('span', array('class' => 'actions'));
foreach ($actions as $action) {
if (!$action->has_class('status')) {
$output .= $this->output->render($action);
}
}
$output .= html_writer::end_tag('span');
$output .= html_writer::end_tag('span');
return $output;
$menu->attributes['class'] .= ' section-cm-edit-actions commands';
return $this->render($menu);
}
/**
@@ -868,7 +880,7 @@ class core_course_renderer extends plugin_renderer_base {
return $output;
}
$indentclasses = 'mod-indent clearfix';
$indentclasses = 'mod-indent';
if (!empty($mod->indent)) {
$indentclasses .= ' mod-indent-'.$mod->indent;
if ($mod->indent > 15) {
@@ -901,11 +913,14 @@ class core_course_renderer extends plugin_renderer_base {
$output .= $contentpart;
}
// Display completion first - so it is the right most floated element (and will align vertically).
if ($this->page->user_is_editing()) {
$editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
$output .= ' '. $this->course_section_cm_edit_actions($editactions, $mod, $displayoptions);
$output .= $mod->get_after_edit_icons();
}
$output .= $this->course_section_cm_completion($course, $completioninfo, $mod, $displayoptions);
// If there is content AND a link, then display the content here
// (AFTER any icons). Otherwise it was displayed before
if (!empty($url)) {
@@ -915,15 +930,6 @@ class core_course_renderer extends plugin_renderer_base {
// show availability info (if module is not available)
$output .= $this->course_section_cm_availability($mod, $displayoptions);
// Now action icons (if editing).
if ($this->page->user_is_editing()) {
$editactions = course_get_cm_edit_actions($mod, $mod->indent, $sectionreturn);
$output .= ' '. $this->course_section_cm_edit_actions($editactions);
$output .= $mod->get_after_edit_icons();
}
// Add a clear div so the container includes all floated elements.
$output .= html_writer::end_tag('div'); // $indentclasses
return $output;
}
+2 -3
View File
@@ -2,8 +2,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
var CSS = {
ACTIVITY : 'activity',
COMMANDSPAN : 'span.actionmenu',
SPINNERCOMMANDSPAN : 'span.instancename',
COMMANDSPAN : '.commands',
CONTENT : 'content',
COURSECONTENT : 'course-content',
EDITINGMOVE : 'editing_move',
@@ -352,7 +351,7 @@ YUI.add('moodle-course-dragdrop', function(Y) {
var dropnode = e.drop.get('node');
// Add spinner if it not there
var spinner = M.util.add_spinner(Y, dragnode.one(CSS.SPINNERCOMMANDSPAN));
var spinner = M.util.add_spinner(Y, dragnode.one(CSS.COMMANDSPAN));
var params = {};
+428 -363
View File
@@ -1,44 +1,47 @@
YUI.add('moodle-course-toolboxes', function(Y) {
WAITICON = {'pix':"i/loading_small",'component':'moodle'};
// The CSS selectors we use
// The following properties contain common strings.
// We separate them out here because when this JS is minified the content is less as
// Variables get compacted to single/double characters and the full length of the string
// exists only once.
// The CSS classes we use.
var CSS = {
ACTIVITYLI : 'li.activity',
COMMANDSPAN : 'span.actionmenu',
ACTIONPREFIX : 'span.actions',
SPINNERCOMMANDSPAN : 'span.instancename',
CONTENTAFTERLINK : 'div.contentafterlink',
DELETE : 'a.editing_delete',
ACTIVITYINSTANCE : 'activityinstance',
AVAILABILITYINFODIV : 'div.availabilityinfo',
CONDITIONALHIDDEN : 'conditionalhidden',
DIMCLASS : 'dimmed',
DIMMEDTEXT : 'dimmed_text',
EDITTITLE : 'a.editing_title',
EDITTITLECLASS : 'edittitle',
GENERICICONCLASS : 'iconsmall',
GROUPSNONE : 'a.editing_groupsnone',
GROUPSSEPARATE : 'a.editing_groupsseparate',
GROUPSVISIBLE : 'a.editing_groupsvisible',
EDITINSTRUCTIONS : 'editinstructions',
HIDE : 'hide',
MODINDENTCOUNT : 'mod-indent-',
MODINDENTHUGE : 'mod-indent-huge',
MODULEIDPREFIX : 'module-',
SECTIONHIDDENCLASS : 'hidden',
SECTIONIDPREFIX : 'section-',
SHOW : 'editing_show',
TITLEEDITOR : 'titleeditor'
},
// The CSS selectors we use.
SELECTOR = {
ACTIONLINKTEXT : '.actionlinktext',
ACTIVITYACTION : 'a.cm-edit-action[data-action]',
ACTIVITYFORM : 'form.'+CSS.ACTIVITYINSTANCE,
ACTIVITYICON : 'img.activityicon',
ACTIVITYLI : 'li.activity',
ACTIVITYTITLE : 'input[name=title]',
COMMANDSPAN : '.commands',
CONTENTAFTERLINK : 'div.contentafterlink',
HIDE : 'a.editing_hide',
HIGHLIGHT : 'a.editing_highlight',
INSTANCENAME : 'span.instancename',
LIGHTBOX : 'lightbox',
MODINDENTCOUNT : 'mod-indent-',
MODINDENTDIV : 'div.mod-indent',
MODINDENTHUGE : 'mod-indent-huge',
MODULEIDPREFIX : 'module-',
MOVELEFT : 'a.editing_moveleft',
MOVELEFTCLASS : 'editing_moveleft',
MOVERIGHT : 'a.editing_moveright',
PAGECONTENT : 'body',
RIGHTSIDE : '.right',
SECTIONHIDDENCLASS : 'hidden',
SECTIONIDPREFIX : 'section-',
PAGECONTENT : 'div#page-content',
SECTIONLI : 'li.section',
SHOW : 'a.editing_show',
SHOWHIDE : 'a.editing_showhide',
CONDITIONALHIDDEN : 'conditionalhidden',
AVAILABILITYINFODIV : 'div.availabilityinfo',
SHOWCLASS : 'editing_show',
HIDECLASS : 'hide'
};
SHOW : 'a.'+CSS.SHOW,
SHOWHIDE : 'a.editing_showhide'
},
BODY = Y.one(document.body);
/**
* The toolbox classes
@@ -52,98 +55,6 @@ YUI.add('moodle-course-toolboxes', function(Y) {
}
Y.extend(TOOLBOX, Y.Base, {
/**
* Toggle the visibility and availability for the specified
* resource show/hide button
*/
toggle_hide_resource_ui : function(button) {
var element = this.get_activity_container(button);
var hideicon = button.one('img');
var dimarea;
var toggle_class;
if (this.get_instance_name(element) == null) {
toggle_class = CSS.DIMMEDTEXT;
dimarea = element.all(CSS.MODINDENTDIV + ' > div').item(1);
} else {
toggle_class = CSS.DIMCLASS;
dimarea = element.one('a');
}
var status = '';
var value;
if (button.hasClass(CSS.SHOWCLASS)) {
status = 'hide';
value = 1;
} else {
status = 'show';
value = 0;
}
// Update button info.
var newstring = M.util.get_string(status, 'moodle');
hideicon.setAttrs({
'alt' : newstring,
'src' : M.util.image_url('t/' + status)
});
button.set('title', newstring);
button.set('className', 'editing_'+status);
// If activity is conditionally hidden, then don't toggle.
if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
// Change the UI.
dimarea.toggleClass(toggle_class);
// We need to toggle dimming on the description too.
element.all(CSS.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
}
// Toggle availablity info for conditional activities.
var availabilityinfo = element.one(CSS.AVAILABILITYINFODIV);
if (availabilityinfo) {
availabilityinfo.toggleClass(CSS.HIDECLASS);
}
return value;
},
get_activity_action_link : function(node, selector) {
// The node is either a direct descendant of the activity container,
// or it is linked through the actionmenu.
Y.log('find activity container');
var element = node.one(selector);
if (element) {
Y.log('direct ancestor ' + element);
return element;
}
var actionmenulink = node.one('a.actionmenu');
Y.log('get action menu link ' + actionmenulink);
var overlay = actionmenulink.getData('actionmenu-menu');
element = overlay.get('boundingBox').one(selector);
Y.log('get ancestor ' + element);
return element;
},
get_activity_container : function(node) {
// The node is either a direct descendant of the activity container,
// or it is linked through the actionmenu.
Y.log('find activity container');
var element = node.ancestor(CSS.ACTIVITYLI);
if (element) {
Y.log('direct ancestor ' + element);
return element;
}
var actionlink = node.ancestor('a');
Y.log('get action link ' + actionlink);
var actionmenulink = actionlink.getData('actionmenu-link');
Y.log('get menu link ' + actionmenulink);
element = actionmenulink.ancestor(CSS.ACTIVITYLI);
Y.log('get ancestor ' + element);
return element;
},
/**
* Send a request using the REST API
*
@@ -222,8 +133,8 @@ YUI.add('moodle-course-toolboxes', function(Y) {
* @return string|null Instance name
*/
get_instance_name : function(target) {
if (target.one(CSS.INSTANCENAME)) {
return target.one(CSS.INSTANCENAME).get('firstChild').get('data');
if (target.one(SELECTOR.INSTANCENAME)) {
return target.one(SELECTOR.INSTANCENAME).get('firstChild').get('data');
}
return null;
},
@@ -263,81 +174,157 @@ YUI.add('moodle-course-toolboxes', function(Y) {
}
);
/**
* Resource and activity toolbox class.
*
* This class is responsible for managing AJAX interactions with activities and resources
* when viewing a course in editing mode.
*
* @namespace M.course.toolbox
* @class ResourceToolbox
* @constructor
*/
var RESOURCETOOLBOX = function() {
RESOURCETOOLBOX.superclass.constructor.apply(this, arguments);
}
Y.extend(RESOURCETOOLBOX, TOOLBOX, {
// Variables
/**
* No groups are being used.
* @static
* @const GROUPS_NONE
* @type Number
*/
GROUPS_NONE : 0,
/**
* Separate groups are being used.
* @static
* @const GROUPS_SEPARATE
* @type Number
*/
GROUPS_SEPARATE : 1,
/**
* Visible groups are being used.
* @static
* @const GROUPS_VISIBLE
* @type Number
*/
GROUPS_VISIBLE : 2,
/**
* Events that were added when editing a title.
* These should all be detached when editing is complete.
* @property edittitleevents
* @type {Event[]}
* @protected
*/
edittitleevents : [],
/**
* Initialize the resource toolbox
*
* Updates all span.commands with relevant handlers and other required changes
* For each activity the commands are updated and a reference to the activity is attached.
* This way it doesn't matter where the commands are going to called from they have a reference to the
* activity that they relate to.
* This is essential as some of the actions are displayed in an actionmenu which removes them from the
* page flow.
*
* This function also creates a single event delegate to manage all AJAX actions for all activities on
* the page.
*
* @method initializer
*/
initializer : function(config) {
this.setup_for_resource();
M.course.coursebase.register_module(this);
var prefix = CSS.ACTIVITYLI + ' ' + CSS.COMMANDSPAN + ' ';
Y.delegate('click', this.edit_resource_title, CSS.PAGECONTENT, CSS.EDITTITLE, this);
Y.delegate('click', this.move_left, CSS.PAGECONTENT, CSS.MOVELEFT, this);
Y.delegate('click', this.move_right, CSS.PAGECONTENT, CSS.MOVERIGHT, this);
Y.delegate('click', this.delete_resource, CSS.PAGECONTENT, prefix + CSS.DELETE, this);
Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.HIDE, this);
Y.delegate('click', this.toggle_hide_resource, CSS.PAGECONTENT, prefix + CSS.SHOW, this);
Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSNONE, this);
Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSSEPARATE, this);
Y.delegate('click', this.toggle_groupmode, CSS.PAGECONTENT, prefix + CSS.GROUPSVISIBLE, this);
Y.all(SELECTOR.ACTIVITYLI).each(function(activity){
activity.all(SELECTOR.COMMANDSPAN+ ' ' + SELECTOR.ACTIVITYACTION).each(function(){
this.setData('activity', activity);
});
}, this);
Y.delegate('click', this.handle_data_action, BODY, SELECTOR.ACTIVITYACTION, this);
},
/**
* Update any span.commands within the scope of the specified
* selector with AJAX equivelants
* Handles the delegation event. When this is fired someone has triggered an action.
*
* @param baseselector The selector to limit scope to
* @return void
* Note not all actions will result in an AJAX enhancement.
*
* @protected
* @method handle_data_action
* @param {EventFacade} ev The event that was triggered.
* @returns {boolean}
*/
setup_for_resource : function(baseselector) {
if (!baseselector) {
var baseselector = CSS.PAGECONTENT + ' ' + CSS.ACTIVITYLI;
handle_data_action : function(ev) {
// We need to get the anchor element that triggered this event.
var node = ev.target;
if (!node.test('a')) {
node = node.ancestor(SELECTOR.ACTIVITYACTION);
}
Y.all(baseselector).each(this._setup_for_resource, this);
},
_setup_for_resource : function(toolboxtarget) {
toolboxtarget = Y.one(toolboxtarget);
// From the anchor we can get both the activity (added during initialisation) and the action being
// performed (added by the UI as a data attribute)
var action = node.getData('action'),
activity = node.getData('activity');
if (!node.test('a') || !action || !activity) {
// It wasn't a valid action node.
return;
}
// Set groupmode attribute for use by this.toggle_groupmode()
var groups;
groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSNONE);
groups.setAttribute('groupmode', this.GROUPS_NONE);
// Switch based upon the action and do the desired thing.
switch (action) {
case 'edittitle' :
// The user wishes to edit the title of the event.
this.edit_title(ev, node, activity, action);
break;
case 'moveleft' :
case 'moveright' :
// The user changing the indent of the activity.
this.change_indent(ev, node, activity, action);
break;
case 'delete' :
// The user is deleting the activity.
this.delete_with_confirmation(ev, node, activity, action);
break;
case 'hide' :
case 'show' :
// The user is changing the visibility of the activity.
this.change_visibility(ev, node, activity, action);
break;
case 'groupsseparate' :
case 'groupsvisible' :
case 'groupsnone' :
// The user is changing the group mode.
callback = 'change_groupmode';
this.change_groupmode(ev, node, activity, action);
break;
case 'move' :
case 'update' :
case 'duplicate' :
case 'assignroles' :
default:
// Nothing to do here!
break;
}
},
groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSSEPARATE);
groups.setAttribute('groupmode', this.GROUPS_SEPARATE);
groups = toolboxtarget.all(CSS.COMMANDSPAN + ' ' + CSS.GROUPSVISIBLE);
groups.setAttribute('groupmode', this.GROUPS_VISIBLE);
},
move_left : function(e) {
this.move_leftright(e, -1);
},
move_right : function(e) {
this.move_leftright(e, 1);
},
move_leftright : function(e, direction) {
/**
* Change the indent of the activity or resource.
*
* @protected
* @method change_indent
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @param {String} action The action that has been requested. Will be 'moveleft' or 'moveright'.
*/
change_indent : function(ev, button, activity, action) {
// Prevent the default button action
e.preventDefault();
ev.preventDefault();
// Get the element we're working on
var element = this.get_activity_container(e.target);
var direction = (action === 'moveleft') ? -1 : 1;
// And we need to determine the current and new indent level
var indentdiv = element.one(CSS.MODINDENTDIV);
var indentdiv = activity.one(SELECTOR.MODINDENTDIV);
var indent = indentdiv.getAttribute('class').match(/mod-indent-(\d{1,})/);
if (indent) {
@@ -355,17 +342,16 @@ YUI.add('moodle-course-toolboxes', function(Y) {
'class' : 'resource',
'field' : 'indent',
'value' : newindent,
'id' : this.get_element_id(element)
'id' : this.get_element_id(activity)
};
var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.COMMANDSPAN));
this.send_request(data, spinner);
// Handle removal/addition of the moveleft button
Y.log(element);
if (newindent == 0) {
this.get_activity_action_link(element, CSS.MOVELEFT).addClass('hidden');
button.remove();
} else if (newindent == 1 && oldindent == 0) {
this.get_activity_action_link(element, CSS.MOVELEFT).removeClass('hidden');
this.change_indent_add_left_button(button);
}
// Handle massive indentation to match non-ajax display
@@ -376,13 +362,23 @@ YUI.add('moodle-course-toolboxes', function(Y) {
indentdiv.removeClass(CSS.MODINDENTHUGE);
}
},
delete_resource : function(e) {
/**
* Deletes the given activity or resource after confirmation.
*
* @protected
* @method delete_with_confirmation
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @return Boolean
*/
delete_with_confirmation : function(ev, button, activity) {
// Prevent the default button action
e.preventDefault();
ev.preventDefault();
// Get the element we're working on
var element = this.get_activity_container(e.target);
var element = activity
// Create confirm string (different if element has or does not have name)
var confirmstring = '';
@@ -409,23 +405,35 @@ YUI.add('moodle-course-toolboxes', function(Y) {
'id' : this.get_element_id(element)
};
this.send_request(data);
if (M.core.actionmenu && M.core.actionmenu.instance) {
M.core.actionmenu.instance.hideMenu();
}
},
toggle_hide_resource : function(e) {
/**
* Changes the visibility of this activity or resource.
*
* @protected
* @method change_visibility
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @param {String} action The action that has been requested.
* @return Boolean
*/
change_visibility : function(ev, button, activity, action) {
// Prevent the default button action
e.preventDefault();
ev.preventDefault();
// Return early if the current section is hidden
var section = e.target.ancestor(M.course.format.get_section_selector(Y));
var section = activity.ancestor(M.course.format.get_section_selector(Y));
if (section && section.hasClass(CSS.SECTIONHIDDENCLASS)) {
return;
}
// Get the element we're working on
var element = this.get_activity_container(e.target);
var button = e.target.ancestor('a', true);
var value = this.toggle_hide_resource_ui(button);
var element = activity;
var value = this.handle_resource_dim(button, activity, action);
// Send the request
var data = {
@@ -434,62 +442,107 @@ YUI.add('moodle-course-toolboxes', function(Y) {
'value' : value,
'id' : this.get_element_id(element)
};
var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
var spinner = M.util.add_spinner(Y, element.one(SELECTOR.COMMANDSPAN));
this.send_request(data, spinner);
return false; // Need to return false to stop the delegate for the new state firing
},
toggle_groupmode : function(e) {
// Prevent the default button action
e.preventDefault();
// Get the element we're working on
var element = this.get_activity_container(e.target);
/**
* Handles the UI aspect of dimming the activity or resource
*
* @protected
* @method handle_resource_dim
* @param {Node} button The button that triggered the action.
* @param {Node} activity The activity node that this action will be performed on.
* @param {String} status Whether the activity was shown or hidden.
* @returns {number} 1 if we were changing to visible, 0 if we were hiding.
*/
handle_resource_dim : function(button, activity, status) {
var toggleclass = CSS.DIMCLASS,
dimarea = activity.one('a'),
availabilityinfo = activity.one(CSS.AVAILABILITYINFODIV),
newstatus = (status === 'hide') ? 'show' : 'hide',
newstring = M.util.get_string(newstatus, 'moodle');
var button = e.target.ancestor('a', true);
var icon = button.one('img');
// Update button info.
button.one('img').setAttrs({
'alt' : newstring,
'src' : M.util.image_url('t/' + newstatus)
});
button.set('title', newstring);
button.replaceClass('editing_'+status, 'editing_'+newstatus)
button.setData('action', newstatus);
// If activity is conditionally hidden, then don't toggle.
if (this.get_instance_name(activity) == null) {
toggleclass = CSS.DIMMEDTEXT;
dimarea = activity.all(SELECTOR.MODINDENTDIV + ' > div').item(1);
}
if (!dimarea.hasClass(CSS.CONDITIONALHIDDEN)) {
// Change the UI.
dimarea.toggleClass(toggleclass);
// We need to toggle dimming on the description too.
activity.all(SELECTOR.CONTENTAFTERLINK).toggleClass(CSS.DIMMEDTEXT);
}
// Toggle availablity info for conditional activities.
if (availabilityinfo) {
availabilityinfo.toggleClass(CSS.HIDE);
}
return (status === 'hide') ? 0 : 1;
},
/**
* Changes the groupmode of the activity to the next groupmode in the sequence.
*
* @protected
* @method change_groupmode
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @param {String} action The action that has been requested.
* @return Boolean
*/
change_groupmode : function(ev, button, activity, action) {
// Prevent the default button action.
ev.preventDefault();
// Current Mode
var groupmode = button.getAttribute('groupmode');
groupmode++;
var oldgroupmode = button.getData('groupmode');
var groupmode = parseInt(oldgroupmode, 10) + 1;
if (groupmode > 2) {
groupmode = 0;
}
button.setAttribute('groupmode', groupmode);
var newtitle = '';
var iconsrc = '';
switch (groupmode) {
case this.GROUPS_NONE:
newtitle = 'groupsnone';
iconsrc = M.util.image_url('t/groupn');
break;
case this.GROUPS_SEPARATE:
newtitle = 'groupsseparate';
iconsrc = M.util.image_url('t/groups');
break;
case this.GROUPS_VISIBLE:
newtitle = 'groupsvisible';
iconsrc = M.util.image_url('t/groupv');
break;
if (groupmode === this.GROUPS_NONE) {
newtitle = 'groupsnone';
iconsrc = M.util.image_url('t/groupn', 'moodle');
} else if (groupmode === this.GROUPS_SEPARATE) {
newtitle = 'groupsseparate';
iconsrc = M.util.image_url('t/groups', 'moodle');
} else if (groupmode === this.GROUPS_VISIBLE) {
newtitle = 'groupsvisible';
iconsrc = M.util.image_url('t/groupv', 'moodle');
}
newtitle = M.util.get_string('clicktochangeinbrackets', 'moodle',
M.util.get_string(newtitle, 'moodle'));
var newtitlestr = M.util.get_string(newtitle, 'moodle'),
newtitlestr = M.util.get_string('clicktochangeinbrackets', 'moodle', newtitlestr);
// Change the UI
icon.setAttrs({
'alt' : newtitle,
button.one('img').setAttrs({
'alt' : newtitlestr,
'src' : iconsrc
});
button.setAttribute('title', newtitle);
button.setAttribute('title', newtitlestr).setData('action', newtitle).setData('groupmode', groupmode);
// And send the request
var data = {
'class' : 'resource',
'field' : 'groupmode',
'value' : groupmode,
'id' : this.get_element_id(element)
'id' : this.get_element_id(activity)
};
var spinner = M.util.add_spinner(Y, element.one(CSS.SPINNERCOMMANDSPAN));
var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.COMMANDSPAN));
this.send_request(data, spinner);
return false; // Need to return false to stop the delegate for the new state firing
},
@@ -497,176 +550,188 @@ YUI.add('moodle-course-toolboxes', function(Y) {
* Add the moveleft button
* This is required after moving left from an initial position of 0
*
* @param target The encapsulating <li> element
* @protected
* @method change_indent_add_left_button
* @param {Node} moveright. The move right icon which should always be there.
*/
add_moveleft : function(target) {
Y.log(target);
var left_string = M.util.get_string('moveleft', 'moodle');
var moveimage = 't/left'; // ltr mode
if ( Y.one(document.body).hasClass('dir-rtl') ) {
moveimage = 't/right';
} else {
moveimage = 't/left';
change_indent_add_left_button : function(moveright) {
var moveleft = moveright.cloneNode(true);
var icon = (BODY.hasClass('dir-rtl')) ? 't/right' : 't/left';
var str = M.util.get_string('moveleft', 'moodle');
moveleft.setAttrs({
'title' : str,
'href' : moveright.getAttribute('href').replace('indent=1', 'indent=-1')
});
moveleft.setData('activity', moveright.getData('activity'));
moveleft.setData('action', 'moveleft');
moveleft.one('img').setAttrs({
'alt' : str,
'src' : M.util.image_url(icon, 'moodle')
});
if (moveleft.one(SELECTOR.ACTIONLINKTEXT)) {
moveleft.one(SELECTOR.ACTIONLINKTEXT).setContent(str);
}
var newicon = Y.Node.create('<img />')
.addClass(CSS.GENERICICONCLASS)
.setAttrs({
'src' : M.util.image_url(moveimage, 'moodle'),
'alt' : left_string
});
var moveright = target.one(CSS.MOVERIGHT);
var newlink = moveright.getAttribute('href').replace('indent=1', 'indent=-1');
var anchor = new Y.Node.create('<a />')
.setStyle('cursor', 'pointer')
.addClass(CSS.MOVELEFTCLASS)
.setAttribute('href', newlink)
.setAttribute('title', left_string);
anchor.appendChild(newicon);
moveright.insert(anchor, 'before');
moveright.insert(moveleft, 'before');
},
/**
* Edit the title for the resource
*
* @protected
* @method edit_title
* @param {EventFacade} ev The event that was fired.
* @param {Node} button The button that triggered this action.
* @param {Node} activity The activity node that this action will be performed on.
* @param {String} action The action that has been requested.
* @return Boolean
*/
edit_resource_title : function(e) {
edit_title : function(ev, button, activity) {
// Get the element we're working on
var element = this.get_activity_container(e.target);
var elementdiv = element.one('div');
var instancename = element.one(CSS.INSTANCENAME);
var currenttitle = instancename.get('firstChild');
var oldtitle = currenttitle.get('data');
var titletext = oldtitle;
var editbutton = element.one('a.' + CSS.EDITTITLECLASS + ' img');
var activityid = this.get_element_id(activity),
instancename = activity.one(SELECTOR.INSTANCENAME),
currenttitle = instancename.get('firstChild'),
oldtitle = currenttitle.get('data'),
titletext = oldtitle,
thisevent,
anchor = instancename.ancestor('a'), // Grab the anchor so that we can swap it with the edit form
data = {
'class' : 'resource',
'field' : 'gettitle',
'id' : activityid
},
response = this.send_request(data);
// Handle events for edit_resource_title
var listenevents = [];
var thisevent;
// Grab the anchor so that we can swap it with the edit form
var anchor = instancename.ancestor('a');
var data = {
'class' : 'resource',
'field' : 'gettitle',
'id' : this.get_element_id(element)
};
if (M.core.actionmenu && M.core.actionmenu.instance) {
M.core.actionmenu.instance.hideMenu();
}
// Try to retrieve the existing string from the server
var response = this.send_request(data, editbutton);
if (response.instancename) {
titletext = response.instancename;
}
// Create the editor and submit button
var editor = Y.Node.create('<input />')
.setAttrs({
'name' : 'title',
'value' : titletext,
'autocomplete' : 'off',
'aria-describedby' : 'id_editinstructions',
'maxLength' : '255'
})
.addClass('titleeditor');
var editform = Y.Node.create('<form />')
.addClass('activityinstance')
.setAttribute('action', '#');
var editinstructions = Y.Node.create('<span />')
.addClass('editinstructions')
.setAttrs({'id' : 'id_editinstructions'})
var editform = Y.Node.create('<form class="'+CSS.ACTIVITYINSTANCE+'" action="#" />');
var editinstructions = Y.Node.create('<span class="'+CSS.EDITINSTRUCTIONS+'" id="id_editinstructions" />')
.set('innerHTML', M.util.get_string('edittitleinstructions', 'moodle'));
var activityicon = element.one('img.activityicon').cloneNode();
var editor = Y.Node.create('<input name="title" type="text" class="'+CSS.TITLEEDITOR+'" />').setAttrs({
'value' : titletext,
'autocomplete' : 'off',
'aria-describedby' : 'id_editinstructions',
'maxLength' : '255'
})
// Clear the existing content and put the editor in
currenttitle.set('data', '');
editform.appendChild(activityicon);
editform.appendChild(activity.one(SELECTOR.ACTIVITYICON).cloneNode());
editform.appendChild(editor);
editform.setData('anchor', anchor);
anchor.replace(editform);
elementdiv.appendChild(editinstructions);
e.preventDefault();
activity.one('div').appendChild(editinstructions);
ev.preventDefault();
// Focus and select the editor text
editor.focus().select();
// Handle removal of the editor
var clear_edittitle = function() {
// Detach all listen events to prevent duplicate triggers
var thisevent;
while (thisevent = listenevents.shift()) {
thisevent.detach();
}
if (editinstructions) {
// Convert back to anchor and remove instructions
editform.replace(anchor);
editinstructions.remove();
editinstructions = null;
}
}
// Handle cancellation of the editor
var cancel_edittitle = function(e) {
clear_edittitle();
// Set the title and anchor back to their previous settings
currenttitle.set('data', oldtitle);
};
// Cancel the edit if we lose focus or the escape key is pressed
thisevent = editor.on('blur', cancel_edittitle);
listenevents.push(thisevent);
thisevent = Y.one('document').on('keydown', function(e) {
if (e.keyCode === 27) {
e.preventDefault();
cancel_edittitle(e);
}
});
listenevents.push(thisevent);
thisevent = editor.on('blur', this.edit_title_cancel, this, activity, false);
this.edittitleevents.push(thisevent);
thisevent = editor.on('key', this.edit_title_cancel, 'esc', this, activity, true);
this.edittitleevents.push(thisevent);
// Handle form submission
thisevent = editform.on('submit', function(e) {
// We don't actually want to submit anything
e.preventDefault();
// Clear the edit title boxes
clear_edittitle();
// We only accept strings which have valid content
var newtitle = Y.Lang.trim(editor.get('value'));
if (newtitle != null && newtitle != "" && newtitle != titletext) {
var data = {
'class' : 'resource',
'field' : 'updatetitle',
'title' : newtitle,
'id' : this.get_element_id(element)
};
var response = this.send_request(data, editbutton);
if (response.instancename) {
currenttitle.set('data', response.instancename);
}
} else {
// Invalid content. Set the title back to it's original contents
currenttitle.set('data', oldtitle);
}
}, this);
listenevents.push(thisevent);
thisevent = editform.on('submit', this.edit_title_submit, this, activity, oldtitle);
this.edittitleevents.push(thisevent);
},
/**
* Handles the submit event when editing the activity or resources title.
*
* @protected
* @method edit_title_submit
* @param {EventFacade} ev The event that triggered this.
* @param {Node} activity The activity whose title we are altering.
* @param {String} originaltitle The original title the activity or resource had.
*/
edit_title_submit : function(ev, activity, originaltitle) {
// We don't actually want to submit anything
ev.preventDefault();
var newtitle = Y.Lang.trim(activity.one(SELECTOR.ACTIVITYFORM + ' ' + SELECTOR.ACTIVITYTITLE).get('value'));
this.edit_title_clear(activity);
var spinner = M.util.add_spinner(Y, activity.one(SELECTOR.INSTANCENAME));
if (newtitle != null && newtitle != "" && newtitle != originaltitle) {
var data = {
'class' : 'resource',
'field' : 'updatetitle',
'title' : newtitle,
'id' : this.get_element_id(activity)
};
var response = this.send_request(data, spinner);
if (response.instancename) {
activity.one(SELECTOR.INSTANCENAME).setContent(response.instancename);
}
}
},
/**
* Handles the cancel event when editing the activity or resources title.
*
* @protected
* @method edit_title_cancel
* @param {EventFacade} ev The event that triggered this.
* @param {Node} activity The activity whose title we are altering.
* @param {Boolean} preventdefault If true we should prevent the default action from occuring.
*/
edit_title_cancel : function(ev, activity, preventdefault) {
if (preventdefault) {
ev.preventDefault();
}
this.edit_title_clear(activity);
},
/**
* Handles clearing the editing UI and returning things to the original state they were in.
*
* @protected
* @method edit_title_clear
* @param {Node} activity The activity whose title we were altering.
*/
edit_title_clear : function(activity) {
// Detach all listen events to prevent duplicate triggers
var thisevent;
while (thisevent = this.edittitleevents.shift()) {
thisevent.detach();
}
var editform = activity.one(SELECTOR.ACTIVITYFORM),
instructions = activity.one('#id_editinstructions');
if (editform) {
editform.replace(editform.getData('anchor'));
}
if (instructions) {
instructions.remove();
}
},
/**
* Set the visibility of the current resource (identified by the element)
* to match the hidden parameter (this is not a toggle).
* Only changes the visibility in the browser (no ajax update).
*
* @public This method is used by other modules.
* @method set_visibility_resource_ui
* @param args An object with 'element' being the A node containing the resource
* and 'visible' being the state that the visiblity should be set to.
* @return void
* and 'visible' being the state that the visibility should be set to.
*/
set_visibility_resource_ui: function(args) {
var element = args.element;
var shouldbevisible = args.visible;
var buttonnode = element.one(CSS.SHOW);
var buttonnode = element.one(SELECTOR.SHOW);
var visible = (buttonnode === null);
if (visible) {
buttonnode = element.one(CSS.HIDE);
buttonnode = element.one(SELECTOR.HIDE);
}
if (visible != shouldbevisible) {
this.toggle_hide_resource_ui(buttonnode);
this.change_visibility_ui(buttonnode);
}
}
}, {
@@ -696,9 +761,9 @@ YUI.add('moodle-course-toolboxes', function(Y) {
M.course.coursebase.register_module(this);
// Section Highlighting
Y.delegate('click', this.toggle_highlight, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.HIGHLIGHT, this);
Y.delegate('click', this.toggle_highlight, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.HIGHLIGHT, this);
// Section Visibility
Y.delegate('click', this.toggle_hide_section, CSS.PAGECONTENT, CSS.SECTIONLI + ' ' + CSS.SHOWHIDE, this);
Y.delegate('click', this.toggle_hide_section, SELECTOR.PAGECONTENT, SELECTOR.SECTIONLI + ' ' + SELECTOR.SHOWHIDE, this);
},
/**
* Update any section areas within the scope of the specified
@@ -710,7 +775,7 @@ YUI.add('moodle-course-toolboxes', function(Y) {
setup_for_section : function(baseselector) {
// Left here for potential future use - not currently needed due to YUI delegation in initializer()
/*if (!baseselector) {
var baseselector = CSS.PAGECONTENT;
var baseselector = SELECTOR.PAGECONTENT;
}
Y.all(baseselector).each(this._setup_for_section, this);*/
@@ -763,17 +828,17 @@ YUI.add('moodle-course-toolboxes', function(Y) {
var response = this.send_request(data, lightbox);
var activities = section.all(CSS.ACTIVITYLI);
var activities = section.all(SELECTOR.ACTIVITYLI);
activities.each(function(node) {
if (node.one(CSS.SHOW)) {
var button = node.one(CSS.SHOW);
if (node.one(SELECTOR.SHOW)) {
var button = node.one(SELECTOR.SHOW);
} else {
var button = node.one(CSS.HIDE);
var button = node.one(SELECTOR.HIDE);
}
var activityid = this.get_element_id(node);
if (Y.Array.indexOf(response.resourcestotoggle, activityid) != -1) {
this.toggle_hide_resource_ui(button);
this.change_visibility_ui(button);
}
}, this);
},
@@ -792,16 +857,16 @@ YUI.add('moodle-course-toolboxes', function(Y) {
// Set the current highlighted item text
var old_string = M.util.get_string('markthistopic', 'moodle');
Y.one(CSS.PAGECONTENT)
.all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT)
Y.one(SELECTOR.PAGECONTENT)
.all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT)
.set('title', old_string);
Y.one(CSS.PAGECONTENT)
.all(M.course.format.get_section_selector(Y) + '.current ' + CSS.HIGHLIGHT + ' img')
Y.one(SELECTOR.PAGECONTENT)
.all(M.course.format.get_section_selector(Y) + '.current ' + SELECTOR.HIGHLIGHT + ' img')
.set('alt', old_string)
.set('src', M.util.image_url('i/marker'));
// Remove the highlighting from all sections
var allsections = Y.one(CSS.PAGECONTENT).all(M.course.format.get_section_selector(Y))
var allsections = Y.one(SELECTOR.PAGECONTENT).all(M.course.format.get_section_selector(Y))
.removeClass('current');
// Then add it if required to the selected section
+3 -22
View File
@@ -1037,9 +1037,6 @@ class block_manager {
if ($this->page->user_can_edit_blocks()) {
// Move icon.
//$controls[] = array('url' => $actionurl . '&bui_moveid=' . $block->instance->id,
// 'icon' => 't/move', 'caption' => get_string('moveblock', 'block', $blocktitle),
// 'class' => 'editing_move status');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_moveid' => $block->instance->id)),
get_string('moveblock', 'block', $blocktitle),
@@ -1052,14 +1049,11 @@ class block_manager {
if ($this->page->user_can_edit_blocks() || $block->user_can_edit()) {
// Edit config icon - always show - needed for positioning UI.
//$controls[] = array('url' => $actionurl . '&bui_editid=' . $block->instance->id,
// 'icon' => 't/edit', 'caption' => get_string('configureblock', 'block', $blocktitle),
// 'class' => 'editing_edit');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_editid' => $block->instance->id)),
get_string('configureblock', 'block', $blocktitle),
null,
array('class' => 'editing_edit', 'title' => get_string('configureblock', 'block', $blocktitle)),
array('class' => 'editing_edit status', 'title' => get_string('configureblock', 'block', $blocktitle)),
new pix_icon('t/edit', get_string('configureblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
@@ -1067,9 +1061,6 @@ class block_manager {
if ($this->user_can_delete_block($block)) {
// Delete icon.
//$controls[] = array('url' => $actionurl . '&bui_deleteid=' . $block->instance->id,
//// 'icon' => 't/delete', 'caption' => get_string('deleteblock', 'block', $blocktitle),
// 'class' => 'editing_delete');
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_deleteid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
@@ -1084,25 +1075,19 @@ class block_manager {
if ($block->instance->visible) {
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_hideid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
get_string('hideblock', 'block', $blocktitle),
null,
array('class' => 'editing_hide status', 'title' => get_string('hideblock', 'block', $blocktitle)),
new pix_icon('t/hide', get_string('hideblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $actionurl . '&bui_hideid=' . $block->instance->id,
// 'icon' => 't/hide', 'caption' => get_string('hideblock', 'block', $blocktitle),
// 'class' => 'editing_hide status');
} else {
$controls[] = new action_link(
new moodle_url($actionurl, array('bui_showid' => $block->instance->id)),
get_string('deleteblock', 'block', $blocktitle),
get_string('showblock', 'block', $blocktitle),
null,
array('class' => 'editing_show status', 'title' => get_string('showblock', 'block', $blocktitle)),
new pix_icon('t/show', get_string('showblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $actionurl . '&bui_showid=' . $block->instance->id,
// 'icon' => 't/show', 'caption' => get_string('showblock', 'block', $blocktitle),
// 'class' => 'editing_show status');
}
}
@@ -1123,10 +1108,6 @@ class block_manager {
array('class' => 'editing_roles', 'title' => get_string('assignrolesinblock', 'block', $blocktitle)),
new pix_icon('t/assignroles', get_string('assignrolesinblock', 'block', $blocktitle), 'moodle', array('class' => 'iconsmall', 'title' => ''))
);
//$controls[] = array('url' => $CFG->wwwroot . '/' . $CFG->admin .
// '/roles/assign.php?contextid=' . $block->context->id . '&returnurl=' . urlencode($return),
// 'icon' => 't/assignroles', 'caption' => get_string('assignrolesinblock', 'block', $blocktitle),
// 'class' => 'editing_roles');
}
return $controls;
}
+160 -35
View File
@@ -2878,41 +2878,6 @@ class custom_menu extends custom_menu_item {
}
}
/**
* A list of action links/icons relating to a specific element in the page.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @package core
*/
class action_menu implements renderable {
/** @var string unique id of the tab in this tree, it is used to find selected and/or inactive tabs */
var $id;
/** @var string title under the link, by default equals to text */
var $title;
/** @var pix_icon representing the icon to open the menu */
var $icon;
/** @var array of action_link objects representing a list of menu items */
var $items;
/**
* Constructor
*
* @param string $title title for the menu. Should include the name of the item this menu relates to.
* @param string $icon pix_icon for the menu. Defaults to a menu icon.
* @param array $items a list of items to put in the menu.
*/
public function __construct($title, $icon = '', $items = array()) {
$this->id = html_writer::random_id('actionmenu');
$this->icon = $icon;
$this->title = $title;
$this->items = $items;
}
public function add(action_link $link) {
array_push($this->items, $link);
}
}
/**
* Stores one tab
*
@@ -3064,3 +3029,163 @@ class tabtree extends tabobject {
$this->set_level(0);
}
}
/**
* An action menu.
*
* This action menu component takes a series of primary and secondary actions.
* The primary actions are displayed permanently and the secondary attributes are displayed within a drop
* down menu.
*
* @package core
* @category output
* @copyright 2013 Sam Hemelryk
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class action_menu implements renderable {
/**
* An array of primary actions. Please use {@see action_menu::add_primary_action()} to add actions.
* @var array
*/
protected $primaryactions = array();
/**
* An array of secondary actions. Please use {@see action_menu::add_secondary_action()} to add actions.
* @var array
*/
protected $secondaryactions = array();
/**
* An array of attributes added to the container of the action menu.
* Initialised with defaults during construction.
* @var array
*/
public $attributes = array();
/**
* An array of attributes added to the container of the primary actions.
* Initialised with defaults during construction.
* @var array
*/
public $attributesprimary = array();
/**
* An array of attributes added to the container of the secondary actions.
* Initialised with defaults during construction.
* @var array
*/
public $attributessecondary = array();
/**
* If set to true only the icons for primary action_link objects will be displayed.
* @var bool
*/
public $displayprimaryiconsonly = true;
/**
* Constructs the action menu with the given items.
*
* @param array $primaryactions An array of primary actions.
* @param array $secondaryactions An array of secondary actions.
*/
public function __construct(array $primaryactions = array(), array $secondaryactions = array()) {
static $initialised = 0;
$this->attributes = array(
'id' => 'action-menu-'.$initialised,
'class' => 'moodle-actionmenu',
'data-enhance' => 'moodle-core-actionmenu'
);
$this->attributesprimary = array(
'id' => 'action-menu-'.$initialised.'-primary',
'class' => 'primary'
);
$this->attributessecondary = array(
'id' => 'action-menu-'.$initialised.'-secondary',
'class' => 'secondary',
'data-rel' => 'menu-content',
);
foreach ($primaryactions as $action) {
$this->add_primary_action($action);
}
foreach ($secondaryactions as $action) {
$this->add_secondary_action($action);
}
// We've initialised!
$initialised++;
}
/**
* Initialises JS required fore the action menu.
* The JS is only required once as it manages all action menu's on the page.
*
* @param moodle_page $page
*/
public function initialise_js(moodle_page $page) {
static $initialised = false;
if (!$initialised) {
$page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
$initialised = true;
}
}
/**
* Adds a primary action to the action menu.
*
* @param action_link|string $action
*/
public function add_primary_action($action) {
$this->primaryactions[] = $action;
}
/**
* Adds a secondary action to the action menu.
*
* @param action_link|string $action
*/
public function add_secondary_action($action) {
$this->secondaryactions[] = $action;
}
/**
* Returns the primary actions ready to be rendered.
*
* @param core_renderer $output The renderer to use for getting icons.
* @return array
*/
public function get_primary_actions(core_renderer $output = null) {
global $OUTPUT;
if ($output === null) {
$output = $OUTPUT;
}
$actions = array();
foreach ($this->primaryactions as $action) {
if ($this->displayprimaryiconsonly && $action instanceof action_link && $action->icon) {
$action->text = null;
}
$actions[] = $action;
}
$attributes = array(
'type' => 'image',
'src' => $output->pix_url('t/contextmenu', 'moodle'),
'alt' => get_string('actions', 'moodle'),
'class' => 'toggle-display'
);
$actions[] = html_writer::empty_tag('input', $attributes);
return $actions;
}
/**
* Returns the secondary actions ready to be rendered.
* @return array
*/
public function get_secondary_actions() {
return $this->secondaryactions;
}
/**
* Sets the selector that should be used to find the owning node of this menu.
* @param string $selector A CSS/YUI selector to identify the owner of the menu.
*/
public function set_owner_selector($selector) {
$this->attributes['data-owner'] = $selector;
}
}
+43 -12
View File
@@ -1029,26 +1029,53 @@ class core_renderer extends renderer_base {
* Output the row of editing icons for a block, as defined by the controls array.
*
* @param array $controls an array like {@link block_contents::$controls}.
* @param string $blockid The ID given to the block.
* @return string HTML fragment.
*/
public function block_controls($actions) {
$this->page->requires->yui_module('moodle-core-actionmenu', 'M.core.actionmenu.init');
$output = html_writer::start_tag('span', array('class' => 'actionmenu'));
$output .= html_writer::start_tag('span', array('class' => 'statuses'));
public function block_controls($actions, $blockid = null) {
if (empty($actions)) {
return '';
}
$menu = new action_menu();
if ($blockid !== null) {
$menu->set_owner_selector('#'.$blockid);
}
foreach ($actions as $action) {
if ($action->has_class('status')) {
$menu->add_primary_action($action);
} else {
$menu->add_secondary_action($action);
}
}
$menu->attributes['class'] .= ' block-control-actions commands';
return $this->render($menu);
}
/**
* Renders an action menu component.
*
* @param action_menu $menu
* @return string HTML
*/
public function render_action_menu(action_menu $menu) {
$menu->initialise_js($this->page);
$output = html_writer::start_tag('div', $menu->attributes);
$output .= html_writer::start_tag('span', $menu->attributesprimary);
foreach ($menu->get_primary_actions($this) as $action) {
if ($action instanceof renderable) {
$output .= $this->render($action);
} else {
$output .= $action;
}
}
$output .= html_writer::end_tag('span');
$output .= html_writer::start_tag('span', array('class' => 'actions'));
foreach ($actions as $action) {
if (!$action->has_class('status')) {
$output .= $this->render($action);
}
$output .= html_writer::start_tag('div', $menu->attributessecondary);
foreach ($menu->get_secondary_actions() as $action) {
$output .= $this->render($action);
}
$output .= html_writer::end_tag('span');
$output .= html_writer::end_tag('span');
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('div');
return $output;
}
@@ -1138,7 +1165,11 @@ class core_renderer extends renderer_base {
$title = html_writer::tag('h2', $bc->title, $attributes);
}
$controlshtml = $this->block_controls($bc->controls);
$blockid = null;
if (isset($bc->attributes['id'])) {
$blockid = $bc->attributes['id'];
}
$controlshtml = $this->block_controls($bc->controls, $blockid);
$output = '';
if ($title || $controlshtml) {
@@ -6,16 +6,16 @@ YUI.add('moodle-core-actionmenu', function (Y, NAME) {
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
var BODY = Y.one(Y.config.doc.body),
CSS = {
MENUSHOWN : 'action-menu-shown'
},
SELECTOR = {
MENU : '[data-enhance=moodle-core-actionmenu]',
MENUCONTENT : '[data-rel=menu-content]',
TOGGLE : '.toggle-display'
},
ACTIONMENU;
/**
* Action menu support.
@@ -27,109 +27,185 @@ RESOURCES = {
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
ACTIONMENU.prototype = {
Y.extend(ACTIONMENU, Y.Base, {
/**
* The dialogue used for all action menu displays.
* @property type
* @type M.core.dialogue
* @protected
*/
dialogue : null,
/**
* An array of events attached during the display of the dialogue.
* @property events
* @type Object
* @protected
*/
events : [],
/**
* The node that owns the currently displayed menu.
*/
owner : null,
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
Y.log('Initialising action menu manager', 'note', ACTIONMENU.NAME);
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT);
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
this.setData('menucontent', menucontent);
}
});
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
/**
* Hides the menu if it is visible.
* @method hideMenu
*/
hideMenu : function() {
if (this.dialogue) {
this.dialogue.hide();
}
e.preventDefault();
}
for (var i in this.events) {
if (this.events[i].detach) {
this.events[i].detach();
}
}
this.events = [];
},
}, {
/**
* Toggles the display of the menu.
* @method toggleMenu
* @param {EventFacade} e
*/
toggleMenu : function(e) {
// Stop all immediate propogation of the events we attach later will be
// triggered and the dialogue immediately hidden again.
e.halt(true);
this.showMenu(e.target.ancestor(SELECTOR.MENU), align = this.calculateAlign(e.target));
this.events.push(BODY.on('key', this.hideMenu, 'esc', this));
},
/**
* Calculates the alignment point for the menu.
* @param {Node} node
* @returns {{node: *, points: Array}}
*/
calculateAlign : function(node) {
var points = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
if (BODY.hasClass('dir-rtl')) {
points = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
}
return {node: node, points : points};
},
/**
* Displays the menu with the given content and alignment.
* @param {Node} bodyContent
* @param Array align
* @returns {M.core.dialogue|dialogue}
*/
showMenu : function(menu, align) {
Y.log('Displaying action menu', 'note', ACTIONMENU.NAME);
var bodyContent = menu.getData('menucontent'),
ownerSelector = menu.getData('owner');
this.owner = (ownerSelector) ? menu.ancestor(ownerSelector) : null;
if (!this.dialogue) {
this.dialogue = new M.core.dialogue({
visible : false,
bodyContent : bodyContent,
align : align,
lightbox : false,
closeButton : false,
additionalBaseClass : 'action-menu-dialogue',
width : 'auto',
constrain : BODY,
hideOn : [
{
eventName: 'clickoutside'
}
]
});
this.dialogue.after('visibleChange', this.flagOwner, this);
this.flagOwner({attrName: 'visible', newVal: true});
} else {
if (this.dialogue.get('visible')) {
this.dialogue.hide();
}
this.dialogue.set('bodyContent', bodyContent);
this.dialogue.set('align', align);
this.dialogue.show();
}
return this.dialogue;
},
/**
* Flags the owner appropriately when the visibility of the dialogue changes.
* @param {EventFacade} e
*/
flagOwner : function(e) {
if (this.owner && e.attrName === 'visible') {
if (e.newVal === true) {
this.owner.addClass(CSS.MENUSHOWN);
} else {
this.owner.removeClass(CSS.MENUSHOWN);
this.owner = null;
}
}
}
};
Y.extend(ACTIONMENU, Y.Base, ACTIONMENU.prototype, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
ATTRS : {
/**
* A selector to use to get the owner of the menu if there is one.
* @attribute owner
* @type String
*/
owner : {
value : null
}
}
});
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
* @namespace M.core
* @class actionmenu
* @static
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
* @static
* @param {Object} params
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
}, '@VERSION@', {"requires": ["base", "node", "overlay", "event"]});
}, '@VERSION@', {"requires": ["base", "event", "moodle-core-notification"]});
@@ -1 +1 @@
YUI.add("moodle-core-actionmenu",function(e,t){CSS={ACTIONS:".actions"},RESOURCES={MENUICON:{pix:"t/contextmenu",component:"moodle"}};var n=function(){n.superclass.constructor.apply(this,arguments)};e.extend(n,e.Base,{initializer:function(){var t=[e.WidgetPositionAlign.TR,e.WidgetPositionAlign.BR],n=[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL];e.all(CSS.ACTIONS).each(function(){var r=e.Node.create("<img/>"),i=M.util.image_url(RESOURCES.MENUICON.pix,RESOURCES.MENUICON.component),s=e.Node.create("<a/>"),o=this.get("parentNode"),u=t,a=0;if(!this.hasChildNodes())return this;s.setAttribute("href","#"),s.addClass("actionmenu"),r.setAttribute("src",i),s.appendChild(r),o.insertBefore(s,this),a=o.getX()+parseInt(o.getComputedStyle("width"),10),a<this.get("winWidth")/2&&(u=n);var f=new e.Overlay({bodyContent:this,visible:!1,align:{node:r,points:u},zIndex:10});return f.render(),f.get("boundingBox").on("focusoutside",f.hide,f),s.setData("actionmenu-menu",f),this.all("a").each(function(){this.setData("actionmenu-link",s)}),this}),e.one("body").delegate("click",this.toggleMenu,".actionmenu"),e.one("body").on("key",this.hideAllMenus,"esc",this)},hideAllMenus:function(){e.all(".actionmenu").each(function(){var e=this.getData("actionmenu-menu");e&&e.hide()})},toggleMenu:function(e){var t=this.getData("actionmenu-menu");typeof t!="undefined"&&(t.get("visible")?t.hide():(M.core.actionmenu.instance.hideAllMenus(),t.show())),e.preventDefault()}},{NAME:"moodle-core-actionmenu",ATTRS:{}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new n(e)}},"@VERSION@",{requires:["base","node","overlay","event"]});
YUI.add("moodle-core-actionmenu",function(e,t){var n=e.one(e.config.doc.body),r={MENUSHOWN:"action-menu-shown"},i={MENU:"[data-enhance=moodle-core-actionmenu]",MENUCONTENT:"[data-rel=menu-content]",TOGGLE:".toggle-display"},s;s=function(){s.superclass.constructor.apply(this,arguments)},s.prototype={dialogue:null,events:[],owner:null,initializer:function(){e.all(i.MENU).each(function(){var e=this.one(i.MENUCONTENT);e.hasChildNodes()&&(this.setAttribute("data-enhanced","1"),this.setData("menucontent",e))}),n.delegate("click",this.toggleMenu,i.MENU+" "+i.TOGGLE,this),n.delegate("key",this.toggleMenu,"enter,space",i.MENU+" "+i.TOGGLE,this)},hideMenu:function(){this.dialogue&&this.dialogue.hide();for(var e in this.events)this.events[e].detach&&this.events[e].detach();this.events=[]},toggleMenu:function(e){e.halt(!0),this.showMenu(e.target.ancestor(i.MENU),align=this.calculateAlign(e.target)),this.events.push(n.on("key",this.hideMenu,"esc",this))},calculateAlign:function(t){var r=[e.WidgetPositionAlign.TR,e.WidgetPositionAlign.BR];return n.hasClass("dir-rtl")&&(r=[e.WidgetPositionAlign.TL,e.WidgetPositionAlign.BL]),{node:t,points:r}},showMenu:function(e,t){var r=e.getData("menucontent"),i=e.getData("owner");return this.owner=i?e.ancestor(i):null,this.dialogue?(this.dialogue.get("visible")&&this.dialogue.hide(),this.dialogue.set("bodyContent",r),this.dialogue.set("align",t),this.dialogue.show()):(this.dialogue=new M.core.dialogue({visible:!1,bodyContent:r,align:t,lightbox:!1,closeButton:!1,additionalBaseClass:"action-menu-dialogue",width:"auto",constrain:n,hideOn:[{eventName:"clickoutside"}]}),this.dialogue.after("visibleChange",this.flagOwner,this),this.flagOwner({attrName:"visible",newVal:!0})),this.dialogue},flagOwner:function(e){this.owner&&e.attrName==="visible"&&(e.newVal===!0?this.owner.addClass(r.MENUSHOWN):(this.owner.removeClass(r.MENUSHOWN),this.owner=null))}},e.extend(s,e.Base,s.prototype,{NAME:"moodle-core-actionmenu",ATTRS:{owner:{value:null}}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new s(e)}},"@VERSION@",{requires:["base","event","moodle-core-notification"]});
+159 -85
View File
@@ -6,16 +6,16 @@ YUI.add('moodle-core-actionmenu', function (Y, NAME) {
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
var BODY = Y.one(Y.config.doc.body),
CSS = {
MENUSHOWN : 'action-menu-shown'
},
SELECTOR = {
MENU : '[data-enhance=moodle-core-actionmenu]',
MENUCONTENT : '[data-rel=menu-content]',
TOGGLE : '.toggle-display'
},
ACTIONMENU;
/**
* Action menu support.
@@ -27,109 +27,183 @@ RESOURCES = {
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
ACTIONMENU.prototype = {
Y.extend(ACTIONMENU, Y.Base, {
/**
* The dialogue used for all action menu displays.
* @property type
* @type M.core.dialogue
* @protected
*/
dialogue : null,
/**
* An array of events attached during the display of the dialogue.
* @property events
* @type Object
* @protected
*/
events : [],
/**
* The node that owns the currently displayed menu.
*/
owner : null,
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT);
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
this.setData('menucontent', menucontent);
}
});
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
/**
* Hides the menu if it is visible.
* @method hideMenu
*/
hideMenu : function() {
if (this.dialogue) {
this.dialogue.hide();
}
e.preventDefault();
}
for (var i in this.events) {
if (this.events[i].detach) {
this.events[i].detach();
}
}
this.events = [];
},
}, {
/**
* Toggles the display of the menu.
* @method toggleMenu
* @param {EventFacade} e
*/
toggleMenu : function(e) {
// Stop all immediate propogation of the events we attach later will be
// triggered and the dialogue immediately hidden again.
e.halt(true);
this.showMenu(e.target.ancestor(SELECTOR.MENU), align = this.calculateAlign(e.target));
this.events.push(BODY.on('key', this.hideMenu, 'esc', this));
},
/**
* Calculates the alignment point for the menu.
* @param {Node} node
* @returns {{node: *, points: Array}}
*/
calculateAlign : function(node) {
var points = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
if (BODY.hasClass('dir-rtl')) {
points = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
}
return {node: node, points : points};
},
/**
* Displays the menu with the given content and alignment.
* @param {Node} bodyContent
* @param Array align
* @returns {M.core.dialogue|dialogue}
*/
showMenu : function(menu, align) {
var bodyContent = menu.getData('menucontent'),
ownerSelector = menu.getData('owner');
this.owner = (ownerSelector) ? menu.ancestor(ownerSelector) : null;
if (!this.dialogue) {
this.dialogue = new M.core.dialogue({
visible : false,
bodyContent : bodyContent,
align : align,
lightbox : false,
closeButton : false,
additionalBaseClass : 'action-menu-dialogue',
width : 'auto',
constrain : BODY,
hideOn : [
{
eventName: 'clickoutside'
}
]
});
this.dialogue.after('visibleChange', this.flagOwner, this);
this.flagOwner({attrName: 'visible', newVal: true});
} else {
if (this.dialogue.get('visible')) {
this.dialogue.hide();
}
this.dialogue.set('bodyContent', bodyContent);
this.dialogue.set('align', align);
this.dialogue.show();
}
return this.dialogue;
},
/**
* Flags the owner appropriately when the visibility of the dialogue changes.
* @param {EventFacade} e
*/
flagOwner : function(e) {
if (this.owner && e.attrName === 'visible') {
if (e.newVal === true) {
this.owner.addClass(CSS.MENUSHOWN);
} else {
this.owner.removeClass(CSS.MENUSHOWN);
this.owner = null;
}
}
}
};
Y.extend(ACTIONMENU, Y.Base, ACTIONMENU.prototype, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
ATTRS : {
/**
* A selector to use to get the owner of the menu if there is one.
* @attribute owner
* @type String
*/
owner : {
value : null
}
}
});
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
* @namespace M.core
* @class actionmenu
* @static
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
* @static
* @param {Object} params
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
};
}, '@VERSION@', {"requires": ["base", "node", "overlay", "event"]});
}, '@VERSION@', {"requires": ["base", "event", "moodle-core-notification"]});
@@ -57,6 +57,11 @@ DIALOGUE = function(config) {
.append(Y.Node.create('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
.append(Y.Node.create('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
Y.one(document.body).append(config.notificationBase);
if (config.additionalBaseClass) {
config.notificationBase.addClass(config.additionalBaseClass);
}
config.srcNode = '#'+id;
config.width = config.width || '400px';
config.visible = config.visible || false;
@@ -64,6 +69,10 @@ DIALOGUE = function(config) {
config.centered = false;
config.COUNT = COUNT;
if (config.width === 'auto') {
delete config.width;
}
// lightbox param to keep the stable versions API.
if (config.lightbox !== false) {
config.modal = true;
@@ -1 +1 @@
YUI.add("moodle-core-notification-dialogue",function(e,t){var n,r,i,s,o,u,a,f;n="moodle-dialogue",r="notificationBase",i=0,s="yesLabel",o="noLabel",u="title",a="question",f={BASE:"moodle-dialogue-base",WRAP:"moodle-dialogue-wrap",HEADER:"moodle-dialogue-hd",BODY:"moodle-dialogue-bd",CONTENT:"moodle-dialogue-content",FOOTER:"moodle-dialogue-ft",HIDDEN:"hidden",LIGHTBOX:"moodle-dialogue-lightbox"},M.core=M.core||{};var l="Moodle dialogue",c;c=function(t){i++;var n="moodle-dialogue-"+i;t.notificationBase=e.Node.create('<div class="'+f.BASE+'">').append(e.Node.create('<div id="'+n+'" role="dialog" aria-labelledby="'+n+'-header-text" class="'+f.WRAP+'"></div>').append(e.Node.create('<div class="'+f.HEADER+' yui3-widget-hd"></div>')).append(e.Node.create('<div class="'+f.BODY+' yui3-widget-bd"></div>')).append(e.Node.create('<div class="'+f.FOOTER+' yui3-widget-ft"></div>'))),e.one(document.body).append(t.notificationBase),t.srcNode="#"+n,t.width=t.width||"400px",t.visible=t.visible||!1,t.center=t.centered||!0,t.centered=!1,t.COUNT=i,t.lightbox!==!1&&(t.modal=!0),delete t.lightbox,t.closeButton===!1?t.buttons=null:t.buttons=[{section:e.WidgetStdMod.HEADER,classNames:"closebutton",action:function(){this.hide()}}],c.superclass.constructor.apply(this,[t]),t.closeButton!==!1&&this.get("buttons").header[0].setAttribute("title",this.get("closeButtonTitle"))},e.extend(c,e.Panel,{initializer:function(){this.after("visibleChange",this.visibilityChanged,this),this.render(),this.show(),this.set("COUNT",i),this.get("boundingBox").setStyle("zIndex",null)},visibilityChanged:function(t){var n;t.attrName==="visible"&&(this.get("maskNode").addClass(f.LIGHTBOX),this.get("center")&&!t.prevVal&&t.newVal&&this.centerDialogue(),this.get("draggable")&&(n="#"+this.get("id")+" ."+f.HEADER,this.plug(e.Plugin.Drag,{handles:[n]}),e.one(n).setStyle("cursor","move")))},centerDialogue:function(){var t=this.get("boundingBox"),r=t.hasClass(n+"-hidden"),i,s;r&&t.setStyle("top","-1000px").removeClass(n+"-hidden"),i=Math.max(Math.round((t.get("winWidth")-t.get("offsetWidth"))/2),15),s=Math.max(Math.round((t.get("winHeight")-t.get("offsetHeight"))/2),15)+e.one(window).get("scrollTop"),r&&t.addClass(n+"-hidden"),t.setStyle("left",i).setStyle("top",s)}},{NAME:l,CSS_PREFIX:n,ATTRS:{notificationBase:{},lightbox:{validator:e.Lang.isBoolean,value:!0},closeButton:{validator:e.Lang.isBoolean,value:!0},closeButtonTitle:{validator:e.Lang.isString,value:"Close"},center:{validator:e.Lang.isBoolean,value:!0},draggable:{validator:e.Lang.isBoolean,value:!1},COUNT:{value:0}}}),M.core.dialogue=c},"@VERSION@",{requires:["base","node","panel","event-key","dd-plugin"]});
YUI.add("moodle-core-notification-dialogue",function(e,t){var n,r,i,s,o,u,a,f;n="moodle-dialogue",r="notificationBase",i=0,s="yesLabel",o="noLabel",u="title",a="question",f={BASE:"moodle-dialogue-base",WRAP:"moodle-dialogue-wrap",HEADER:"moodle-dialogue-hd",BODY:"moodle-dialogue-bd",CONTENT:"moodle-dialogue-content",FOOTER:"moodle-dialogue-ft",HIDDEN:"hidden",LIGHTBOX:"moodle-dialogue-lightbox"},M.core=M.core||{};var l="Moodle dialogue",c;c=function(t){i++;var n="moodle-dialogue-"+i;t.notificationBase=e.Node.create('<div class="'+f.BASE+'">').append(e.Node.create('<div id="'+n+'" role="dialog" aria-labelledby="'+n+'-header-text" class="'+f.WRAP+'"></div>').append(e.Node.create('<div class="'+f.HEADER+' yui3-widget-hd"></div>')).append(e.Node.create('<div class="'+f.BODY+' yui3-widget-bd"></div>')).append(e.Node.create('<div class="'+f.FOOTER+' yui3-widget-ft"></div>'))),e.one(document.body).append(t.notificationBase),t.additionalBaseClass&&t.notificationBase.addClass(t.additionalBaseClass),t.srcNode="#"+n,t.width=t.width||"400px",t.visible=t.visible||!1,t.center=t.centered||!0,t.centered=!1,t.COUNT=i,t.width==="auto"&&delete t.width,t.lightbox!==!1&&(t.modal=!0),delete t.lightbox,t.closeButton===!1?t.buttons=null:t.buttons=[{section:e.WidgetStdMod.HEADER,classNames:"closebutton",action:function(){this.hide()}}],c.superclass.constructor.apply(this,[t]),t.closeButton!==!1&&this.get("buttons").header[0].setAttribute("title",this.get("closeButtonTitle"))},e.extend(c,e.Panel,{initializer:function(){this.after("visibleChange",this.visibilityChanged,this),this.render(),this.show(),this.set("COUNT",i),this.get("boundingBox").setStyle("zIndex",null)},visibilityChanged:function(t){var n;t.attrName==="visible"&&(this.get("maskNode").addClass(f.LIGHTBOX),this.get("center")&&!t.prevVal&&t.newVal&&this.centerDialogue(),this.get("draggable")&&(n="#"+this.get("id")+" ."+f.HEADER,this.plug(e.Plugin.Drag,{handles:[n]}),e.one(n).setStyle("cursor","move")))},centerDialogue:function(){var t=this.get("boundingBox"),r=t.hasClass(n+"-hidden"),i,s;r&&t.setStyle("top","-1000px").removeClass(n+"-hidden"),i=Math.max(Math.round((t.get("winWidth")-t.get("offsetWidth"))/2),15),s=Math.max(Math.round((t.get("winHeight")-t.get("offsetHeight"))/2),15)+e.one(window).get("scrollTop"),r&&t.addClass(n+"-hidden"),t.setStyle("left",i).setStyle("top",s)}},{NAME:l,CSS_PREFIX:n,ATTRS:{notificationBase:{},lightbox:{validator:e.Lang.isBoolean,value:!0},closeButton:{validator:e.Lang.isBoolean,value:!0},closeButtonTitle:{validator:e.Lang.isString,value:"Close"},center:{validator:e.Lang.isBoolean,value:!0},draggable:{validator:e.Lang.isBoolean,value:!1},COUNT:{value:0}}}),M.core.dialogue=c},"@VERSION@",{requires:["base","node","panel","event-key","dd-plugin"]});
@@ -57,6 +57,11 @@ DIALOGUE = function(config) {
.append(Y.Node.create('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
.append(Y.Node.create('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
Y.one(document.body).append(config.notificationBase);
if (config.additionalBaseClass) {
config.notificationBase.addClass(config.additionalBaseClass);
}
config.srcNode = '#'+id;
config.width = config.width || '400px';
config.visible = config.visible || false;
@@ -64,6 +69,10 @@ DIALOGUE = function(config) {
config.centered = false;
config.COUNT = COUNT;
if (config.width === 'auto') {
delete config.width;
}
// lightbox param to keep the stable versions API.
if (config.lightbox !== false) {
config.modal = true;
+160 -84
View File
@@ -4,16 +4,16 @@
* @module moodle-core-actionmenu
*/
CSS = {
ACTIONS : '.actions'
};
RESOURCES = {
MENUICON : {
pix : 't/contextmenu',
component : 'moodle'
}
};
var BODY = Y.one(Y.config.doc.body),
CSS = {
MENUSHOWN : 'action-menu-shown'
},
SELECTOR = {
MENU : '[data-enhance=moodle-core-actionmenu]',
MENUCONTENT : '[data-rel=menu-content]',
TOGGLE : '.toggle-display'
},
ACTIONMENU;
/**
* Action menu support.
@@ -25,105 +25,181 @@ RESOURCES = {
* @constructor
* @extends Y.Base
*/
var ACTIONMENU = function() {
ACTIONMENU = function() {
ACTIONMENU.superclass.constructor.apply(this, arguments);
};
ACTIONMENU.prototype = {
Y.extend(ACTIONMENU, Y.Base, {
/**
* The dialogue used for all action menu displays.
* @property type
* @type M.core.dialogue
* @protected
*/
dialogue : null,
/**
* An array of events attached during the display of the dialogue.
* @property events
* @type Object
* @protected
*/
events : [],
/**
* The node that owns the currently displayed menu.
*/
owner : null,
/**
* Called during the initialisation process of the object.
* @method initializer
*/
initializer : function() {
var rightalignpoints = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
var leftalignpoints = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
Y.all(CSS.ACTIONS).each(function() {
// Prepend menu icon before the list.
var imgnode = Y.Node.create('<img/>');
var imgsrc = M.util.image_url(RESOURCES.MENUICON.pix, RESOURCES.MENUICON.component);
var linknode = Y.Node.create('<a/>');
var parentnode = this.get('parentNode');
var alignpoints = rightalignpoints;
var menux = 0;
if (!this.hasChildNodes()) {
return this;
}
linknode.setAttribute('href', '#');
linknode.addClass('actionmenu');
imgnode.setAttribute('src', imgsrc);
linknode.appendChild(imgnode);
parentnode.insertBefore(linknode, this);
menux = (parentnode.getX() + parseInt(parentnode.getComputedStyle('width'), 10));
if (menux < (this.get('winWidth') / 2)) {
alignpoints = leftalignpoints;
}
var overlay = new Y.Overlay({
bodyContent : this,
visible: false,
align: {node: imgnode, points: alignpoints},
zIndex: 10
});
overlay.render();
overlay.get('boundingBox').on('focusoutside', overlay.hide, overlay);
// Create bi-directional links between the menu and the open button.
linknode.setData('actionmenu-menu', overlay);
this.all('a').each(function () {
this.setData('actionmenu-link', linknode);
});
return this;
});
Y.one('body').delegate('click', this.toggleMenu, '.actionmenu');
Y.one('body').on('key', this.hideAllMenus, 'esc', this);
},
hideAllMenus : function() {
// Hide all actionmenus.
Y.all('.actionmenu').each(function() {
var overlay = this.getData('actionmenu-menu');
if (overlay) {
            overlay.hide();
Y.log('Initialising action menu manager', 'note', ACTIONMENU.NAME);
Y.all(SELECTOR.MENU).each(function() {
var menucontent = this.one(SELECTOR.MENUCONTENT);
if (menucontent.hasChildNodes()) {
this.setAttribute('data-enhanced', '1');
this.setData('menucontent', menucontent);
}
});
BODY.delegate('click', this.toggleMenu, SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
BODY.delegate('key', this.toggleMenu, 'enter,space', SELECTOR.MENU + ' ' + SELECTOR.TOGGLE, this);
},
toggleMenu : function(e) {
var overlay = this.getData('actionmenu-menu');
if (typeof overlay !== "undefined") {
if (overlay.get('visible')) {
            overlay.hide();
        } else {
M.core.actionmenu.instance.hideAllMenus();
            overlay.show();
        }
/**
* Hides the menu if it is visible.
* @method hideMenu
*/
hideMenu : function() {
if (this.dialogue) {
this.dialogue.hide();
}
e.preventDefault();
}
for (var i in this.events) {
if (this.events[i].detach) {
this.events[i].detach();
}
}
this.events = [];
},
}, {
/**
* Toggles the display of the menu.
* @method toggleMenu
* @param {EventFacade} e
*/
toggleMenu : function(e) {
// Stop all immediate propogation of the events we attach later will be
// triggered and the dialogue immediately hidden again.
e.halt(true);
this.showMenu(e.target.ancestor(SELECTOR.MENU), align = this.calculateAlign(e.target));
this.events.push(BODY.on('key', this.hideMenu, 'esc', this));
},
/**
* Calculates the alignment point for the menu.
* @param {Node} node
* @returns {{node: *, points: Array}}
*/
calculateAlign : function(node) {
var points = [Y.WidgetPositionAlign.TR, Y.WidgetPositionAlign.BR];
if (BODY.hasClass('dir-rtl')) {
points = [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL];
}
return {node: node, points : points};
},
/**
* Displays the menu with the given content and alignment.
* @param {Node} menu
* @param Array align
* @returns {M.core.dialogue|dialogue}
*/
showMenu : function(menu, align) {
Y.log('Displaying action menu', 'note', ACTIONMENU.NAME);
var bodyContent = menu.getData('menucontent'),
ownerSelector = menu.getData('owner');
this.owner = (ownerSelector) ? menu.ancestor(ownerSelector) : null;
if (!this.dialogue) {
this.dialogue = new M.core.dialogue({
visible : false,
bodyContent : bodyContent,
align : align,
lightbox : false,
closeButton : false,
additionalBaseClass : 'action-menu-dialogue',
width : 'auto',
constrain : BODY,
hideOn : [
{
eventName: 'clickoutside'
}
]
});
this.dialogue.after('visibleChange', this.flagOwner, this);
this.flagOwner({attrName: 'visible', newVal: true});
} else {
if (this.dialogue.get('visible')) {
this.dialogue.hide();
}
this.dialogue.set('bodyContent', bodyContent);
this.dialogue.set('align', align);
this.dialogue.show();
}
return this.dialogue;
},
/**
* Flags the owner appropriately when the visibility of the dialogue changes.
* @param {EventFacade} e
*/
flagOwner : function(e) {
if (this.owner && e.attrName === 'visible') {
if (e.newVal === true) {
this.owner.addClass(CSS.MENUSHOWN);
} else {
this.owner.removeClass(CSS.MENUSHOWN);
this.owner = null;
}
}
}
};
Y.extend(ACTIONMENU, Y.Base, ACTIONMENU.prototype, {
NAME : 'moodle-core-actionmenu',
ATTRS : { }
ATTRS : {
/**
* A selector to use to get the owner of the menu if there is one.
* @attribute owner
* @type String
*/
owner : {
value : null
}
}
});
/**
* Core namespace.
* @static
* @class core
*/
M.core = M.core || {};
/**
* Actionmenu namespace.
* @namespace M.core
* @class actionmenu
* @static
*/
M.core.actionmenu = M.core.actionmenu || {};
/**
* Init function - will only ever create one instance of the actionmenu class.
* @method init
* @static
* @param {Object} params
*/
M.core.actionmenu.init = M.core.actionmenu.init || function(params) {
M.core.actionmenu.instance = M.core.actionmenu.instance || new ACTIONMENU(params);
+2 -3
View File
@@ -2,9 +2,8 @@
"moodle-core-actionmenu": {
"requires": [
"base",
"node",
"overlay",
"event"
"event",
"moodle-core-notification"
]
}
}
+9
View File
@@ -26,6 +26,11 @@ DIALOGUE = function(config) {
.append(Y.Node.create('<div class="'+CSS.BODY+' yui3-widget-bd"></div>'))
.append(Y.Node.create('<div class="'+CSS.FOOTER+' yui3-widget-ft"></div>')));
Y.one(document.body).append(config.notificationBase);
if (config.additionalBaseClass) {
config.notificationBase.addClass(config.additionalBaseClass);
}
config.srcNode = '#'+id;
config.width = config.width || '400px';
config.visible = config.visible || false;
@@ -33,6 +38,10 @@ DIALOGUE = function(config) {
config.centered = false;
config.COUNT = COUNT;
if (config.width === 'auto') {
delete config.width;
}
// lightbox param to keep the stable versions API.
if (config.lightbox !== false) {
config.modal = true;
+28
View File
@@ -1372,3 +1372,31 @@ div.badge .expireimage { width: 100px; height: 100px; left: 20px; top: 0px; }
}
.path-mod-lesson .centerpadded { padding: 5px; text-align: center; }
.moodle-actionmenu .iconsmall {vertical-align: top;margin:0 3px;}
.jsenabled .block .editing_move {display: none;}
.jsenabled .moodle-actionmenu [data-rel=menu-content] {display: none;margin-top: 4px;}
.moodle-actionmenu .toggle-display {display: none;padding-right: 4px;padding-left: 4px;}
.jsenabled .moodle-actionmenu .toggle-display {display: inline;opacity: 0.5;filter: alpha(opacity=50);}
.moodle-actionmenu[data-enhanced] .toggle-display {opacity: 1;filter: alpha(opacity=100);}
.statuses .actionlinktext {display: none;}
.action-menu-dialogue {min-width:300px;}
.action-menu-dialogue .moodle-dialogue-wrap {margin-top: 4px;margin-left: 4px;-webkit-border-radius: 4px;-moz-border-radius: 4px;border-radius: 4px;}
.action-menu-dialogue .moodle-dialogue .moodle-dialogue-bd {padding: 0;}
.action-menu-dialogue .moodle-dialogue-hd,
.action-menu-dialogue .moodle-dialogue-ft {display: none;}
.action-menu-dialogue a {display: block;padding: 2px 1em;color: #333333;}
.action-menu-dialogue a.hidden {display: none;}
.action-menu-dialogue .iconsmall {margin-right:8px;}
.action-menu-dialogue a:hover,
.action-menu-dialogue a:focus {color: #ffffff;background-color: #0088cc;}
.action-menu-dialogue a:first-child {-webkit-border-top-right-radius: 4px;border-top-right-radius: 4px;-webkit-border-top-left-radius: 4px;border-top-left-radius: 4px;-moz-border-radius-topright: 4px;-moz-border-radius-topleft: 4px;}
.action-menu-dialogue a:last-child {-webkit-border-bottom-right-radius: 4px;border-bottom-right-radius: 4px;-webkit-border-bottom-left-radius: 4px;border-bottom-left-radius: 4px;-moz-border-radius-bottomright: 4px;-moz-border-radius-bottomleft: 4px;}
.block .moodle-actionmenu {text-align: right;}
.dir-rtl .block moodle-actionmenu {text-align: left;}
.dir-rtl .block moodle-actionmenu {text-align: left;}
.dir-rtl .action-menu-dialogue a {padding:2px 0.5em 2px 1em;}
.dir-rtl .action-menu-dialogue .iconsmall {margin-right:0;margin-left:8px;}
.dir-rtl .moodle-actionmenu .toggle-display {padding-left:0;padding-right:4px;}
+1 -1
View File
@@ -17,7 +17,7 @@
.block_adminblock .content,
.block .footer {
display: block;
padding: 3px 15px;
padding: 3px 5px;
}
.block .header .block_action {
float: right;
+102 -46
View File
@@ -1911,65 +1911,121 @@ div.badge .expireimage {
margin: 0;
}
.jsenabled .actions {
visibility: hidden;
.jsenabled .block .editing_move {
display:none;
}
.jsenabled .moodle-actionmenu [data-rel=menu-content] {
display:none;
margin-top: 4px;
}
a.actionmenu {
.moodle-actionmenu .toggle-display {
padding-left: 4px;
padding-right: 4px;
/*
padding-right: 16px;
    background-image:url([[pix:t/expanded]]);
    background-position:100% 30%;
    background-repeat:no-repeat;
*/
display: none; /** Hidden by default, display none so that we don't take up space. **/
cursor:auto;
}
.jsenabled .moodle-actionmenu .toggle-display {
display: inline; /** JS is enabled, we'll be using it so return display to normal **/
.opacity(50);
}
.moodle-actionmenu[data-enhanced] .toggle-display {
.opacity(100);
}
.yui3-overlay .actions {
visibility: inherit;
background-color: @dropdownBackground;
border: 1px solid @dropdownBorder;
.action-menu-shown .moodle-actionmenu[data-enhanced] .toggle-display {
background-color: #FFF;
}
.moodle-actionmenu,
.moodle-actionmenu .secondary {
display: inline-block;
}
.jsenabled .moodle-actionmenu,
.jsenabled .moodle-actionmenu .secondary {
display: block;
padding: 4px;
.border-radius(4px);
}
.autocompletion,
a.status .iconsmall {
padding-left: 8px;
padding-right: 8px;
.moodle-actionmenu .actionlinktext {
display:none;
}
.statuses .actionlinktext {
display: none;
.jsenabled .moodle-actionmenu[data-enhanced] .actionlinktext {
display:inline;
}
.yui3-overlay .actions a {
display: block;
color: @dropdownLinkColor;
padding-left: 4px;
padding-right: 4px;
}
.yui3-overlay .actions a.hidden {
display: none;
}
.yui3-overlay .actions img {
vertical-align: middle;
}
.yui3-overlay .actions a:hover,
.yui3-overlay .actions a:focus
{
color: @dropdownLinkColorHover;
background-color: @dropdownLinkBackgroundHover;
}
.block span.actionmenu {
display: block;
.block .moodle-actionmenu {
text-align: right;
}
.dir-rtl .block span.actionmenu {
text-align: left;
.jsenabled #page .moodle-actionmenu .secondary {
display: none;
}
.action-menu-dialogue {
.moodle-dialogue-wrap {
margin-top:4px;
margin-left:4px;
.border-radius(4px);
}
.moodle-dialogue {
.moodle-dialogue-bd {
padding: 0;
}
}
.moodle-dialogue-hd,
.moodle-dialogue-ft {
display: none;
}
a {
display: block;
color: @dropdownLinkColor;
padding:2px 1em 2px 0.5em;
&:hover,
&:focus {
color: @dropdownLinkColorHover;
background-color: @dropdownLinkBackgroundHover;
}
&:first-child {
.border-top-radius(4px);
}
&:last-child {
.border-bottom-radius(4px);
}
}
a.hidden {
display: none;
}
img {
vertical-align: middle;
}
.iconsmall {
margin-right:8px;
}
}
.safari .action-menu-dialogue .moodle-dialogue {
/** Safari specific bug here **/
white-space: nowrap;
}
.dir-rtl {
.block .moodle-actionmenu {
text-align: left;
}
.action-menu-dialogue {
a {
padding:2px 0.5em 2px 1em;
}
.iconsmall {
margin-right:0;
margin-left:8px;
}
}
.moodle-actionmenu .toggle-display {
padding-left:0;
padding-right:4px;
}
}
+2 -1
View File
@@ -95,7 +95,8 @@
.section .activity .contentafterlink p {
margin: .5em 0;
}
.editing .section .activity:hover {
.editing .section .activity:hover,
.editing .section .activity.action-menu-shown {
background-color: @grayLighter;
}
.course-content .current {
File diff suppressed because one or more lines are too long