oauth edit
This commit is contained in:
@@ -128,6 +128,130 @@ app.group = (function(app){
|
||||
return {list, get, remove}
|
||||
})(app)
|
||||
|
||||
// Reusable UI widgets. app.ui.tagInput is a generic tag/token field with
|
||||
// autocomplete; app.ui.groupSelect is a tagInput preloaded with every LDAP
|
||||
// group, meant to be dropped in anywhere group selection is needed.
|
||||
app.ui = (function(app){
|
||||
|
||||
// All LDAP group CNs, fetched once and shared across every group selector.
|
||||
var _groupsPromise = null;
|
||||
function loadGroups(){
|
||||
if(!_groupsPromise){
|
||||
_groupsPromise = new Promise(function(resolve){
|
||||
app.group.list(function(error, data){
|
||||
if(error || !data || !data.results){ resolve([]); return; }
|
||||
resolve(data.results.map(function(g){ return g.cn || g; }).filter(Boolean).sort());
|
||||
});
|
||||
});
|
||||
}
|
||||
return _groupsPromise;
|
||||
}
|
||||
// Drop the cache (e.g. after a group is created) so the next selector refetches.
|
||||
function refreshGroups(){ _groupsPromise = null; return loadGroups(); }
|
||||
|
||||
// opts: { values, options, freeSolo, placeholder, name, separator }
|
||||
// Returns a handle: { get, set, add, clear, setOptions, element }.
|
||||
function tagInput(mount, opts){
|
||||
opts = opts || {};
|
||||
var separator = opts.separator != null ? opts.separator : '\n';
|
||||
var values = (opts.values || []).slice();
|
||||
var options = (opts.options || []).slice();
|
||||
var freeSolo = opts.freeSolo !== false; // default: allow custom entries
|
||||
|
||||
var $mount = $(mount).addClass('tag-input form-control shadow').empty();
|
||||
var $chips = $('<span class="tag-chips"></span>');
|
||||
var $input = $('<input type="text" class="tag-typeahead" autocomplete="off">')
|
||||
.attr('placeholder', opts.placeholder || 'Type to add…');
|
||||
var $menu = $('<div class="tag-menu"></div>').hide();
|
||||
var $hidden = $('<input type="hidden">').attr('name', opts.name || '');
|
||||
$mount.append($chips, $input, $menu, $hidden);
|
||||
|
||||
function syncHidden(){
|
||||
var joined = values.join(separator);
|
||||
$hidden.val(joined).attr('value', joined);
|
||||
$mount.trigger('change');
|
||||
}
|
||||
function renderChips(){
|
||||
$chips.empty();
|
||||
values.forEach(function(v){
|
||||
var $remove = $('<a class="tag-remove" href="#">×</a>').on('click', function(e){
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
values = values.filter(function(x){ return x !== v; });
|
||||
renderChips(); syncHidden();
|
||||
});
|
||||
$chips.append($('<span class="tag-chip badge"></span>').text(v).append(' ', $remove));
|
||||
});
|
||||
}
|
||||
function suggestions(){
|
||||
var q = ($input.val() || '').toLowerCase();
|
||||
return options.filter(function(o){
|
||||
return values.indexOf(o) === -1 && o.toLowerCase().indexOf(q) !== -1;
|
||||
}).slice(0, 10);
|
||||
}
|
||||
function showMenu(){
|
||||
var items = suggestions();
|
||||
if(!items.length){ return hideMenu(); }
|
||||
$menu.empty();
|
||||
items.forEach(function(o){
|
||||
$('<div class="tag-option"></div>').text(o).on('mousedown', function(e){
|
||||
e.preventDefault(); add(o);
|
||||
}).appendTo($menu);
|
||||
});
|
||||
$menu.show();
|
||||
}
|
||||
function hideMenu(){ $menu.hide(); }
|
||||
function add(val){
|
||||
val = (val || '').trim();
|
||||
if(!val){ return; }
|
||||
if(!freeSolo && options.indexOf(val) === -1){ return; } // reject invalid
|
||||
if(values.indexOf(val) === -1){ values.push(val); renderChips(); syncHidden(); }
|
||||
$input.val(''); hideMenu();
|
||||
}
|
||||
|
||||
$input.on('input focus', showMenu);
|
||||
$input.on('keydown', function(e){
|
||||
if(e.key === 'Enter' || e.key === ','){
|
||||
e.preventDefault();
|
||||
add(freeSolo ? $input.val() : (suggestions()[0] || ''));
|
||||
}else if(e.key === 'Backspace' && !$input.val() && values.length){
|
||||
values.pop(); renderChips(); syncHidden();
|
||||
}
|
||||
});
|
||||
$input.on('blur', function(){ setTimeout(hideMenu, 150); });
|
||||
$mount.on('click', function(e){
|
||||
if(e.target === $mount[0] || e.target === $chips[0]){ $input.focus(); }
|
||||
});
|
||||
|
||||
renderChips(); syncHidden();
|
||||
|
||||
return {
|
||||
get: function(){ return values.slice(); },
|
||||
set: function(v){ values = (v || []).slice(); renderChips(); syncHidden(); },
|
||||
add: add,
|
||||
clear: function(){ values = []; renderChips(); syncHidden(); },
|
||||
setOptions: function(o){ options = (o || []).slice(); },
|
||||
element: $mount,
|
||||
};
|
||||
}
|
||||
|
||||
// Universal group selector. Preloads all LDAP groups for autocomplete.
|
||||
function groupSelect(mount, opts){
|
||||
opts = opts || {};
|
||||
var handle = tagInput(mount, {
|
||||
name: opts.name || 'groups',
|
||||
values: opts.values || [],
|
||||
options: [],
|
||||
freeSolo: opts.freeSolo !== false,
|
||||
separator: opts.separator != null ? opts.separator : '\n',
|
||||
placeholder: opts.placeholder || 'Type a group name…',
|
||||
});
|
||||
loadGroups().then(function(groups){ handle.setOptions(groups); });
|
||||
return handle;
|
||||
}
|
||||
|
||||
return { tagInput: tagInput, groupSelect: groupSelect, loadGroups: loadGroups, refreshGroups: refreshGroups };
|
||||
})(app);
|
||||
|
||||
app.oauthClient = (function(app){
|
||||
function list(callack){
|
||||
return app.api.get('oauth/client/', function(error, data){
|
||||
|
||||
Reference in New Issue
Block a user