diff --git a/static/js/jquery.shiftcheckbox.js b/static/js/jquery.shiftcheckbox.js new file mode 100644 index 000000000..f2d01210d --- /dev/null +++ b/static/js/jquery.shiftcheckbox.js @@ -0,0 +1,196 @@ +/* ShiftCheckbox jQuery plugin + * + * Copyright (C) 2011-2012 James Nylen + * + * Released under MIT license + * For details see: + * https://github.com/nylen/shiftcheckbox + * + * Requires jQuery v1.7 or higher. + */ + +(function($) { + var ns = '.shiftcheckbox'; + + $.fn.shiftcheckbox = function(opts) { + opts = $.extend({ + checkboxSelector : null, + selectAll : null, + onChange : null, + ignoreClick : null + }, opts); + + if (typeof opts.onChange != 'function') { + opts.onChange = function(checked) { }; + } + + $.fn.scb_changeChecked = function(opts, checked) { + this.prop('checked', checked); + opts.onChange.call(this, checked); + return this; + } + + var $containers, + $checkboxes, + $containersSelectAll, + $checkboxesSelectAll, + $otherSelectAll, + $containersAll, + $checkboxesAll; + + if (opts.selectAll) { + // We need to set up a "select all" control + $containersSelectAll = $(opts.selectAll); + if ($containersSelectAll && !$containersSelectAll.length) { + $containersSelectAll = false; + } + } + + if ($containersSelectAll) { + $checkboxesSelectAll = $containersSelectAll + .filter(':checkbox') + .add($containersSelectAll.find(':checkbox')); + + $containersSelectAll = $containersSelectAll.not(':checkbox'); + $otherSelectAll = $containersSelectAll.filter(function() { + return !$(this).find($checkboxesSelectAll).length; + }); + $containersSelectAll = $containersSelectAll.filter(function() { + return !!$(this).find($checkboxesSelectAll).length; + }).each(function() { + $(this).data('childCheckbox', $(this).find($checkboxesSelectAll)[0]); + }); + } + + if (opts.checkboxSelector) { + + // checkboxSelector means that the elements we need to attach handlers to + // ($containers) are not actually checkboxes but contain them instead + + $containersAll = this.filter(function() { + return !!$(this).find(opts.checkboxSelector).filter(':checkbox').length; + }).each(function() { + $(this).data('childCheckbox', $(this).find(opts.checkboxSelector).filter(':checkbox')[0]); + }).add($containersSelectAll); + + $checkboxesAll = $containersAll.map(function() { + return $(this).data('childCheckbox'); + }); + + } else { + + $checkboxesAll = this.filter(':checkbox'); + + } + + if ($checkboxesSelectAll && !$checkboxesSelectAll.length) { + $checkboxesSelectAll = false; + } else { + $checkboxesAll = $checkboxesAll.add($checkboxesSelectAll); + } + + if ($otherSelectAll && !$otherSelectAll.length) { + $otherSelectAll = false; + } + + if ($containersAll) { + $containers = $containersAll.not($containersSelectAll); + } + $checkboxes = $checkboxesAll.not($checkboxesSelectAll); + + if (!$checkboxes.length) { + return; + } + + var lastIndex = -1; + + var checkboxClicked = function(e) { + var checked = !!$(this).prop('checked'); + + var curIndex = $checkboxes.index(this); + if (curIndex < 0) { + if ($checkboxesSelectAll.filter(this).length) { + $checkboxesAll.scb_changeChecked(opts, checked); + } + return; + } + + if (e.shiftKey && lastIndex != -1) { + var di = (curIndex > lastIndex ? 1 : -1); + for (var i = lastIndex; i != curIndex; i += di) { + $checkboxes.eq(i).scb_changeChecked(opts, checked); + } + } + + if ($checkboxesSelectAll) { + if (checked && !$checkboxes.not(':checked').length) { + $checkboxesSelectAll.scb_changeChecked(opts, true); + } else if (!checked) { + $checkboxesSelectAll.scb_changeChecked(opts, false); + } + } + + lastIndex = curIndex; + }; + + if ($checkboxesSelectAll) { + $checkboxesSelectAll + .prop('checked', !$checkboxes.not(':checked').length) + .filter(function() { + return !$containersAll.find(this).length; + }).on('click' + ns, checkboxClicked); + } + + if ($otherSelectAll) { + $otherSelectAll.on('click' + ns, function() { + var checked; + if ($checkboxesSelectAll) { + checked = !!$checkboxesSelectAll.eq(0).prop('checked'); + } else { + checked = !!$checkboxes.eq(0).prop('checked'); + } + $checkboxesAll.scb_changeChecked(opts, !checked); + }); + } + + if (opts.checkboxSelector) { + $containersAll.on('click' + ns, function(e) { + if ($(e.target).closest(opts.ignoreClick).length) { + return; + } + var $checkbox = $($(this).data('childCheckbox')); + $checkbox.not(e.target).each(function() { + var checked = !$checkbox.prop('checked'); + $(this).scb_changeChecked(opts, checked); + }); + + $checkbox[0].focus(); + checkboxClicked.call($checkbox, e); + + // If the user clicked on a label inside the row that points to the + // current row's checkbox, cancel the event. + var $label = $(e.target).closest('label'); + var labelFor = $label.attr('for'); + if (labelFor && labelFor == $checkbox.attr('id')) { + if ($label.find($checkbox).length) { + // Special case: The label contains the checkbox. + if ($checkbox[0] != e.target) { + return false; + } + } else { + return false; + } + } + }).on('mousedown' + ns, function(e) { + if (e.shiftKey) { + // Prevent selecting text by Shift+click + return false; + } + }); + } else { + $checkboxes.on('click' + ns, checkboxClicked); + } + + return this; + }; +})(jQuery); diff --git a/templates/juser/group_list.html b/templates/juser/group_list.html index 092c75776..88d8dd32c 100644 --- a/templates/juser/group_list.html +++ b/templates/juser/group_list.html @@ -43,7 +43,7 @@ - + 组名 成员数目 @@ -55,7 +55,8 @@ {% for group in user_groups.object_list %} - + {{ group.name }} {{ group.id | members_count }} @@ -83,6 +84,10 @@ {% endblock %} +{% block self_head_css_js %} + {% load staticfiles %} + +{% endblock %} {% block self_footer_js %} {% endblock %} \ No newline at end of file diff --git a/templates/juser/user_list.html b/templates/juser/user_list.html index 9d6fa45cf..60bb38853 100644 --- a/templates/juser/user_list.html +++ b/templates/juser/user_list.html @@ -91,6 +91,10 @@ {% endblock %} {% block self_head_css_js %} + {% load staticfiles %} + +{% endblock %} +{% block self_footer_js %} {% endblock %} \ No newline at end of file