merge communities-allow-list and lint compliance

This commit is contained in:
ansuz
2020-02-25 09:48:41 -05:00
28 changed files with 2059 additions and 191 deletions

View File

@@ -218,14 +218,15 @@ define([
var titles = [];
var active = 0;
tabs.forEach(function (tab, i) {
if (!tab.content || !tab.title) { return; }
if (!(tab.content || tab.disabled) || !tab.title) { return; }
var content = h('div.alertify-tabs-content', tab.content);
var title = h('span.alertify-tabs-title', tab.title);
var title = h('span.alertify-tabs-title'+ (tab.disabled ? '.disabled' : ''), tab.title);
if (tab.icon) {
var icon = h('i', {class: tab.icon});
$(title).prepend(' ').prepend(icon);
}
$(title).click(function () {
if (tab.disabled) { return; }
var old = tabs[active];
if (old.onHide) { old.onHide(); }
titles.forEach(function (t) { $(t).removeClass('alertify-tabs-active'); });
@@ -239,7 +240,7 @@ define([
});
titles.push(title);
contents.push(content);
if (tab.active) { active = i; }
if (tab.active && !tab.disabled) { active = i; }
});
if (contents.length) {
$(contents[active]).addClass('alertify-tabs-content-active');
@@ -1192,15 +1193,20 @@ define([
var $ok = $('<span>', {'class': 'fa fa-check', title: Messages.saved}).hide();
var $spinner = $('<span>', {'class': 'fa fa-spinner fa-pulse'}).hide();
var state = false;
var spin = function () {
state = true;
$ok.hide();
$spinner.show();
};
var hide = function () {
state = false;
$ok.hide();
$spinner.hide();
};
var done = function () {
state = false;
$ok.show();
$spinner.hide();
};
@@ -1211,6 +1217,7 @@ define([
}
return {
getState: function () { return state; },
ok: $ok[0],
spinner: $spinner[0],
spin: spin,

View File

@@ -14,7 +14,7 @@ define([
'/customize/application_config.js',
'/customize/pages.js',
'/bower_components/nthen/index.js',
'/common/invitation.js',
'/common/inner/invitation.js',
'css!/customize/fonts/cptools/style.css',
'/bower_components/croppie/croppie.min.js',
@@ -99,7 +99,7 @@ define([
});
};
};
/*
var getPropertiesData = function (common, cb) {
var data = {};
NThen(function (waitFor) {
@@ -127,6 +127,32 @@ define([
cb(void 0, data);
});
};
*/
var getPropertiesData = function (common, opts, cb) {
opts = opts || {};
var data = {};
NThen(function (waitFor) {
var base = common.getMetadataMgr().getPrivateData().origin;
common.getPadAttribute('', waitFor(function (err, val) {
if (err || !val) {
waitFor.abort();
return void cb(err || 'EEMPTY');
}
if (!val.fileType) {
delete val.owners;
delete val.expire;
}
Util.extend(data, val);
if (data.href) { data.href = base + data.href; }
if (data.roHref) { data.roHref = base + data.roHref; }
}), opts.href);
}).nThen(function () {
cb(void 0, data);
});
};
/*
var createOwnerModal = function (common, data) {
var friends = common.getFriends(true);
var sframeChan = common.getSframeChannel();
@@ -425,8 +451,8 @@ define([
var link = h('div.cp-share-columns', [
div1,
div2
/*drawRemove()[0],
drawAdd()[0]*/
// drawRemove()[0],
//drawAdd()[0]
]);
var linkButtons = [{
className: 'cancel',
@@ -436,6 +462,8 @@ define([
}];
return UI.dialog.customModal(link, {buttons: linkButtons});
};
*/
/*
var getRightsProperties = function (common, data, cb) {
var $div = $('<div>');
if (!data) { return void cb(void 0, $div); }
@@ -707,7 +735,10 @@ define([
cb(void 0, $div);
};
var getPadProperties = function (common, data, cb) {
*/
var getPadProperties = function (common, data, opts, cb) {
opts = opts || {};
var $d = $('<div>');
if (!data) { return void cb(void 0, $d); }
@@ -721,7 +752,7 @@ define([
}));
}
if (data.roHref) {
if (data.roHref && !opts.noReadOnly) {
$('<label>', {'for': 'cp-app-prop-rolink'}).text(Messages.viewShare).appendTo($d);
$d.append(UI.dialog.selectable(data.roHref, {
id: 'cp-app-prop-rolink',
@@ -859,35 +890,42 @@ define([
};
UIElements.getProperties = function (common, data, cb) {
var c1;
var c2;
UIElements.getProperties = function (common, opts, cb) {
var data;
var content;
var button = [{
className: 'primary',
name: Messages.okButton,
className: 'cancel',
name: Messages.filePicker_close,
onClick: function () {},
keys: [13]
keys: [13,27]
}];
NThen(function (waitFor) {
getPadProperties(common, data, waitFor(function (e, c) {
c1 = UI.dialog.customModal(c[0], {
buttons: button
});
getPropertiesData(common, opts, waitFor(function (e, _data) {
if (e) {
waitFor.abort();
return void cb(e);
}
data = _data;
}));
getRightsProperties(common, data, waitFor(function (e, c) {
c2 = UI.dialog.customModal(c[0], {
}).nThen(function (waitFor) {
getPadProperties(common, data, opts, waitFor(function (e, c) {
if (e) {
waitFor.abort();
return void cb(e);
}
content = UI.dialog.customModal(c[0], {
buttons: button
});
}));
}).nThen(function () {
var tabs = UI.dialog.tabs([{
title: Messages.fc_prop,
content: c1
}, {
title: Messages.creation_propertiesTitle,
content: c2
icon: "fa fa-info-circle",
content: content
}]);
cb (void 0, $(tabs));
var modal = UI.openCustomModal(tabs);
cb (void 0, modal);
});
};
@@ -901,7 +939,15 @@ define([
var name = data.displayName || data.name || Messages.anonymous;
var avatar = h('span.cp-usergrid-avatar.cp-avatar');
UIElements.displayAvatar(common, $(avatar), data.avatar, name);
return h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
var removeBtn, el;
if (config.remove) {
removeBtn = h('span.fa.fa-times');
$(removeBtn).click(function () {
config.remove(el);
});
}
el = h('div.cp-usergrid-user'+(data.selected?'.cp-selected':'')+(config.large?'.large':''), {
'data-ed': data.edPublic,
'data-teamid': data.teamId,
'data-curve': data.curvePublic || '',
@@ -911,17 +957,20 @@ define([
style: 'order:'+i+';'
},[
avatar,
h('span.cp-usergrid-user-name', name)
h('span.cp-usergrid-user-name', name),
data.notRemovable ? undefined : removeBtn
]);
return el;
}).filter(function (x) { return x; });
var noOthers = icons.length === 0 ? '.cp-usergrid-empty' : '';
var classes = noOthers + (config.large?'.large':'') + (config.list?'.list':'');
var inputFilter = h('input', {
placeholder: Messages.share_filterFriend
});
var div = h('div.cp-usergrid-container' + noOthers + (config.large?'.large':''), [
var div = h('div.cp-usergrid-container' + classes, [
label ? h('label', label) : undefined,
h('div.cp-usergrid-filter', (config.noFilter || config.noSelect) ? undefined : [
inputFilter
@@ -2374,6 +2423,26 @@ define([
});
updateIcon(data.element.is(':visible'));
break;
case 'access':
button = $('<button>', {
'class': 'fa fa-unlock-alt cp-toolbar-icon-access',
title: "ACCESS", // XXX
}).append($('<span>', {'class': 'cp-toolbar-drawer-element'})
.text("ACCESS")) // XXX
.click(common.prepareFeedback(type))
.click(function () {
common.isPadStored(function (err, data) {
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
require(['/common/inner/access.js'], function (Access) {
Access.getAccessModal(common, {}, function (e) {
if (e) { console.error(e); }
});
});
});
});
break;
case 'properties':
button = $('<button>', {
'class': 'fa fa-info-circle cp-toolbar-icon-properties',
@@ -2386,12 +2455,8 @@ define([
if (!data) {
return void UI.alert(Messages.autostore_notAvailable);
}
getPropertiesData(common, function (e, data) {
UIElements.getProperties(common, {}, function (e) {
if (e) { return void console.error(e); }
UIElements.getProperties(common, data, function (e, $prop) {
if (e) { return void console.error(e); }
UI.openCustomModal($prop[0]);
});
});
});
});
@@ -4144,7 +4209,7 @@ define([
};
UIElements.onServerError = function (common, err, toolbar, cb) {
if (["EDELETED", "EEXPIRED"].indexOf(err.type) === -1) { return; }
if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
var priv = common.getMetadataMgr().getPrivateData();
var msg = err.type;
if (err.type === 'EEXPIRED') {
@@ -4158,11 +4223,13 @@ define([
if (err.loaded) {
msg += Messages.errorCopy;
}
} else if (err.type === 'ERESTRICTED') {
msg = Messages.restrictedError || "RESTRICTED"; // XXX
}
var sframeChan = common.getSframeChannel();
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
UI.errorLoadingScreen(msg, true, true);
UI.errorLoadingScreen(msg, Boolean(err.loaded), Boolean(err.loaded));
(cb || function () {})();
};

View File

@@ -68,6 +68,19 @@
};
};
Util.mkTimeout = function (_f, ms) {
ms = ms || 0;
var f = Util.once(_f);
var timeout = setTimeout(function () {
f('TIMEOUT');
}, ms);
return Util.both(f, function () {
clearTimeout(timeout);
});
};
Util.response = function () {
var pending = {};
var timeouts = {};

View File

@@ -8,6 +8,9 @@ define([
'/common/common-interface.js',
'/common/common-constants.js',
'/common/common-feedback.js',
'/common/inner/access.js',
'/bower_components/nthen/index.js',
'/common/hyperscript.js',
'/common/proxy-manager.js',
@@ -23,6 +26,7 @@ define([
UI,
Constants,
Feedback,
Access,
nThen,
h,
ProxyManager,
@@ -80,6 +84,7 @@ define([
var faTrash = 'fa-trash';
var faCopy = 'fa-clone';
var faDelete = 'fa-eraser';
var faAccess = 'fa-unlock-alt';
var faProperties = 'fa-info-circle';
var faTags = 'fa-hashtag';
var faUploadFiles = 'cptools-file-upload';
@@ -117,9 +122,9 @@ define([
var $addIcon = $('<span>', {"class": "fa fa-plus"});
var $renamedIcon = $('<span>', {"class": "fa fa-flag"});
var $readonlyIcon = $('<span>', {"class": "fa " + faReadOnly});
var $ownedIcon = $('<span>', {"class": "fa fa-id-card-o"});
var $ownedIcon = $('<span>', {"class": "fa fa-id-badge"});
var $sharedIcon = $('<span>', {"class": "fa " + faShared});
var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
//var $ownerIcon = $('<span>', {"class": "fa fa-id-card"});
var $tagsIcon = $('<span>', {"class": "fa " + faTags});
var $passwordIcon = $('<span>', {"class": "fa fa-lock"});
var $expirableIcon = $('<span>', {"class": "fa fa-clock-o"});
@@ -453,6 +458,10 @@ define([
'data-icon': faDelete,
}, Messages.fc_remove_sharedfolder)),
$separator.clone()[0],
h('li', h('a.cp-app-drive-context-access.dropdown-item', {
'tabindex': '-1',
'data-icon': faAccess,
}, "ACCESS")), // XXX
h('li', h('a.cp-app-drive-context-properties.dropdown-item', {
'tabindex': '-1',
'data-icon': faProperties,
@@ -1212,7 +1221,7 @@ define([
hide.push('savelocal');
hide.push('openro');
hide.push('openincode');
hide.push('properties');
hide.push('properties', 'access');
hide.push('hashtag');
hide.push('makeacopy');
}
@@ -1243,7 +1252,7 @@ define([
});
if (paths.length > 1) {
hide.push('restore');
hide.push('properties');
hide.push('properties', 'access');
hide.push('rename');
hide.push('openparent');
hide.push('hashtag');
@@ -1273,7 +1282,7 @@ define([
'deleteowned', 'removesf', 'properties', 'hashtag'];
break;
case 'default':
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'hashtag', 'makeacopy'];
show = ['open', 'openro', 'share', 'openparent', 'delete', 'deleteowned', 'properties', 'access', 'hashtag', 'makeacopy'];
break;
case 'trashtree': {
show = ['empty'];
@@ -1811,10 +1820,10 @@ define([
var $owned = $ownedIcon.clone().appendTo($state);
$owned.attr('title', Messages.fm_padIsOwned);
$span.addClass('cp-app-drive-element-owned');
} else if (data.owners && data.owners.length) {
} /* else if (data.owners && data.owners.length) {
var $owner = $ownerIcon.clone().appendTo($state);
$owner.attr('title', Messages.fm_padIsOwnedOther);
}
} */
};
var thumbsUrls = {};
var addFileData = function (element, $element) {
@@ -3086,9 +3095,8 @@ define([
}).appendTo($openDir);
}
$('<a>').text(Messages.fc_prop).click(function () {
APP.getProperties(r.id, function (e, $prop) {
APP.getProperties(r.id, function (e) {
if (e) { return void logError(e); }
UI.alert($prop[0], undefined, true);
});
}).appendTo($openDir);
}
@@ -3836,12 +3844,11 @@ define([
}
};
var getProperties = APP.getProperties = function (el, cb) {
APP.getProperties = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
//var ro = manager.isReadOnlyFile(el);
var base = APP.origin;
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
@@ -3850,42 +3857,42 @@ define([
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
if (data.href) {
data.href = base + data.href;
}
if (data.roHref) {
data.roHref = base + data.roHref;
}
if (currentPath[0] === TEMPLATE) {
data.isTemplate = true;
}
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
if (manager.isSharedFolder(el)) {
var ro = folders[el] && folders[el].version >= 2;
if (!ro) { delete data.roHref; }
//data.noPassword = true;
//data.noEditPassword = true;
data.noExpiration = true;
// this is here to allow users to check the channel id of a shared folder
// we should remove it at some point
data.sharedFolder = true;
if (!ro) { opts.noReadOnly = true; }
}
UIElements.getProperties(common, opts, cb);
};
APP.getAccess = function (el, cb) {
if (!manager.isFile(el) && !manager.isSharedFolder(el)) {
return void cb('NOT_FILE');
}
var data;
if (manager.isSharedFolder(el)) {
data = JSON.parse(JSON.stringify(manager.getSharedFolderData(el)));
} else {
data = JSON.parse(JSON.stringify(manager.getFileData(el)));
}
if (!data || !(data.href || data.roHref)) { return void cb('INVALID_FILE'); }
var opts = {};
opts.href = Hash.getRelativeHref(data.href || data.roHref);
opts.channel = data.channel;
// Transfer ownership: templates are stored as templates for other users/teams
if (currentPath[0] === TEMPLATE) {
opts.isTemplate = true;
}
if ((manager.isFile(el) && data.roHref) || manager.isSharedFolder(el)) { // Only for pads!
sframeChan.query('Q_GET_PAD_METADATA', {
channel: data.channel
}, function (err, val) {
if (!err && !(val && val.error)) {
data.owners = val.owners;
data.expire = val.expire;
data.pending_owners = val.pending_owners;
}
UIElements.getProperties(common, data, cb);
});
return;
// Shared folders: no expiration date
if (manager.isSharedFolder(el)) {
opts.noExpiration = true;
}
UIElements.getProperties(common, data, cb);
Access.getAccessModal(common, opts, cb);
};
if (!APP.loggedIn) {
@@ -4259,9 +4266,19 @@ define([
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
getProperties(el, function (e, $prop) {
APP.getProperties(el, function (e) {
if (e) { return void logError(e); }
});
}
else if ($this.hasClass("cp-app-drive-context-access")) {
if (paths.length !== 1) { return; }
el = manager.find(paths[0].path);
if (paths[0].path[0] === SHARED_FOLDER && APP.newSharedFolder) {
// ANON_SHARED_FOLDER
el = manager.find(paths[0].path.slice(1), APP.newSharedFolder);
}
APP.getAccess(el, function (e) {
if (e) { return void logError(e); }
UI.openCustomModal($prop[0]);
});
}
else if ($this.hasClass("cp-app-drive-context-hashtag")) {

1144
www/common/inner/access.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1479,6 +1479,8 @@ define([
var $properties = common.createButton('properties', true);
toolbar.$drawer.append($properties);
var $access = common.createButton('access', true);
toolbar.$drawer.append($access);
};
config.onReady = function (info) {

View File

@@ -1628,10 +1628,11 @@ define([
// data.send === true ==> send the request
Store.requestPadAccess = function (clientId, data, cb) {
var owner = data.owner;
var owners = data.owners;
// If the owner was not is the pad metadata, check if it is a friend.
// We'll contact the first owner for whom we know the mailbox
/* // XXX check mailbox in our contacts is not compatible with the new "mute pad" feature
var owners = data.owners;
if (!owner && Array.isArray(owners)) {
var friends = store.proxy.friends || {};
// If we have friends, check if an owner is one of them (with a mailbox)
@@ -1648,6 +1649,7 @@ define([
});
}
}
*/
// If send is true, send the request to the owner.
if (owner) {

View File

@@ -900,10 +900,11 @@ define([
cb = cb || function () {};
var sfId = Env.user.userObject.getSFIdFromHref(data.href);
if (sfId) {
var sfData = Env.user.proxy[UserObject.SHARED_FOLDERS][sfId];
var sfData = getSharedFolderData(Env, sfId);
var sfValue = data.attr ? sfData[data.attr] : JSON.parse(JSON.stringify(sfData));
setTimeout(function () {
cb(null, {
value: sfData[data.attr],
value: sfValue,
atime: 1
});
});

View File

@@ -702,6 +702,8 @@ define([
var $properties = common.createButton('properties', true);
toolbar.$drawer.append($properties);
var $access = common.createButton('access', true);
toolbar.$drawer.append($access);
createFilePicker();

View File

@@ -1250,22 +1250,24 @@ define([
});
// REQUEST_ACCESS is used both to check IF we can contact an owner (send === false)
// AND also to send the request if we want (send === true)
sframeChan.on('Q_REQUEST_ACCESS', function (send, cb) {
sframeChan.on('Q_REQUEST_ACCESS', function (data, cb) {
if (readOnly && hashes.editHash) {
return void cb({error: 'ALREADYKNOWN'});
}
var send = data.send;
var metadata = data.metadata;
var owner, owners;
var crypto = Crypto.createEncryptor(secret.keys);
var _secret = secret;
if (metadata && metadata.roHref) {
var _parsed = Utils.Hash.parsePadUrl(metadata.roHref);
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
}
var crypto = Crypto.createEncryptor(_secret.keys);
nThen(function (waitFor) {
// Try to get the owner's mailbox from the pad metadata first.
// If it's is an older owned pad, check if the owner is a friend
// or an acquaintance (from async-store directly in requestAccess)
Cryptpad.getPadMetadata({
channel: secret.channel
}, waitFor(function (obj) {
obj = obj || {};
if (obj.error) { return; }
var todo = function (obj) {
owners = obj.owners;
var mailbox;
@@ -1284,22 +1286,107 @@ define([
owner = data;
} catch (e) { console.error(e); }
}
};
// If we already have metadata, use it, otherwise, try to get it
if (metadata) { return void todo(metadata); }
Cryptpad.getPadMetadata({
channel: _secret.channel
}, waitFor(function (obj) {
obj = obj || {};
if (obj.error) { return; }
todo(obj);
}));
}).nThen(function () {
// If we are just checking (send === false) and there is a mailbox field, cb state true
// If there is no mailbox, we'll have to check if an owner is a friend in the worker
/* // XXX
if (owner && !send) {
return void cb({state: true});
}
*/
if (!send) { return void cb({state: Boolean(owner)}); }
Cryptpad.padRpc.requestAccess({
send: send,
channel: secret.channel,
channel: _secret.channel,
owner: owner,
owners: owners
}, cb);
});
});
// Add or remove our mailbox from the list if we're an owner
sframeChan.on('Q_UPDATE_MAILBOX', function (data, cb) {
var metadata = data.metadata;
var add = data.add;
var _secret = secret;
if (metadata && (metadata.href || metadata.roHref)) {
var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref);
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
}
var crypto = Crypto.createEncryptor(_secret.keys);
nThen(function (waitFor) {
// If we already have metadata, use it, otherwise, try to get it
if (metadata) { return; }
Cryptpad.getPadMetadata({
channel: secret.channel
}, waitFor(function (obj) {
obj = obj || {};
if (obj.error) {
waitFor.abort();
return void cb(obj);
}
metadata = obj;
}));
}).nThen(function () {
// Get and maybe migrate the existing mailbox object
var owners = metadata.owners;
if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) {
return void cb({ error: 'INSUFFICIENT_PERMISSIONS' });
}
// Remove a mailbox
if (!add) {
// Old format: this is the mailbox of the first owner
if (typeof (metadata.mailbox) === "string" && metadata.mailbox) {
// Not our mailbox? abort
if (owners[0] !== edPublic) {
return void cb({ error: 'INSUFFICIENT_PERMISSIONS' });
}
// Remove it
return void Cryptpad.setPadMetadata({
channel: _secret.channel,
command: 'RM_MAILBOX',
value: []
}, cb);
} else if (metadata.mailbox) { // New format
return void Cryptpad.setPadMetadata({
channel: _secret.channel,
command: 'RM_MAILBOX',
value: [edPublic]
}, cb);
}
return void cb({
error: 'NO_MAILBOX'
});
}
// Add a mailbox
var toAdd = {};
toAdd[edPublic] = crypto.encrypt(JSON.stringify({
notifications: notifications,
curvePublic: curvePublic
}));
Cryptpad.setPadMetadata({
channel: _secret.channel,
command: 'ADD_MAILBOX',
value: toAdd
}, cb);
});
});
sframeChan.on('EV_BURN_PAD', function (channel) {
if (!burnAfterReading) { return; }
Cryptpad.burnPad({

View File

@@ -574,6 +574,7 @@ MessengerUI, Messages) {
return $shareBlock;
};
/*
var createRequest = function (toolbar, config) {
if (!config.metadataMgr) {
throw new Error("You must provide a `metadataMgr` to display the request access button");
@@ -590,13 +591,13 @@ MessengerUI, Messages) {
// If we have access to the owner's mailbox, display the button and enable it
// false => check if we can contact the owner
// true ==> send the request
Common.getSframeChannel().query('Q_REQUEST_ACCESS', false, function (err, obj) {
Common.getSframeChannel().query('Q_REQUEST_ACCESS', {send:false}, function (err, obj) {
if (obj && obj.state) {
var locked = false;
$requestBlock.show().click(function () {
if (locked) { return; }
locked = true;
Common.getSframeChannel().query('Q_REQUEST_ACCESS', true, function (err, obj) {
Common.getSframeChannel().query('Q_REQUEST_ACCESS', {send:true}, function (err, obj) {
if (obj && obj.state) {
UI.log(Messages.requestEdit_sent);
$requestBlock.hide();
@@ -614,6 +615,7 @@ MessengerUI, Messages) {
return $requestBlock;
};
*/
var createTitle = function (toolbar, config) {
var $titleContainer = $('<span>', {
@@ -1226,7 +1228,7 @@ MessengerUI, Messages) {
tb['fileshare'] = createFileShare;
tb['title'] = createTitle;
tb['pageTitle'] = createPageTitle;
tb['request'] = createRequest;
//tb['request'] = createRequest;
tb['lag'] = $.noop;
tb['spinner'] = createSpinner;
tb['state'] = $.noop;