1583 lines
63 KiB
JavaScript
1583 lines
63 KiB
JavaScript
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var bulma, h, AuthWS;
|
||
bulma = require("./bulma.ls");
|
||
h = require('maquette').h;
|
||
AuthWS = function(socketUrl){
|
||
var self, requestTypes, responseTypes, key, value;
|
||
self = {};
|
||
requestTypes = {
|
||
"get-token": 0,
|
||
"add-user": 1,
|
||
"get-user": 2,
|
||
"get-user-by-credentials": 3,
|
||
"mod-user": 4,
|
||
"register": 5,
|
||
"get-extra": 6,
|
||
"set-extra": 7,
|
||
"update-password": 8,
|
||
"list-users": 9
|
||
};
|
||
responseTypes = {
|
||
"error": 0,
|
||
"token": 1,
|
||
"user": 2,
|
||
"user-added": 3,
|
||
"user-edited": 4,
|
||
"extra": 5,
|
||
"extra-updated": 6,
|
||
"users-list": 7
|
||
};
|
||
self.userOnSocketError = [];
|
||
self.userOnSocketClose = [];
|
||
self.callbacks = {};
|
||
for (key in responseTypes) {
|
||
value = responseTypes[key];
|
||
self.callbacks[value] = [];
|
||
}
|
||
self.addEventListener = function(type, callback){
|
||
var ref$;
|
||
type = responseTypes[type];
|
||
return (ref$ = self.callbacks)[type] = ref$[type].concat([callback]);
|
||
};
|
||
self.openSocket = function(){
|
||
self.socket = new WebSocket(socketUrl);
|
||
self.socket.onerror = function(event){
|
||
var i$, ref$, len$, f;
|
||
for (i$ = 0, len$ = (ref$ = self.userOnSocketError).length; i$ < len$; ++i$) {
|
||
f = ref$[i$];
|
||
f(event);
|
||
}
|
||
return self.socket.close();
|
||
};
|
||
self.socket.onclose = function(event){
|
||
var i$, ref$, len$, f, results$ = [];
|
||
for (i$ = 0, len$ = (ref$ = self.userOnSocketClose).length; i$ < len$; ++i$) {
|
||
f = ref$[i$];
|
||
results$.push(f(event));
|
||
}
|
||
return results$;
|
||
};
|
||
return self.socket.onmessage = function(event){
|
||
var message, i$, ref$, len$, f, results$ = [];
|
||
message = JSON.parse(event.data);
|
||
for (i$ = 0, len$ = (ref$ = self.callbacks[message.mtype]).length; i$ < len$; ++i$) {
|
||
f = ref$[i$];
|
||
results$.push(f(JSON.parse(message.payload)));
|
||
}
|
||
return results$;
|
||
};
|
||
};
|
||
self.reopen = function(){
|
||
self.socket.close();
|
||
return self.openSocket();
|
||
};
|
||
self.openSocket();
|
||
self.send = function(type, opts){
|
||
return self.socket.send(JSON.stringify({
|
||
mtype: type,
|
||
payload: opts
|
||
}));
|
||
};
|
||
self.getToken = function(login, password){
|
||
return self.send(requestTypes['get-token'], JSON.stringify({
|
||
login: login,
|
||
password: password
|
||
}));
|
||
};
|
||
self.getUserByCredentials = function(login, password){
|
||
return self.send(requestTypes['get-user-by-credentials'], JSON.stringify({
|
||
login: login,
|
||
password: password
|
||
}));
|
||
};
|
||
self.login = function(login, password){
|
||
self.getToken(login, password);
|
||
return self.getUserByCredentials(login, password);
|
||
};
|
||
self.getUser = function(uid){
|
||
return self.send(requestTypes['get-user'], JSON.stringify({
|
||
uid: uid
|
||
}));
|
||
};
|
||
self.register = function(login, password){
|
||
return self.send(requestTypes['register'], JSON.stringify({
|
||
login: login,
|
||
password: password
|
||
}));
|
||
};
|
||
self.getExtra = function(token, name){
|
||
return self.send(requestTypes['get-extra'], JSON.stringify({
|
||
token: token,
|
||
name: name
|
||
}));
|
||
};
|
||
self.setExtra = function(token, name, extra){
|
||
return self.send(requestTypes['set-extra'], JSON.stringify({
|
||
token: token,
|
||
name: name,
|
||
extra: extra
|
||
}));
|
||
};
|
||
self.updatePassword = function(login, oldPassword, newPassword){
|
||
return self.send(requestTypes['update-password'], JSON.stringify({
|
||
login: login,
|
||
old_password: oldPassword,
|
||
new_password: newPassword
|
||
}));
|
||
};
|
||
self.listUsers = function(token){
|
||
return self.send(requestTypes['list-users'], JSON.stringify({
|
||
token: token
|
||
}));
|
||
};
|
||
return self;
|
||
};
|
||
module.exports = AuthWS;
|
||
|
||
|
||
|
||
},{"./bulma.ls":2,"maquette":7}],2:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var h;
|
||
h = require('maquette').h;
|
||
module.exports = {
|
||
box: function(args, children){
|
||
return h('div.box', args, children);
|
||
},
|
||
title: function(level, args, label){
|
||
if (!label) {
|
||
label = args;
|
||
args = {};
|
||
}
|
||
return h("div.title.is-" + level, args, [label]);
|
||
},
|
||
label: function(args, label){
|
||
if (!label) {
|
||
label = args;
|
||
args = {};
|
||
}
|
||
return h('label.label', args, [label]);
|
||
},
|
||
input: function(args, children){
|
||
return h('input.input', args, children);
|
||
},
|
||
field: function(args, children){
|
||
return h('div.field', args, children);
|
||
},
|
||
modal: function(args, content){
|
||
return h('div.modal', args, [h('div.modal-background', args.background), h('div.modal-content', [args.content])]);
|
||
},
|
||
form: function(method, url, content){
|
||
return h('form.form', {
|
||
action: url,
|
||
method: method
|
||
}, content);
|
||
}
|
||
};
|
||
|
||
|
||
|
||
},{"maquette":7}],3:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var maquette, createProjector, h, projector, bulma, AuthWS, LoginForm, UserConfigurationPanel, UserAdminPanel, model, authwsUrl;
|
||
maquette = require("maquette");
|
||
createProjector = maquette.createProjector, h = maquette.h;
|
||
projector = createProjector();
|
||
bulma = require("./bulma.ls");
|
||
AuthWS = require("./authws.ls");
|
||
LoginForm = require("./login-form.ls");
|
||
UserConfigurationPanel = require("./user-configuration-panel.ls");
|
||
UserAdminPanel = require("./user-admin-panel.ls");
|
||
model = {
|
||
token: void 8
|
||
};
|
||
authwsUrl = "ws://localhost:9999/auth.JSON";
|
||
document.addEventListener('DOMContentLoaded', function(){
|
||
var userConfigPanel, userAdminPanel, loginForm;
|
||
userConfigPanel = void 8;
|
||
userAdminPanel = void 8;
|
||
loginForm = LoginForm({
|
||
enableRegistration: true,
|
||
authwsUrl: authwsUrl,
|
||
onLogin: function(user, token){
|
||
model.user = user;
|
||
model.token = token;
|
||
if (false) {
|
||
userAdminPanel = UserAdminPanel({
|
||
authwsUrl: authwsUrl,
|
||
user: model.user,
|
||
token: model.token,
|
||
onModelUpdate: function(){
|
||
return projector.scheduleRender();
|
||
},
|
||
onLogout: function(){
|
||
model.token = void 8;
|
||
return model.user = void 8;
|
||
}
|
||
});
|
||
} else {
|
||
userConfigPanel = UserConfigurationPanel({
|
||
authwsUrl: authwsUrl,
|
||
user: model.user,
|
||
token: model.token,
|
||
onModelUpdate: function(){
|
||
return projector.scheduleRender();
|
||
},
|
||
onLogout: function(){
|
||
model.token = void 8;
|
||
return model.user = void 8;
|
||
}
|
||
});
|
||
}
|
||
return projector.scheduleRender();
|
||
},
|
||
onError: function(error){
|
||
return projector.scheduleRender();
|
||
}
|
||
});
|
||
return projector.append(document.body, function(){
|
||
return h('div.body', [model.token === void 8
|
||
? h('div.section.hero.is-fullheight', [h('div.hero-body', [h('div.container', [h('div.columns', [h('div.column', []), h('div.column.is-3', [loginForm.render()]), h('div.column', [])])])])])
|
||
: userConfigPanel
|
||
? h('div.section', [h('div.container', [userConfigPanel.render()])])
|
||
: userAdminPanel ? h('div.section', [h('div.container', [userAdminPanel.render()])]) : void 8]);
|
||
});
|
||
});
|
||
|
||
|
||
|
||
},{"./authws.ls":1,"./bulma.ls":2,"./login-form.ls":4,"./user-admin-panel.ls":5,"./user-configuration-panel.ls":6,"maquette":7}],4:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var maquette, h, bulma, AuthWS, LoginForm;
|
||
maquette = require("maquette");
|
||
h = maquette.h;
|
||
bulma = require("./bulma.ls");
|
||
AuthWS = require("./authws.ls");
|
||
LoginForm = function(args){
|
||
var self, authWs;
|
||
args || (args = {});
|
||
self = {
|
||
onLogin: args.onLogin || function(){},
|
||
onError: args.onError || function(){},
|
||
scheduleRender: args.scheduleRender || function(){},
|
||
currentView: "login",
|
||
enableRegistration: args.enableRegistration || false,
|
||
registrating: false,
|
||
input: {
|
||
login: "",
|
||
password: "",
|
||
repeatPassword: ""
|
||
},
|
||
lockedInput: true,
|
||
error: void 8,
|
||
authwsUrl: args.authwsUrl || (location.protocol === 'https' ? 'wss' : 'ws') + '://' + location.hostname + ":9999/auth.JSON"
|
||
};
|
||
authWs = AuthWS(self.authwsUrl);
|
||
authWs.socket.onopen = function(){
|
||
console.log("socket is open");
|
||
self.lockedInput = false;
|
||
return self.scheduleRender();
|
||
};
|
||
authWs.userOnSocketError.push(function(){
|
||
self.error = "socket error";
|
||
return self.onError.apply(this, arguments);
|
||
});
|
||
authWs.addEventListener('token', function(message){
|
||
self.error = void 8;
|
||
self.token = message.token;
|
||
self.lockedInput = false;
|
||
if (self.user) {
|
||
return self.onLogin(self.user, self.token);
|
||
}
|
||
});
|
||
authWs.addEventListener('user', function(message){
|
||
self.error = void 8;
|
||
self.user = message.user;
|
||
if (self.token) {
|
||
return self.onLogin(self.user, self.token);
|
||
}
|
||
});
|
||
authWs.addEventListener('user-added', function(message){
|
||
var ref$, login, password;
|
||
ref$ = {
|
||
login: self.input.login,
|
||
password: self.input.password
|
||
}, login = ref$.login, password = ref$.password;
|
||
console.log("user added, duh");
|
||
self.user = message.user;
|
||
return authWs.getToken(login, password);
|
||
});
|
||
authWs.addEventListener('error', function(message){
|
||
if (message.reason === "user not found") {
|
||
return;
|
||
}
|
||
self.error = message.reason;
|
||
self.lockedInput = false;
|
||
return self.onError(message.reason);
|
||
});
|
||
self.render = function(){
|
||
if (self.error === "socket error") {
|
||
return h('div.notification.is-danger', [h('div.title.is-4', ["WebSocket error!"]), h('p', ["Cannot connect to authd."])]);
|
||
}
|
||
return h('form.form.login-form', {
|
||
key: self,
|
||
onsubmit: function(e){
|
||
var ref$, login, password;
|
||
ref$ = {
|
||
login: self.input.login,
|
||
password: self.input.password
|
||
}, login = ref$.login, password = ref$.password;
|
||
self.lockedInput = true;
|
||
if (self.registrating) {
|
||
authWs.register(login, password);
|
||
} else {
|
||
authWs.getToken(login, password);
|
||
authWs.getUserByCredentials(login, password);
|
||
}
|
||
return e.preventDefault();
|
||
}
|
||
}, [
|
||
h('div.field', {
|
||
key: 'login'
|
||
}, [
|
||
bulma.label("Login"), bulma.input({
|
||
type: "text",
|
||
id: "login",
|
||
name: "login",
|
||
classes: {
|
||
"is-danger": self.error === "invalid credentials"
|
||
},
|
||
disabled: self.lockedInput || void 8,
|
||
oninput: function(e){
|
||
return self.input.login = e.target.value;
|
||
}
|
||
})
|
||
]), h('div.field', {
|
||
key: 'password'
|
||
}, [
|
||
bulma.label("Password"), bulma.input({
|
||
type: "password",
|
||
id: "password",
|
||
name: "password",
|
||
classes: {
|
||
"is-danger": self.error === "invalid credentials"
|
||
},
|
||
oninput: function(e){
|
||
return self.input.password = e.target.value;
|
||
},
|
||
disabled: self.lockedInput
|
||
})
|
||
]), self.registrating ? h('div.field', {
|
||
key: 'password-repeat'
|
||
}, [
|
||
bulma.label("Password (reapeat)"), bulma.input({
|
||
type: 'password',
|
||
id: 'password-repeat',
|
||
name: 'password-repeat',
|
||
classes: {
|
||
"is-danger": self.input.password !== self.input.repeatPassword
|
||
},
|
||
disabled: self.lockedInput,
|
||
oninput: function(e){
|
||
return self.input.repeatPassword = e.target.value;
|
||
}
|
||
})
|
||
]) : void 8, self.error ? h('div.field', {
|
||
key: 'error-notification'
|
||
}, [h('div.notification.is-danger', [self.error])]) : void 8, self.registrating
|
||
? h('div.field.is-grouped', {
|
||
key: 'login-button'
|
||
}, [self.input.login === ""
|
||
? h('button.button.is-static.is-fullwidth', {
|
||
type: 'submit'
|
||
}, ["(empty login)"])
|
||
: self.input.password !== self.input.repeatPassword
|
||
? h('button.button.is-static.is-fullwidth', {
|
||
type: 'submit'
|
||
}, ["(passwords don’t match)"])
|
||
: self.input.password === ""
|
||
? h('button.button.is-static.is-fullwidth', {
|
||
type: 'submit'
|
||
}, ["(empty password)"])
|
||
: h('button.button.is-success.is-fullwidth', {
|
||
type: 'submit'
|
||
}, ["Register!"])])
|
||
: h('div.field.is-grouped', {
|
||
key: 'login-button'
|
||
}, [h('button.button.is-fullwidth.is-success', {
|
||
type: 'submit'
|
||
}, ["Log in!"])]), h('div.field.level', {
|
||
key: 'extra-buttons'
|
||
}, [self.enableRegistration ? h('div.level-right', [self.registrating
|
||
? h('a.link', {
|
||
onclick: function(e){
|
||
return self.registrating = false;
|
||
}
|
||
}, ["Log in"])
|
||
: h('a.link', {
|
||
onclick: function(e){
|
||
return self.registrating = true;
|
||
}
|
||
}, ["Create account!"])]) : void 8])
|
||
]);
|
||
};
|
||
return self;
|
||
};
|
||
module.exports = LoginForm;
|
||
|
||
|
||
|
||
},{"./authws.ls":1,"./bulma.ls":2,"maquette":7}],5:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var h, AuthWS, UserAdminPanel;
|
||
h = require("maquette").h;
|
||
AuthWS = require("./authws.ls");
|
||
UserAdminPanel = function(args){
|
||
var self, authws;
|
||
self = {
|
||
token: args.token,
|
||
authwsUrl: args.authwsUrl,
|
||
onLogout: args.onLogout || function(){},
|
||
onModelUpdate: args.onModelUpdate || function(){},
|
||
users: []
|
||
};
|
||
authws = AuthWS(self.authwsUrl);
|
||
authws.socket.onopen = function(){
|
||
return authws.listUsers(self.token);
|
||
};
|
||
authws.addEventListener('users-list', function(message){
|
||
self.users = message.users;
|
||
return self.onModelUpdate();
|
||
});
|
||
self.render = function(){
|
||
var user;
|
||
return h('div.section', [
|
||
h('div.container', [h('table.table.is-fullwidth', [
|
||
h('thead', [h('tr', [h('th', ["Login"]), h('th', ["UID"]), h('th', ["GID"])])]), h('tbody', [(function(){
|
||
var i$, ref$, len$, results$ = [];
|
||
for (i$ = 0, len$ = (ref$ = self.users).length; i$ < len$; ++i$) {
|
||
user = ref$[i$];
|
||
results$.push(h('tr', {
|
||
key: user.uid
|
||
}, [h('td', [user.login]), h('td', [user.uid.toString()]), h('td', [user.gid.toString()])]));
|
||
}
|
||
return results$;
|
||
}())])
|
||
])]), h('div.button', {
|
||
onclick: function(){
|
||
self.onLogout();
|
||
return self.onModelUpdate();
|
||
}
|
||
}, ["Log out"])
|
||
]);
|
||
};
|
||
return self;
|
||
};
|
||
module.exports = UserAdminPanel;
|
||
|
||
|
||
|
||
},{"./authws.ls":1,"maquette":7}],6:[function(require,module,exports){
|
||
// Generated by LiveScript 1.6.0
|
||
var h, AuthWS, getFullName, defaultSideBarRenderer, defaultHeadingRenderer, Fields, UserConfigurationPanel;
|
||
h = require("maquette").h;
|
||
AuthWS = require("./authws.ls");
|
||
getFullName = function(self){
|
||
var fullName, ref$;
|
||
fullName = ((ref$ = self.profile) != null ? ref$.fullName : void 8) || "";
|
||
if (fullName === "") {
|
||
return self.user.login;
|
||
} else {
|
||
return fullName;
|
||
}
|
||
};
|
||
defaultSideBarRenderer = function(self){
|
||
var avatar, ref$;
|
||
return h('div', {
|
||
key: 'side-bar'
|
||
}, [h('figure.image.is-128x128.is-clipped', [(avatar = (ref$ = self.profile) != null ? ref$.avatar : void 8) ? h('img', {
|
||
src: avatar,
|
||
alt: "Avatar of " + getFullName(self)
|
||
}) : void 8])]);
|
||
};
|
||
defaultHeadingRenderer = function(self){
|
||
var fullName;
|
||
fullName = getFullName(self);
|
||
return h('div.section', {
|
||
key: 'heading'
|
||
}, [h('div.media', [
|
||
h('div.media-content', [h('div.title.is-2', [fullName]), fullName !== self.user.login ? h('div.title.is-3.subtitle', [self.user.login]) : void 8]), self.onLogout ? h('div.media-right', [h('a', {
|
||
onclick: function(){
|
||
return self.onLogout();
|
||
}
|
||
}, ["Logout"])]) : void 8
|
||
])]);
|
||
};
|
||
Fields = {
|
||
renderTextInput: function(token, authWs, key, inputs, model, onRequest){
|
||
var upload;
|
||
upload = function(){
|
||
var payload, _key, ref$, value;
|
||
console.log("clickity click", key, inputs[key], inputs);
|
||
if (!inputs[key]) {
|
||
return;
|
||
}
|
||
payload = {};
|
||
for (_key in ref$ = model) {
|
||
value = ref$[_key];
|
||
payload[_key] = value;
|
||
}
|
||
payload[key] = inputs[key];
|
||
inputs[key] = void 8;
|
||
onRequest();
|
||
return authWs.setExtra(token, "profile", payload);
|
||
};
|
||
return h('div.field.has-addons', {
|
||
key: key
|
||
}, [
|
||
h('div.control.is-expanded', [h('input.input', {
|
||
value: inputs[key] || model[key],
|
||
oninput: function(e){
|
||
console.log("input for", key);
|
||
return inputs[key] = e.target.value;
|
||
}
|
||
})]), h('div.control', [h('div.button', {
|
||
onclick: upload
|
||
}, ["Update"])])
|
||
]);
|
||
}
|
||
};
|
||
UserConfigurationPanel = function(args){
|
||
var self, authWs;
|
||
self = {
|
||
user: args.user || {},
|
||
profile: args.profile,
|
||
token: args.token,
|
||
authwsUrl: args.authwsUrl || (location.protocol === 'https' ? 'wss' : 'ws') + '://' + location.hostname + ":9999/auth.JSON",
|
||
sideBarRenderer: args.sideBarRenderer || defaultSideBarRenderer,
|
||
headingRenderer: args.headingRenderer || defaultHeadingRenderer,
|
||
onModelUpdate: args.onModelUpdate || function(){},
|
||
onLogout: args.onLogout || void 8,
|
||
model: args.model || [["fullName", "Full Name", "string"], ["avatar", "Profile Picture", "image-url"], ["email", "Mail Address", "string"]],
|
||
input: {}
|
||
};
|
||
authWs = AuthWS(self.authwsUrl);
|
||
authWs.addEventListener('extra', function(message){
|
||
if (message.name === "profile") {
|
||
console.log("got profile", message.extra);
|
||
self.profile = message.extra || {};
|
||
return self.onModelUpdate();
|
||
}
|
||
});
|
||
authWs.addEventListener('extra-updated', function(message){
|
||
if (message.name === "profile") {
|
||
console.log("got profile", message.extra);
|
||
self.profile = message.extra || {};
|
||
return self.onModelUpdate();
|
||
}
|
||
});
|
||
authWs.addEventListener('error', function(message){
|
||
self.error = message.reason;
|
||
return self.onModelUpdate();
|
||
});
|
||
authWs.addEventListener('user-edited', function(message){
|
||
self.input["password.old"] = void 8;
|
||
self.input["password.new"] = void 8;
|
||
self.input["password.new2"] = void 8;
|
||
self.success = "password";
|
||
return self.onModelUpdate();
|
||
});
|
||
if (!self.profile) {
|
||
authWs.socket.onopen = function(){
|
||
return authWs.getExtra(self.token, "profile");
|
||
};
|
||
}
|
||
self.render = function(){
|
||
var element, key, label, type, service, permissions;
|
||
return h('div.columns', {
|
||
key: self
|
||
}, [
|
||
h('div.column.is-narrow', [self.sideBarRenderer(self)]), h('div.column', [
|
||
self.headingRenderer(self), self.profile
|
||
? h('div.box', {
|
||
key: 'profile'
|
||
}, [h('div.form', [
|
||
h('div.title.is-4', ["Profile"]), (function(){
|
||
var i$, ref$, len$, results$ = [];
|
||
for (i$ = 0, len$ = (ref$ = self.model).length; i$ < len$; ++i$) {
|
||
element = ref$[i$];
|
||
key = element[0], label = element[1], type = element[2];
|
||
switch (type) {
|
||
case "string":
|
||
case "image-url":
|
||
results$.push(h('div.field', {
|
||
key: key
|
||
}, [h('div.label', [label]), Fields.renderTextInput(self.token, authWs, key, self.input, self.profile, fn$)]));
|
||
}
|
||
}
|
||
return results$;
|
||
function fn$(){
|
||
return self.error = void 8;
|
||
}
|
||
}())
|
||
])])
|
||
: h('div.button.is-loading'), h('div.box', {
|
||
key: 'password'
|
||
}, [
|
||
h('div.title.is-4', ["Password"]), h('div.label', ["Old password"]), h('div.control', [
|
||
h('input.input', {
|
||
type: 'password',
|
||
value: self.input["password.old"],
|
||
oninput: function(e){
|
||
return self.input["password.old"] = e.target.value;
|
||
}
|
||
}), self.error === "invalid credentials" ? h('div.help.is-danger', ["The old password was invalid!"]) : void 8
|
||
]), h('div.label', ["New password"]), h('div.control', [h('input.input', {
|
||
type: 'password',
|
||
value: self.input["password.new"],
|
||
oninput: function(e){
|
||
return self.input["password.new"] = e.target.value;
|
||
}
|
||
})]), h('div.label', ["New password (repeat)"]), h('div.field.has-addons', [
|
||
h('div.control.is-expanded', [h('input.input', {
|
||
type: 'password',
|
||
value: self.input["password.new2"],
|
||
oninput: function(e){
|
||
return self.input["password.new2"] = e.target.value;
|
||
}
|
||
})]), h('div.control', [h('div.button', {
|
||
classes: {
|
||
"is-danger": self.input["password.new"] && self.input["password.new"] !== self.input["password.new2"],
|
||
"is-static": !self.input["password.new"] && self.input["password.new"] !== self.input["password.new2"]
|
||
},
|
||
onclick: function(){
|
||
if (self.input["password.new"] !== self.input["password.new2"]) {
|
||
return;
|
||
}
|
||
self.error = void 8;
|
||
return authWs.updatePassword(self.user.login, self.input["password.old"], self.input["password.new"]);
|
||
}
|
||
}, ["Update"])])
|
||
]), self.success === "password" ? h('div.help.is-success', ["Password successfully updated!"]) : void 8
|
||
]), self.showDeveloper
|
||
? h('div.box', {
|
||
key: 'passwd'
|
||
}, [
|
||
h('div.title.is-4', ["Permissions"]), h('div.form', [
|
||
h('div.field', {
|
||
key: 'uid'
|
||
}, [h('div.label', ["User ID"]), h('div.control', [self.user.uid.toString()])]), h('div.field', {
|
||
key: 'gid'
|
||
}, [h('div.label', ["Group ID"]), h('div.control', [self.user.gid.toString()])]), h('div.field', {
|
||
key: 'permissions'
|
||
}, [
|
||
h('div.label', ["Permissions"]), h('div.control.is-grouped', [h('div.tags', [(function(){
|
||
var ref$, results$ = [];
|
||
for (service in ref$ = self.user.permissions) {
|
||
permissions = ref$[service];
|
||
results$.push(permissions.map(fn$));
|
||
}
|
||
return results$;
|
||
function fn$(perm){
|
||
return h('div.tag', [service, permission]);
|
||
}
|
||
}())])])
|
||
])
|
||
])
|
||
])
|
||
: h('a.is-pulled-right.is-small.has-text-grey', {
|
||
key: 'passwd',
|
||
onclick: function(){
|
||
self.showDeveloper = true;
|
||
return self.onModelUpdate();
|
||
}
|
||
}, ["Show developer data!"])
|
||
])
|
||
]);
|
||
};
|
||
return self;
|
||
};
|
||
module.exports = UserConfigurationPanel;
|
||
|
||
|
||
|
||
},{"./authws.ls":1,"maquette":7}],7:[function(require,module,exports){
|
||
(function (global, factory) {
|
||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||
(global = global || self, factory(global.maquette = {}));
|
||
}(this, function (exports) { 'use strict';
|
||
|
||
/* tslint:disable no-http-string */
|
||
var NAMESPACE_W3 = 'http://www.w3.org/';
|
||
/* tslint:enable no-http-string */
|
||
var NAMESPACE_SVG = NAMESPACE_W3 + "2000/svg";
|
||
var NAMESPACE_XLINK = NAMESPACE_W3 + "1999/xlink";
|
||
var emptyArray = [];
|
||
var extend = function (base, overrides) {
|
||
var result = {};
|
||
Object.keys(base).forEach(function (key) {
|
||
result[key] = base[key];
|
||
});
|
||
if (overrides) {
|
||
Object.keys(overrides).forEach(function (key) {
|
||
result[key] = overrides[key];
|
||
});
|
||
}
|
||
return result;
|
||
};
|
||
var same = function (vnode1, vnode2) {
|
||
if (vnode1.vnodeSelector !== vnode2.vnodeSelector) {
|
||
return false;
|
||
}
|
||
if (vnode1.properties && vnode2.properties) {
|
||
if (vnode1.properties.key !== vnode2.properties.key) {
|
||
return false;
|
||
}
|
||
return vnode1.properties.bind === vnode2.properties.bind;
|
||
}
|
||
return !vnode1.properties && !vnode2.properties;
|
||
};
|
||
var checkStyleValue = function (styleValue) {
|
||
if (typeof styleValue !== 'string') {
|
||
throw new Error('Style values must be strings');
|
||
}
|
||
};
|
||
var findIndexOfChild = function (children, sameAs, start) {
|
||
if (sameAs.vnodeSelector !== '') {
|
||
// Never scan for text-nodes
|
||
for (var i = start; i < children.length; i++) {
|
||
if (same(children[i], sameAs)) {
|
||
return i;
|
||
}
|
||
}
|
||
}
|
||
return -1;
|
||
};
|
||
var checkDistinguishable = function (childNodes, indexToCheck, parentVNode, operation) {
|
||
var childNode = childNodes[indexToCheck];
|
||
if (childNode.vnodeSelector === '') {
|
||
return; // Text nodes need not be distinguishable
|
||
}
|
||
var properties = childNode.properties;
|
||
var key = properties ? (properties.key === undefined ? properties.bind : properties.key) : undefined;
|
||
if (!key) { // A key is just assumed to be unique
|
||
for (var i = 0; i < childNodes.length; i++) {
|
||
if (i !== indexToCheck) {
|
||
var node = childNodes[i];
|
||
if (same(node, childNode)) {
|
||
throw new Error(parentVNode.vnodeSelector + " had a " + childNode.vnodeSelector + " child " + (operation === 'added' ? operation : 'removed') + ", but there is now more than one. You must add unique key properties to make them distinguishable.");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
var nodeAdded = function (vNode) {
|
||
if (vNode.properties) {
|
||
var enterAnimation = vNode.properties.enterAnimation;
|
||
if (enterAnimation) {
|
||
enterAnimation(vNode.domNode, vNode.properties);
|
||
}
|
||
}
|
||
};
|
||
var removedNodes = [];
|
||
var requestedIdleCallback = false;
|
||
var visitRemovedNode = function (node) {
|
||
(node.children || []).forEach(visitRemovedNode);
|
||
if (node.properties && node.properties.afterRemoved) {
|
||
node.properties.afterRemoved.apply(node.properties.bind || node.properties, [node.domNode]);
|
||
}
|
||
};
|
||
var processPendingNodeRemovals = function () {
|
||
requestedIdleCallback = false;
|
||
removedNodes.forEach(visitRemovedNode);
|
||
removedNodes.length = 0;
|
||
};
|
||
var scheduleNodeRemoval = function (vNode) {
|
||
removedNodes.push(vNode);
|
||
if (!requestedIdleCallback) {
|
||
requestedIdleCallback = true;
|
||
if (typeof window !== 'undefined' && 'requestIdleCallback' in window) {
|
||
window.requestIdleCallback(processPendingNodeRemovals, { timeout: 16 });
|
||
}
|
||
else {
|
||
setTimeout(processPendingNodeRemovals, 16);
|
||
}
|
||
}
|
||
};
|
||
var nodeToRemove = function (vNode) {
|
||
var domNode = vNode.domNode;
|
||
if (vNode.properties) {
|
||
var exitAnimation = vNode.properties.exitAnimation;
|
||
if (exitAnimation) {
|
||
domNode.style.pointerEvents = 'none';
|
||
var removeDomNode = function () {
|
||
if (domNode.parentNode) {
|
||
domNode.parentNode.removeChild(domNode);
|
||
scheduleNodeRemoval(vNode);
|
||
}
|
||
};
|
||
exitAnimation(domNode, removeDomNode, vNode.properties);
|
||
return;
|
||
}
|
||
}
|
||
if (domNode.parentNode) {
|
||
domNode.parentNode.removeChild(domNode);
|
||
scheduleNodeRemoval(vNode);
|
||
}
|
||
};
|
||
var setProperties = function (domNode, properties, projectionOptions) {
|
||
if (!properties) {
|
||
return;
|
||
}
|
||
var eventHandlerInterceptor = projectionOptions.eventHandlerInterceptor;
|
||
var propNames = Object.keys(properties);
|
||
var propCount = propNames.length;
|
||
var _loop_1 = function (i) {
|
||
var propName = propNames[i];
|
||
var propValue = properties[propName];
|
||
if (propName === 'className') {
|
||
throw new Error('Property "className" is not supported, use "class".');
|
||
}
|
||
else if (propName === 'class') {
|
||
toggleClasses(domNode, propValue, true);
|
||
}
|
||
else if (propName === 'classes') {
|
||
// object with string keys and boolean values
|
||
var classNames = Object.keys(propValue);
|
||
var classNameCount = classNames.length;
|
||
for (var j = 0; j < classNameCount; j++) {
|
||
var className = classNames[j];
|
||
if (propValue[className]) {
|
||
domNode.classList.add(className);
|
||
}
|
||
}
|
||
}
|
||
else if (propName === 'styles') {
|
||
// object with string keys and string (!) values
|
||
var styleNames = Object.keys(propValue);
|
||
var styleCount = styleNames.length;
|
||
for (var j = 0; j < styleCount; j++) {
|
||
var styleName = styleNames[j];
|
||
var styleValue = propValue[styleName];
|
||
if (styleValue) {
|
||
checkStyleValue(styleValue);
|
||
projectionOptions.styleApplyer(domNode, styleName, styleValue);
|
||
}
|
||
}
|
||
}
|
||
else if (propName !== 'key' && propValue !== null && propValue !== undefined) {
|
||
var type = typeof propValue;
|
||
if (type === 'function') {
|
||
if (propName.lastIndexOf('on', 0) === 0) { // lastIndexOf(,0)===0 -> startsWith
|
||
if (eventHandlerInterceptor) {
|
||
propValue = eventHandlerInterceptor(propName, propValue, domNode, properties); // intercept eventhandlers
|
||
}
|
||
if (propName === 'oninput') {
|
||
/* tslint:disable no-this-keyword no-invalid-this only-arrow-functions no-void-expression */
|
||
(function () {
|
||
// record the evt.target.value, because IE and Edge sometimes do a requestAnimationFrame between changing value and running oninput
|
||
var oldPropValue = propValue;
|
||
propValue = function (evt) {
|
||
oldPropValue.apply(this, [evt]);
|
||
evt.target['oninput-value'] = evt.target.value; // may be HTMLTextAreaElement as well
|
||
};
|
||
}());
|
||
/* tslint:enable */
|
||
}
|
||
domNode[propName] = propValue;
|
||
}
|
||
}
|
||
else if (projectionOptions.namespace === NAMESPACE_SVG) {
|
||
if (propName === 'href') {
|
||
domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
|
||
}
|
||
else {
|
||
// all SVG attributes are read-only in DOM, so...
|
||
domNode.setAttribute(propName, propValue);
|
||
}
|
||
}
|
||
else if (type === 'string' && propName !== 'value' && propName !== 'innerHTML') {
|
||
domNode.setAttribute(propName, propValue);
|
||
}
|
||
else {
|
||
domNode[propName] = propValue;
|
||
}
|
||
}
|
||
};
|
||
for (var i = 0; i < propCount; i++) {
|
||
_loop_1(i);
|
||
}
|
||
};
|
||
var addChildren = function (domNode, children, projectionOptions) {
|
||
if (!children) {
|
||
return;
|
||
}
|
||
for (var _i = 0, children_1 = children; _i < children_1.length; _i++) {
|
||
var child = children_1[_i];
|
||
createDom(child, domNode, undefined, projectionOptions);
|
||
}
|
||
};
|
||
var initPropertiesAndChildren = function (domNode, vnode, projectionOptions) {
|
||
addChildren(domNode, vnode.children, projectionOptions); // children before properties, needed for value property of <select>.
|
||
if (vnode.text) {
|
||
domNode.textContent = vnode.text;
|
||
}
|
||
setProperties(domNode, vnode.properties, projectionOptions);
|
||
if (vnode.properties && vnode.properties.afterCreate) {
|
||
vnode.properties.afterCreate.apply(vnode.properties.bind || vnode.properties, [domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children]);
|
||
}
|
||
};
|
||
var createDom = function (vnode, parentNode, insertBefore, projectionOptions) {
|
||
var domNode;
|
||
var start = 0;
|
||
var vnodeSelector = vnode.vnodeSelector;
|
||
var doc = parentNode.ownerDocument;
|
||
if (vnodeSelector === '') {
|
||
domNode = vnode.domNode = doc.createTextNode(vnode.text);
|
||
if (insertBefore !== undefined) {
|
||
parentNode.insertBefore(domNode, insertBefore);
|
||
}
|
||
else {
|
||
parentNode.appendChild(domNode);
|
||
}
|
||
}
|
||
else {
|
||
for (var i = 0; i <= vnodeSelector.length; ++i) {
|
||
var c = vnodeSelector.charAt(i);
|
||
if (i === vnodeSelector.length || c === '.' || c === '#') {
|
||
var type = vnodeSelector.charAt(start - 1);
|
||
var found = vnodeSelector.slice(start, i);
|
||
if (type === '.') {
|
||
domNode.classList.add(found);
|
||
}
|
||
else if (type === '#') {
|
||
domNode.id = found;
|
||
}
|
||
else {
|
||
if (found === 'svg') {
|
||
projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
|
||
}
|
||
if (projectionOptions.namespace !== undefined) {
|
||
domNode = vnode.domNode = doc.createElementNS(projectionOptions.namespace, found);
|
||
}
|
||
else {
|
||
domNode = vnode.domNode = (vnode.domNode || doc.createElement(found));
|
||
if (found === 'input' && vnode.properties && vnode.properties.type !== undefined) {
|
||
// IE8 and older don't support setting input type after the DOM Node has been added to the document
|
||
domNode.setAttribute('type', vnode.properties.type);
|
||
}
|
||
}
|
||
if (insertBefore !== undefined) {
|
||
parentNode.insertBefore(domNode, insertBefore);
|
||
}
|
||
else if (domNode.parentNode !== parentNode) {
|
||
parentNode.appendChild(domNode);
|
||
}
|
||
}
|
||
start = i + 1;
|
||
}
|
||
}
|
||
initPropertiesAndChildren(domNode, vnode, projectionOptions);
|
||
}
|
||
};
|
||
var updateDom;
|
||
/**
|
||
* Adds or removes classes from an Element
|
||
* @param domNode the element
|
||
* @param classes a string separated list of classes
|
||
* @param on true means add classes, false means remove
|
||
*/
|
||
var toggleClasses = function (domNode, classes, on) {
|
||
if (!classes) {
|
||
return;
|
||
}
|
||
classes.split(' ').forEach(function (classToToggle) {
|
||
if (classToToggle) {
|
||
domNode.classList.toggle(classToToggle, on);
|
||
}
|
||
});
|
||
};
|
||
var updateProperties = function (domNode, previousProperties, properties, projectionOptions) {
|
||
if (!properties) {
|
||
return;
|
||
}
|
||
var propertiesUpdated = false;
|
||
var propNames = Object.keys(properties);
|
||
var propCount = propNames.length;
|
||
for (var i = 0; i < propCount; i++) {
|
||
var propName = propNames[i];
|
||
// assuming that properties will be nullified instead of missing is by design
|
||
var propValue = properties[propName];
|
||
var previousValue = previousProperties[propName];
|
||
if (propName === 'class') {
|
||
if (previousValue !== propValue) {
|
||
toggleClasses(domNode, previousValue, false);
|
||
toggleClasses(domNode, propValue, true);
|
||
}
|
||
}
|
||
else if (propName === 'classes') {
|
||
var classList = domNode.classList;
|
||
var classNames = Object.keys(propValue);
|
||
var classNameCount = classNames.length;
|
||
for (var j = 0; j < classNameCount; j++) {
|
||
var className = classNames[j];
|
||
var on = !!propValue[className];
|
||
var previousOn = !!previousValue[className];
|
||
if (on === previousOn) {
|
||
continue;
|
||
}
|
||
propertiesUpdated = true;
|
||
if (on) {
|
||
classList.add(className);
|
||
}
|
||
else {
|
||
classList.remove(className);
|
||
}
|
||
}
|
||
}
|
||
else if (propName === 'styles') {
|
||
var styleNames = Object.keys(propValue);
|
||
var styleCount = styleNames.length;
|
||
for (var j = 0; j < styleCount; j++) {
|
||
var styleName = styleNames[j];
|
||
var newStyleValue = propValue[styleName];
|
||
var oldStyleValue = previousValue[styleName];
|
||
if (newStyleValue === oldStyleValue) {
|
||
continue;
|
||
}
|
||
propertiesUpdated = true;
|
||
if (newStyleValue) {
|
||
checkStyleValue(newStyleValue);
|
||
projectionOptions.styleApplyer(domNode, styleName, newStyleValue);
|
||
}
|
||
else {
|
||
projectionOptions.styleApplyer(domNode, styleName, '');
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
if (!propValue && typeof previousValue === 'string') {
|
||
propValue = '';
|
||
}
|
||
if (propName === 'value') { // value can be manipulated by the user directly and using event.preventDefault() is not an option
|
||
var domValue = domNode[propName];
|
||
if (domValue !== propValue // The 'value' in the DOM tree !== newValue
|
||
&& (domNode['oninput-value']
|
||
? domValue === domNode['oninput-value'] // If the last reported value to 'oninput' does not match domValue, do nothing and wait for oninput
|
||
: propValue !== previousValue // Only update the value if the vdom changed
|
||
)) {
|
||
// The edge cases are described in the tests
|
||
domNode[propName] = propValue; // Reset the value, even if the virtual DOM did not change
|
||
domNode['oninput-value'] = undefined;
|
||
} // else do not update the domNode, otherwise the cursor position would be changed
|
||
if (propValue !== previousValue) {
|
||
propertiesUpdated = true;
|
||
}
|
||
}
|
||
else if (propValue !== previousValue) {
|
||
var type = typeof propValue;
|
||
if (type !== 'function' || !projectionOptions.eventHandlerInterceptor) { // Function updates are expected to be handled by the EventHandlerInterceptor
|
||
if (projectionOptions.namespace === NAMESPACE_SVG) {
|
||
if (propName === 'href') {
|
||
domNode.setAttributeNS(NAMESPACE_XLINK, propName, propValue);
|
||
}
|
||
else {
|
||
// all SVG attributes are read-only in DOM, so...
|
||
domNode.setAttribute(propName, propValue);
|
||
}
|
||
}
|
||
else if (type === 'string' && propName !== 'innerHTML') {
|
||
if (propName === 'role' && propValue === '') {
|
||
domNode.removeAttribute(propName);
|
||
}
|
||
else {
|
||
domNode.setAttribute(propName, propValue);
|
||
}
|
||
}
|
||
else if (domNode[propName] !== propValue) { // Comparison is here for side-effects in Edge with scrollLeft and scrollTop
|
||
domNode[propName] = propValue;
|
||
}
|
||
propertiesUpdated = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return propertiesUpdated;
|
||
};
|
||
var updateChildren = function (vnode, domNode, oldChildren, newChildren, projectionOptions) {
|
||
if (oldChildren === newChildren) {
|
||
return false;
|
||
}
|
||
oldChildren = oldChildren || emptyArray;
|
||
newChildren = newChildren || emptyArray;
|
||
var oldChildrenLength = oldChildren.length;
|
||
var newChildrenLength = newChildren.length;
|
||
var oldIndex = 0;
|
||
var newIndex = 0;
|
||
var i;
|
||
var textUpdated = false;
|
||
while (newIndex < newChildrenLength) {
|
||
var oldChild = (oldIndex < oldChildrenLength) ? oldChildren[oldIndex] : undefined;
|
||
var newChild = newChildren[newIndex];
|
||
if (oldChild !== undefined && same(oldChild, newChild)) {
|
||
textUpdated = updateDom(oldChild, newChild, projectionOptions) || textUpdated;
|
||
oldIndex++;
|
||
}
|
||
else {
|
||
var findOldIndex = findIndexOfChild(oldChildren, newChild, oldIndex + 1);
|
||
if (findOldIndex >= 0) {
|
||
// Remove preceding missing children
|
||
for (i = oldIndex; i < findOldIndex; i++) {
|
||
nodeToRemove(oldChildren[i]);
|
||
checkDistinguishable(oldChildren, i, vnode, 'removed');
|
||
}
|
||
textUpdated = updateDom(oldChildren[findOldIndex], newChild, projectionOptions) || textUpdated;
|
||
oldIndex = findOldIndex + 1;
|
||
}
|
||
else {
|
||
// New child
|
||
createDom(newChild, domNode, (oldIndex < oldChildrenLength) ? oldChildren[oldIndex].domNode : undefined, projectionOptions);
|
||
nodeAdded(newChild);
|
||
checkDistinguishable(newChildren, newIndex, vnode, 'added');
|
||
}
|
||
}
|
||
newIndex++;
|
||
}
|
||
if (oldChildrenLength > oldIndex) {
|
||
// Remove child fragments
|
||
for (i = oldIndex; i < oldChildrenLength; i++) {
|
||
nodeToRemove(oldChildren[i]);
|
||
checkDistinguishable(oldChildren, i, vnode, 'removed');
|
||
}
|
||
}
|
||
return textUpdated;
|
||
};
|
||
updateDom = function (previous, vnode, projectionOptions) {
|
||
var domNode = previous.domNode;
|
||
var textUpdated = false;
|
||
if (previous === vnode) {
|
||
return false; // By contract, VNode objects may not be modified anymore after passing them to maquette
|
||
}
|
||
var updated = false;
|
||
if (vnode.vnodeSelector === '') {
|
||
if (vnode.text !== previous.text) {
|
||
var newTextNode = domNode.ownerDocument.createTextNode(vnode.text);
|
||
domNode.parentNode.replaceChild(newTextNode, domNode);
|
||
vnode.domNode = newTextNode;
|
||
textUpdated = true;
|
||
return textUpdated;
|
||
}
|
||
vnode.domNode = domNode;
|
||
}
|
||
else {
|
||
if (vnode.vnodeSelector.lastIndexOf('svg', 0) === 0) { // lastIndexOf(needle,0)===0 means StartsWith
|
||
projectionOptions = extend(projectionOptions, { namespace: NAMESPACE_SVG });
|
||
}
|
||
if (previous.text !== vnode.text) {
|
||
updated = true;
|
||
if (vnode.text === undefined) {
|
||
domNode.removeChild(domNode.firstChild); // the only textnode presumably
|
||
}
|
||
else {
|
||
domNode.textContent = vnode.text;
|
||
}
|
||
}
|
||
vnode.domNode = domNode;
|
||
updated = updateChildren(vnode, domNode, previous.children, vnode.children, projectionOptions) || updated;
|
||
updated = updateProperties(domNode, previous.properties, vnode.properties, projectionOptions) || updated;
|
||
if (vnode.properties && vnode.properties.afterUpdate) {
|
||
vnode.properties.afterUpdate.apply(vnode.properties.bind || vnode.properties, [domNode, projectionOptions, vnode.vnodeSelector, vnode.properties, vnode.children]);
|
||
}
|
||
}
|
||
if (updated && vnode.properties && vnode.properties.updateAnimation) {
|
||
vnode.properties.updateAnimation(domNode, vnode.properties, previous.properties);
|
||
}
|
||
return textUpdated;
|
||
};
|
||
var createProjection = function (vnode, projectionOptions) {
|
||
return {
|
||
getLastRender: function () { return vnode; },
|
||
update: function (updatedVnode) {
|
||
if (vnode.vnodeSelector !== updatedVnode.vnodeSelector) {
|
||
throw new Error('The selector for the root VNode may not be changed. (consider using dom.merge and add one extra level to the virtual DOM)');
|
||
}
|
||
var previousVNode = vnode;
|
||
vnode = updatedVnode;
|
||
updateDom(previousVNode, updatedVnode, projectionOptions);
|
||
},
|
||
domNode: vnode.domNode
|
||
};
|
||
};
|
||
|
||
var DEFAULT_PROJECTION_OPTIONS = {
|
||
namespace: undefined,
|
||
performanceLogger: function () { return undefined; },
|
||
eventHandlerInterceptor: undefined,
|
||
styleApplyer: function (domNode, styleName, value) {
|
||
// Provides a hook to add vendor prefixes for browsers that still need it.
|
||
domNode.style[styleName] = value;
|
||
}
|
||
};
|
||
var applyDefaultProjectionOptions = function (projectorOptions) {
|
||
return extend(DEFAULT_PROJECTION_OPTIONS, projectorOptions);
|
||
};
|
||
var dom = {
|
||
/**
|
||
* Creates a real DOM tree from `vnode`. The [[Projection]] object returned will contain the resulting DOM Node in
|
||
* its [[Projection.domNode|domNode]] property.
|
||
* This is a low-level method. Users will typically use a [[Projector]] instead.
|
||
* @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
|
||
* objects may only be rendered once.
|
||
* @param projectionOptions - Options to be used to create and update the projection.
|
||
* @returns The [[Projection]] which also contains the DOM Node that was created.
|
||
*/
|
||
create: function (vnode, projectionOptions) {
|
||
projectionOptions = applyDefaultProjectionOptions(projectionOptions);
|
||
createDom(vnode, document.createElement('div'), undefined, projectionOptions);
|
||
return createProjection(vnode, projectionOptions);
|
||
},
|
||
/**
|
||
* Appends a new child node to the DOM which is generated from a [[VNode]].
|
||
* This is a low-level method. Users will typically use a [[Projector]] instead.
|
||
* @param parentNode - The parent node for the new child node.
|
||
* @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
|
||
* objects may only be rendered once.
|
||
* @param projectionOptions - Options to be used to create and update the [[Projection]].
|
||
* @returns The [[Projection]] that was created.
|
||
*/
|
||
append: function (parentNode, vnode, projectionOptions) {
|
||
projectionOptions = applyDefaultProjectionOptions(projectionOptions);
|
||
createDom(vnode, parentNode, undefined, projectionOptions);
|
||
return createProjection(vnode, projectionOptions);
|
||
},
|
||
/**
|
||
* Inserts a new DOM node which is generated from a [[VNode]].
|
||
* This is a low-level method. Users wil typically use a [[Projector]] instead.
|
||
* @param beforeNode - The node that the DOM Node is inserted before.
|
||
* @param vnode - The root of the virtual DOM tree that was created using the [[h]] function.
|
||
* NOTE: [[VNode]] objects may only be rendered once.
|
||
* @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
|
||
* @returns The [[Projection]] that was created.
|
||
*/
|
||
insertBefore: function (beforeNode, vnode, projectionOptions) {
|
||
projectionOptions = applyDefaultProjectionOptions(projectionOptions);
|
||
createDom(vnode, beforeNode.parentNode, beforeNode, projectionOptions);
|
||
return createProjection(vnode, projectionOptions);
|
||
},
|
||
/**
|
||
* Merges a new DOM node which is generated from a [[VNode]] with an existing DOM Node.
|
||
* This means that the virtual DOM and the real DOM will have one overlapping element.
|
||
* Therefore the selector for the root [[VNode]] will be ignored, but its properties and children will be applied to the Element provided.
|
||
* This is a low-level method. Users wil typically use a [[Projector]] instead.
|
||
* @param element - The existing element to adopt as the root of the new virtual DOM. Existing attributes and child nodes are preserved.
|
||
* @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]] objects
|
||
* may only be rendered once.
|
||
* @param projectionOptions - Options to be used to create and update the projection, see [[createProjector]].
|
||
* @returns The [[Projection]] that was created.
|
||
*/
|
||
merge: function (element, vnode, projectionOptions) {
|
||
projectionOptions = applyDefaultProjectionOptions(projectionOptions);
|
||
vnode.domNode = element;
|
||
initPropertiesAndChildren(element, vnode, projectionOptions);
|
||
return createProjection(vnode, projectionOptions);
|
||
},
|
||
/**
|
||
* Replaces an existing DOM node with a node generated from a [[VNode]].
|
||
* This is a low-level method. Users will typically use a [[Projector]] instead.
|
||
* @param element - The node for the [[VNode]] to replace.
|
||
* @param vnode - The root of the virtual DOM tree that was created using the [[h]] function. NOTE: [[VNode]]
|
||
* objects may only be rendered once.
|
||
* @param projectionOptions - Options to be used to create and update the [[Projection]].
|
||
* @returns The [[Projection]] that was created.
|
||
*/
|
||
replace: function (element, vnode, projectionOptions) {
|
||
projectionOptions = applyDefaultProjectionOptions(projectionOptions);
|
||
createDom(vnode, element.parentNode, element, projectionOptions);
|
||
element.parentNode.removeChild(element);
|
||
return createProjection(vnode, projectionOptions);
|
||
}
|
||
};
|
||
|
||
/* tslint:disable function-name */
|
||
var toTextVNode = function (data) {
|
||
return {
|
||
vnodeSelector: '',
|
||
properties: undefined,
|
||
children: undefined,
|
||
text: data.toString(),
|
||
domNode: null
|
||
};
|
||
};
|
||
var appendChildren = function (parentSelector, insertions, main) {
|
||
for (var i = 0, length_1 = insertions.length; i < length_1; i++) {
|
||
var item = insertions[i];
|
||
if (Array.isArray(item)) {
|
||
appendChildren(parentSelector, item, main);
|
||
}
|
||
else {
|
||
if (item !== null && item !== undefined && item !== false) {
|
||
if (typeof item === 'string') {
|
||
item = toTextVNode(item);
|
||
}
|
||
main.push(item);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
function h(selector, properties, children) {
|
||
if (Array.isArray(properties)) {
|
||
children = properties;
|
||
properties = undefined;
|
||
}
|
||
else if ((properties && (typeof properties === 'string' || properties.hasOwnProperty('vnodeSelector'))) ||
|
||
(children && (typeof children === 'string' || children.hasOwnProperty('vnodeSelector')))) {
|
||
throw new Error('h called with invalid arguments');
|
||
}
|
||
var text;
|
||
var flattenedChildren;
|
||
// Recognize a common special case where there is only a single text node
|
||
if (children && children.length === 1 && typeof children[0] === 'string') {
|
||
text = children[0];
|
||
}
|
||
else if (children) {
|
||
flattenedChildren = [];
|
||
appendChildren(selector, children, flattenedChildren);
|
||
if (flattenedChildren.length === 0) {
|
||
flattenedChildren = undefined;
|
||
}
|
||
}
|
||
return {
|
||
vnodeSelector: selector,
|
||
properties: properties,
|
||
children: flattenedChildren,
|
||
text: (text === '') ? undefined : text,
|
||
domNode: null
|
||
};
|
||
}
|
||
|
||
var createParentNodePath = function (node, rootNode) {
|
||
var parentNodePath = [];
|
||
while (node !== rootNode) {
|
||
parentNodePath.push(node);
|
||
node = node.parentNode;
|
||
}
|
||
return parentNodePath;
|
||
};
|
||
var find;
|
||
if (Array.prototype.find) {
|
||
find = function (items, predicate) { return items.find(predicate); };
|
||
}
|
||
else {
|
||
find = function (items, predicate) { return items.filter(predicate)[0]; };
|
||
}
|
||
var findVNodeByParentNodePath = function (vnode, parentNodePath) {
|
||
var result = vnode;
|
||
parentNodePath.forEach(function (node) {
|
||
result = (result && result.children) ? find(result.children, function (child) { return child.domNode === node; }) : undefined;
|
||
});
|
||
return result;
|
||
};
|
||
var createEventHandlerInterceptor = function (projector, getProjection, performanceLogger) {
|
||
var modifiedEventHandler = function (evt) {
|
||
performanceLogger('domEvent', evt);
|
||
var projection = getProjection();
|
||
var parentNodePath = createParentNodePath(evt.currentTarget, projection.domNode);
|
||
parentNodePath.reverse();
|
||
var matchingVNode = findVNodeByParentNodePath(projection.getLastRender(), parentNodePath);
|
||
projector.scheduleRender();
|
||
var result;
|
||
if (matchingVNode) {
|
||
/* tslint:disable no-invalid-this */
|
||
result = matchingVNode.properties["on" + evt.type].apply(matchingVNode.properties.bind || this, arguments);
|
||
/* tslint:enable no-invalid-this */
|
||
}
|
||
performanceLogger('domEventProcessed', evt);
|
||
return result;
|
||
};
|
||
return function (propertyName, eventHandler, domNode, properties) { return modifiedEventHandler; };
|
||
};
|
||
/**
|
||
* Creates a [[Projector]] instance using the provided projectionOptions.
|
||
*
|
||
* For more information, see [[Projector]].
|
||
*
|
||
* @param projectorOptions Options that influence how the DOM is rendered and updated.
|
||
*/
|
||
var createProjector = function (projectorOptions) {
|
||
var projector;
|
||
var projectionOptions = applyDefaultProjectionOptions(projectorOptions);
|
||
var performanceLogger = projectionOptions.performanceLogger;
|
||
var renderCompleted = true;
|
||
var scheduled;
|
||
var stopped = false;
|
||
var projections = [];
|
||
var renderFunctions = []; // matches the projections array
|
||
var addProjection = function (
|
||
/* one of: dom.append, dom.insertBefore, dom.replace, dom.merge */
|
||
domFunction,
|
||
/* the parameter of the domFunction */
|
||
node, renderFunction) {
|
||
var projection;
|
||
var getProjection = function () { return projection; };
|
||
projectionOptions.eventHandlerInterceptor = createEventHandlerInterceptor(projector, getProjection, performanceLogger);
|
||
projection = domFunction(node, renderFunction(), projectionOptions);
|
||
projections.push(projection);
|
||
renderFunctions.push(renderFunction);
|
||
};
|
||
var doRender = function () {
|
||
scheduled = undefined;
|
||
if (!renderCompleted) {
|
||
return; // The last render threw an error, it should have been logged in the browser console.
|
||
}
|
||
renderCompleted = false;
|
||
performanceLogger('renderStart', undefined);
|
||
for (var i = 0; i < projections.length; i++) {
|
||
var updatedVnode = renderFunctions[i]();
|
||
performanceLogger('rendered', undefined);
|
||
projections[i].update(updatedVnode);
|
||
performanceLogger('patched', undefined);
|
||
}
|
||
performanceLogger('renderDone', undefined);
|
||
renderCompleted = true;
|
||
};
|
||
projector = {
|
||
renderNow: doRender,
|
||
scheduleRender: function () {
|
||
if (!scheduled && !stopped) {
|
||
scheduled = requestAnimationFrame(doRender);
|
||
}
|
||
},
|
||
stop: function () {
|
||
if (scheduled) {
|
||
cancelAnimationFrame(scheduled);
|
||
scheduled = undefined;
|
||
}
|
||
stopped = true;
|
||
},
|
||
resume: function () {
|
||
stopped = false;
|
||
renderCompleted = true;
|
||
projector.scheduleRender();
|
||
},
|
||
append: function (parentNode, renderFunction) {
|
||
addProjection(dom.append, parentNode, renderFunction);
|
||
},
|
||
insertBefore: function (beforeNode, renderFunction) {
|
||
addProjection(dom.insertBefore, beforeNode, renderFunction);
|
||
},
|
||
merge: function (domNode, renderFunction) {
|
||
addProjection(dom.merge, domNode, renderFunction);
|
||
},
|
||
replace: function (domNode, renderFunction) {
|
||
addProjection(dom.replace, domNode, renderFunction);
|
||
},
|
||
detach: function (renderFunction) {
|
||
for (var i = 0; i < renderFunctions.length; i++) {
|
||
if (renderFunctions[i] === renderFunction) {
|
||
renderFunctions.splice(i, 1);
|
||
return projections.splice(i, 1)[0];
|
||
}
|
||
}
|
||
throw new Error('renderFunction was not found');
|
||
}
|
||
};
|
||
return projector;
|
||
};
|
||
|
||
/**
|
||
* Creates a [[CalculationCache]] object, useful for caching [[VNode]] trees.
|
||
* In practice, caching of [[VNode]] trees is not needed, because achieving 60 frames per second is almost never a problem.
|
||
* For more information, see [[CalculationCache]].
|
||
*
|
||
* @param <Result> The type of the value that is cached.
|
||
*/
|
||
var createCache = function () {
|
||
var cachedInputs;
|
||
var cachedOutcome;
|
||
return {
|
||
invalidate: function () {
|
||
cachedOutcome = undefined;
|
||
cachedInputs = undefined;
|
||
},
|
||
result: function (inputs, calculation) {
|
||
if (cachedInputs) {
|
||
for (var i = 0; i < inputs.length; i++) {
|
||
if (cachedInputs[i] !== inputs[i]) {
|
||
cachedOutcome = undefined;
|
||
}
|
||
}
|
||
}
|
||
if (!cachedOutcome) {
|
||
cachedOutcome = calculation();
|
||
cachedInputs = inputs;
|
||
}
|
||
return cachedOutcome;
|
||
}
|
||
};
|
||
};
|
||
|
||
/**
|
||
* Creates a {@link Mapping} instance that keeps an array of result objects synchronized with an array of source objects.
|
||
* See {@link http://maquettejs.org/docs/arrays.html|Working with arrays}.
|
||
*
|
||
* @param <Source> The type of source items. A database-record for instance.
|
||
* @param <Target> The type of target items. A [[MaquetteComponent]] for instance.
|
||
* @param getSourceKey `function(source)` that must return a key to identify each source object. The result must either be a string or a number.
|
||
* @param createResult `function(source, index)` that must create a new result object from a given source. This function is identical
|
||
* to the `callback` argument in `Array.map(callback)`.
|
||
* @param updateResult `function(source, target, index)` that updates a result to an updated source.
|
||
*/
|
||
var createMapping = function (getSourceKey, createResult, updateResult) {
|
||
var keys = [];
|
||
var results = [];
|
||
return {
|
||
results: results,
|
||
map: function (newSources) {
|
||
var newKeys = newSources.map(getSourceKey);
|
||
var oldTargets = results.slice();
|
||
var oldIndex = 0;
|
||
for (var i = 0; i < newSources.length; i++) {
|
||
var source = newSources[i];
|
||
var sourceKey = newKeys[i];
|
||
if (sourceKey === keys[oldIndex]) {
|
||
results[i] = oldTargets[oldIndex];
|
||
updateResult(source, oldTargets[oldIndex], i);
|
||
oldIndex++;
|
||
}
|
||
else {
|
||
var found = false;
|
||
for (var j = 1; j < keys.length + 1; j++) {
|
||
var searchIndex = (oldIndex + j) % keys.length;
|
||
if (keys[searchIndex] === sourceKey) {
|
||
results[i] = oldTargets[searchIndex];
|
||
updateResult(newSources[i], oldTargets[searchIndex], i);
|
||
oldIndex = searchIndex + 1;
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!found) {
|
||
results[i] = createResult(source, i);
|
||
}
|
||
}
|
||
}
|
||
results.length = newSources.length;
|
||
keys = newKeys;
|
||
}
|
||
};
|
||
};
|
||
|
||
exports.createCache = createCache;
|
||
exports.createMapping = createMapping;
|
||
exports.createProjector = createProjector;
|
||
exports.dom = dom;
|
||
exports.h = h;
|
||
|
||
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
||
}));
|
||
|
||
},{}]},{},[3]);
|