Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
058ee2691f | ||
|
|
f0cf7f4956 | ||
|
|
75c8720b04 | ||
|
|
f73f2f670f | ||
|
|
c6e62e1cfa | ||
|
|
67956d9a42 | ||
|
|
64b3ca6066 | ||
|
|
8349f7da9b | ||
|
|
0636042878 | ||
|
|
6207ffa516 | ||
|
|
9d33612054 | ||
|
|
face6387a0 | ||
|
|
b308595dac | ||
|
|
1d2de197a4 | ||
|
|
a3d790b47d | ||
|
|
efe6dd87db | ||
|
|
5b33a6e783 | ||
|
|
8730fab40b | ||
|
|
992776bb40 | ||
|
|
bc9ec4421c | ||
|
|
4c31ab601c | ||
|
|
c4e77c7eae | ||
|
|
2cd6e27f70 | ||
|
|
f6b2dde479 | ||
|
|
44da62a962 | ||
|
|
4e96047e90 | ||
|
|
212986ac37 | ||
|
|
877f516565 | ||
|
|
750022f0f1 | ||
|
|
00a050d337 |
@@ -3,9 +3,8 @@
|
||||
# 'meteor add' and 'meteor remove' will edit this file for you,
|
||||
# but you can also edit it by hand.
|
||||
|
||||
iron:router
|
||||
accounts-password@1.4.0
|
||||
accounts-ui@1.1.9
|
||||
accounts-password@1.5.0
|
||||
accounts-ui@1.2.0
|
||||
random@1.0.10
|
||||
dburles:collection-helpers
|
||||
reactive-var@1.0.11
|
||||
@@ -18,37 +17,40 @@ dburles:mongo-collection-instances
|
||||
percolate:migrations
|
||||
ecwyne:mathjs
|
||||
useraccounts:polymer
|
||||
accounts-google@1.2.0
|
||||
accounts-google@1.3.0
|
||||
splendido:accounts-meld
|
||||
email@1.2.3
|
||||
meteorhacks:subs-manager
|
||||
chuangbo:marked
|
||||
reywood:iron-router-ga
|
||||
meteor-base@1.1.0
|
||||
mobile-experience@1.0.4
|
||||
mongo@1.2.0
|
||||
meteor-base@1.2.0
|
||||
mobile-experience@1.0.5
|
||||
mongo@1.3.1
|
||||
blaze-html-templates
|
||||
session@1.1.7
|
||||
jquery@1.11.10
|
||||
tracker@1.1.3
|
||||
logging@1.1.17
|
||||
logging@1.1.19
|
||||
reload@1.1.11
|
||||
ejson@1.0.14
|
||||
ejson@1.1.0
|
||||
spacebars
|
||||
check@1.2.5
|
||||
useraccounts:iron-routing
|
||||
wizonesolutions:canonical
|
||||
standard-minifier-js@2.1.1
|
||||
shell-server@0.2.4
|
||||
standard-minifier-js@2.2.0
|
||||
shell-server@0.3.0
|
||||
seba:minifiers-autoprefixer
|
||||
nikogosovd:multiple-uihooks
|
||||
templates:array
|
||||
ecmascript@0.8.2
|
||||
ecmascript@0.9.0
|
||||
es5-shim@4.6.15
|
||||
differential:vulcanize
|
||||
reactive-dict@1.1.9
|
||||
reactive-dict@1.2.0
|
||||
percolate:synced-cron
|
||||
ongoworks:speakingurl
|
||||
service-configuration@1.0.11
|
||||
google-config-ui
|
||||
dynamic-import
|
||||
google-config-ui@1.0.0
|
||||
dynamic-import@0.2.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
rate-limit@1.0.8
|
||||
iron:router
|
||||
|
||||
@@ -1 +1 @@
|
||||
METEOR@1.5.2
|
||||
METEOR@1.6.0.1
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
accounts-base@1.3.3
|
||||
accounts-google@1.2.0
|
||||
accounts-base@1.4.2
|
||||
accounts-google@1.3.1
|
||||
accounts-oauth@1.1.15
|
||||
accounts-password@1.4.0
|
||||
accounts-ui@1.1.9
|
||||
accounts-ui-unstyled@1.2.1
|
||||
accounts-password@1.5.0
|
||||
accounts-ui@1.2.0
|
||||
accounts-ui-unstyled@1.3.0
|
||||
aldeed:collection2@2.10.0
|
||||
aldeed:collection2-core@1.2.0
|
||||
aldeed:schema-deny@1.1.0
|
||||
aldeed:schema-index@1.1.1
|
||||
aldeed:simple-schema@1.5.3
|
||||
allow-deny@1.0.9
|
||||
allow-deny@1.1.0
|
||||
autoupdate@1.3.12
|
||||
babel-compiler@6.20.0
|
||||
babel-runtime@1.0.1
|
||||
babel-compiler@6.24.7
|
||||
babel-runtime@1.1.1
|
||||
base64@1.0.10
|
||||
binary-heap@1.0.10
|
||||
blaze@2.3.2
|
||||
blaze-html-templates@1.1.2
|
||||
blaze-tools@1.0.10
|
||||
boilerplate-generator@1.2.0
|
||||
caching-compiler@1.1.9
|
||||
boilerplate-generator@1.3.1
|
||||
caching-compiler@1.1.11
|
||||
caching-html-compiler@1.1.2
|
||||
callback-hook@1.0.10
|
||||
check@1.2.5
|
||||
@@ -27,31 +27,30 @@ chuangbo:marked@0.3.5_1
|
||||
coffeescript@1.11.1_4
|
||||
dburles:collection-helpers@1.1.0
|
||||
dburles:mongo-collection-instances@0.3.5
|
||||
ddp@1.3.1
|
||||
ddp-client@2.1.3
|
||||
ddp-common@1.2.9
|
||||
ddp@1.4.0
|
||||
ddp-client@2.2.0
|
||||
ddp-common@1.3.0
|
||||
ddp-rate-limiter@1.0.7
|
||||
ddp-server@2.0.2
|
||||
ddp-server@2.1.1
|
||||
deps@1.0.12
|
||||
diff-sequence@1.0.7
|
||||
differential:vulcanize@3.0.0
|
||||
dynamic-import@0.1.3
|
||||
ecmascript@0.8.2
|
||||
ecmascript-runtime@0.4.1
|
||||
ecmascript-runtime-client@0.4.3
|
||||
ecmascript-runtime-server@0.4.1
|
||||
dynamic-import@0.2.1
|
||||
ecmascript@0.9.0
|
||||
ecmascript-runtime@0.5.0
|
||||
ecmascript-runtime-client@0.5.0
|
||||
ecmascript-runtime-server@0.5.0
|
||||
ecwyne:mathjs@0.25.0
|
||||
ejson@1.0.14
|
||||
ejson@1.1.0
|
||||
email@1.2.3
|
||||
es5-shim@4.6.15
|
||||
fastclick@1.0.13
|
||||
geojson-utils@1.0.10
|
||||
google-config-ui@1.0.0
|
||||
google-oauth@1.2.4
|
||||
google-oauth@1.2.5
|
||||
hot-code-push@1.0.4
|
||||
html-tools@1.0.11
|
||||
htmljs@1.0.11
|
||||
http@1.2.12
|
||||
http@1.3.0
|
||||
id-map@1.0.9
|
||||
iron:controller@1.0.12
|
||||
iron:core@1.0.11
|
||||
@@ -64,41 +63,41 @@ iron:url@1.1.0
|
||||
jquery@1.11.10
|
||||
lai:collection-extensions@0.2.1_1
|
||||
launch-screen@1.1.1
|
||||
less@2.7.9
|
||||
less@2.7.12
|
||||
livedata@1.0.18
|
||||
localstorage@1.1.1
|
||||
logging@1.1.17
|
||||
localstorage@1.2.0
|
||||
logging@1.1.19
|
||||
matb33:collection-hooks@0.8.4
|
||||
mdg:validation-error@0.5.1
|
||||
meteor@1.7.2
|
||||
meteor-base@1.1.0
|
||||
meteor@1.8.2
|
||||
meteor-base@1.2.0
|
||||
meteorhacks:subs-manager@1.6.4
|
||||
minifier-css@1.2.16
|
||||
minifier-js@2.1.3
|
||||
minimongo@1.3.1
|
||||
mobile-experience@1.0.4
|
||||
minifier-js@2.2.2
|
||||
minimongo@1.4.3
|
||||
mobile-experience@1.0.5
|
||||
mobile-status-bar@1.0.14
|
||||
modules@0.10.0
|
||||
modules-runtime@0.8.0
|
||||
momentjs:moment@2.18.1
|
||||
mongo@1.2.2
|
||||
mongo-dev-server@1.0.1
|
||||
modules@0.11.3
|
||||
modules-runtime@0.9.2
|
||||
momentjs:moment@2.20.1
|
||||
mongo@1.3.1
|
||||
mongo-dev-server@1.1.0
|
||||
mongo-id@1.0.6
|
||||
nikogosovd:multiple-uihooks@0.1.8
|
||||
npm-bcrypt@0.9.3
|
||||
npm-mongo@2.2.30
|
||||
oauth@1.1.13
|
||||
oauth2@1.1.11
|
||||
npm-mongo@2.2.34
|
||||
oauth@1.2.1
|
||||
oauth2@1.2.0
|
||||
observe-sequence@1.0.16
|
||||
ongoworks:speakingurl@9.0.0
|
||||
ordered-dict@1.0.9
|
||||
percolate:migrations@0.9.8
|
||||
percolate:synced-cron@1.3.2
|
||||
promise@0.9.0
|
||||
promise@0.10.1
|
||||
raix:eventemitter@0.1.3
|
||||
random@1.0.10
|
||||
rate-limit@1.0.8
|
||||
reactive-dict@1.1.9
|
||||
reactive-dict@1.2.0
|
||||
reactive-var@1.0.11
|
||||
reload@1.1.11
|
||||
retry@1.0.9
|
||||
@@ -108,14 +107,14 @@ seba:minifiers-autoprefixer@1.0.1
|
||||
service-configuration@1.0.11
|
||||
session@1.1.7
|
||||
sha@1.0.9
|
||||
shell-server@0.2.4
|
||||
shell-server@0.3.1
|
||||
softwarerero:accounts-t9n@1.3.11
|
||||
spacebars@1.0.15
|
||||
spacebars-compiler@1.1.3
|
||||
splendido:accounts-emails-field@1.2.0
|
||||
splendido:accounts-meld@1.3.1
|
||||
srp@1.0.10
|
||||
standard-minifier-js@2.1.1
|
||||
standard-minifier-js@2.2.3
|
||||
templates:array@1.0.3
|
||||
templating@1.3.2
|
||||
templating-compiler@1.3.3
|
||||
@@ -128,7 +127,7 @@ url@1.1.0
|
||||
useraccounts:core@1.14.2
|
||||
useraccounts:iron-routing@1.14.2
|
||||
useraccounts:polymer@1.14.2
|
||||
webapp@1.3.19
|
||||
webapp@1.4.0
|
||||
webapp-hashing@1.0.9
|
||||
wizonesolutions:canonical@0.0.5
|
||||
zimme:collection-behaviours@1.1.3
|
||||
|
||||
@@ -164,9 +164,9 @@ Schemas.Character = new SimpleSchema({
|
||||
|
||||
//permissions
|
||||
party: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true},
|
||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id},
|
||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: []},
|
||||
owner: {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||
readers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||
writers: {type: [String], regEx: SimpleSchema.RegEx.Id, defaultValue: [], index: 1},
|
||||
color: {
|
||||
type: String,
|
||||
allowedValues: _.pluck(colorOptions, "key"),
|
||||
@@ -185,11 +185,13 @@ Schemas.Character = new SimpleSchema({
|
||||
type: String,
|
||||
defaultValue: "whitelist",
|
||||
allowedValues: ["whitelist", "public"],
|
||||
index: 1,
|
||||
},
|
||||
"settings.swapStatAndModifier": {type: Boolean, defaultValue: false},
|
||||
"settings.exportFeatures": {type: Boolean, defaultValue: true},
|
||||
"settings.exportAttacks": {type: Boolean, defaultValue: true},
|
||||
"settings.exportDescription": {type: Boolean, defaultValue: true},
|
||||
"settings.newUserExperience": {type: Boolean, optional: true},
|
||||
});
|
||||
|
||||
Characters.attachSchema(Schemas.Character);
|
||||
@@ -297,6 +299,7 @@ Characters.calculate = {
|
||||
var fieldSelector = {};
|
||||
fieldSelector[fieldName] = 1;
|
||||
var char = Characters.findOne(charId, {fields: fieldSelector});
|
||||
if (!char) return;
|
||||
var field = char[fieldName];
|
||||
if (field === undefined){
|
||||
throw new Meteor.Error(
|
||||
@@ -330,6 +333,7 @@ Characters.calculate = {
|
||||
},
|
||||
attributeValue: memoize(function(charId, attributeName){
|
||||
var attribute = Characters.calculate.getField(charId, attributeName);
|
||||
if (!attribute) return;
|
||||
//base value
|
||||
var value = Characters.calculate.attributeBase(charId, attributeName);
|
||||
//plus adjustment
|
||||
@@ -341,6 +345,7 @@ Characters.calculate = {
|
||||
}),
|
||||
skillMod: memoize(preventLoop(function(charId, skillName){
|
||||
var skill = Characters.calculate.getField(charId, skillName);
|
||||
if (!skill) return;
|
||||
//get the final value of the ability score
|
||||
var ability = Characters.calculate.attributeValue(charId, skill.ability);
|
||||
|
||||
@@ -392,7 +397,6 @@ Characters.calculate = {
|
||||
return prof && prof.value || 0;
|
||||
}),
|
||||
passiveSkill: memoize(function(charId, skillName){
|
||||
var skill = Characters.calculate.getField(charId, skillName);
|
||||
var mod = +Characters.calculate.skillMod(charId, skillName);
|
||||
var value = 10 + mod;
|
||||
Effects.find(
|
||||
@@ -553,6 +557,10 @@ if (Meteor.isServer){
|
||||
});
|
||||
Characters.before.insert(function(userId, doc) {
|
||||
doc.urlName = getSlug(doc.name, {maintainCase: true}) || "-";
|
||||
// The first character a user creates should have the new user experience
|
||||
if (!Characters.find({owner: userId}).count()){
|
||||
doc.settings.newUserExperience = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,75 @@
|
||||
Schemas.UserProfile = new SimpleSchema({
|
||||
username: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
Schemas.User = new SimpleSchema({
|
||||
username: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
profile: {
|
||||
type: Schemas.UserProfile,
|
||||
optional: true,
|
||||
},
|
||||
emails: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
},
|
||||
"emails.$": {
|
||||
type: Object,
|
||||
},
|
||||
"emails.$.address": {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Email,
|
||||
},
|
||||
"emails.$.verified": {
|
||||
type: Boolean,
|
||||
},
|
||||
registered_emails: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
},
|
||||
"registered_emails.$": {
|
||||
type: Object,
|
||||
blackbox: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: Date
|
||||
},
|
||||
services: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
roles: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
roles: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
},
|
||||
"roles.$": {
|
||||
type: String
|
||||
},
|
||||
// In order to avoid an 'Exception in setInterval callback' from Meteor
|
||||
heartbeat: {
|
||||
type: Date,
|
||||
optional: true,
|
||||
},
|
||||
apiKey: {
|
||||
type: String,
|
||||
index: 1,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
Meteor.users.attachSchema(Schemas.User);
|
||||
|
||||
Meteor.users.allow({
|
||||
update: function(userId, doc, fields, modifier) {
|
||||
if (
|
||||
@@ -21,3 +93,13 @@ Meteor.users.allow({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (Meteor.isServer) Meteor.methods({
|
||||
generateMyApiKey() {
|
||||
var user = Meteor.users.findOne(this.userId);
|
||||
if (!user) return;
|
||||
if (user && user.apiKey) return;
|
||||
var apiKey = Random.id(30);
|
||||
Meteor.users.update(this.userId, {$set: {apiKey}});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,7 +4,11 @@ Router.map(function() {
|
||||
where: "server",
|
||||
action: function() {
|
||||
this.response.setHeader("Content-Type", "application/json");
|
||||
this.response.end(vMixCharacter(this.params._id));
|
||||
var query = this.params.query;
|
||||
var key = query && query.key;
|
||||
ifKeyValid(key, this.response, () =>
|
||||
this.response.end(vMixCharacter(this.params._id))
|
||||
);
|
||||
},
|
||||
});
|
||||
this.route("vmixParty", {
|
||||
@@ -12,7 +16,38 @@ Router.map(function() {
|
||||
where: "server",
|
||||
action: function() {
|
||||
this.response.setHeader("Content-Type", "application/json");
|
||||
this.response.end(vMixParty(this.params._id));
|
||||
var query = this.params.query;
|
||||
var key = query && query.key;
|
||||
ifKeyValid(key, this.response, () =>
|
||||
this.response.end(vMixParty(this.params._id))
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
var ifKeyValid = function(apiKey, response, callback){
|
||||
if (!apiKey){
|
||||
response.writeHead(403, "You must use an api key to access this api");
|
||||
response.end();
|
||||
} else if (!isKeyValid(apiKey)){
|
||||
response.writeHead(403, "API key is invalid");
|
||||
response.end();
|
||||
} else if (isRateLimited(apiKey)){
|
||||
response.writeHead(429, "Too many requests");
|
||||
response.end();
|
||||
} else {
|
||||
rateLimiter.increment({apiKey})
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
var isKeyValid = function(apiKey){
|
||||
return !!Meteor.users.findOne({apiKey});
|
||||
};
|
||||
|
||||
var rateLimiter = new RateLimiter();
|
||||
rateLimiter.addRule({apiKey: String}, 2, 10000);
|
||||
|
||||
var isRateLimited = function(apiKey){
|
||||
return !rateLimiter.check({apiKey}).allowed;
|
||||
};
|
||||
|
||||
@@ -78,6 +78,35 @@ Router.map(function() {
|
||||
fastRender: true,
|
||||
});
|
||||
|
||||
this.route("printedCharacterSheet", {
|
||||
path: "/character/:_id/:urlName/print",
|
||||
waitOn: function(){
|
||||
return [
|
||||
subsManager.subscribe("singleCharacter", this.params._id),
|
||||
];
|
||||
},
|
||||
data: function() {
|
||||
var data = Characters.findOne(
|
||||
{_id: this.params._id},
|
||||
{fields: {_id: 1, name: 1, color: 1, writers: 1, readers: 1}}
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onAfterAction: function() {
|
||||
var char = Characters.findOne({_id: this.params._id}, {fields: {name: 1}});
|
||||
var name = char && char.name;
|
||||
if (name){
|
||||
document.title = name + " - Printing";
|
||||
}
|
||||
},
|
||||
//analytics
|
||||
trackPageView: false,
|
||||
onRun: function() {
|
||||
window.ga && window.ga("send", "pageview", "/print-character");
|
||||
this.next();
|
||||
},
|
||||
});
|
||||
|
||||
this.route("library", {
|
||||
path: "/library",
|
||||
waitOn: function(){
|
||||
|
||||
174
rpg-docs/client/compatibility/jquery.quickfit.js
Normal file
174
rpg-docs/client/compatibility/jquery.quickfit.js
Normal file
@@ -0,0 +1,174 @@
|
||||
// jscs:disable
|
||||
// https://github.com/chunksnbits/jquery-quickfit
|
||||
(function ($) {
|
||||
var Quickfit, QuickfitHelper, defaults, pluginName;
|
||||
|
||||
pluginName = 'quickfit';
|
||||
|
||||
defaults = {
|
||||
min: 8,
|
||||
max: 12,
|
||||
tolerance: 0.02,
|
||||
truncate: false,
|
||||
width: null,
|
||||
sampleNumberOfLetters: 10,
|
||||
sampleFontSize: 12
|
||||
};
|
||||
QuickfitHelper = (function () {
|
||||
|
||||
var sharedInstance = null;
|
||||
|
||||
QuickfitHelper.instance = function (options) {
|
||||
if (!sharedInstance) {
|
||||
sharedInstance = new QuickfitHelper(options);
|
||||
}
|
||||
return sharedInstance;
|
||||
};
|
||||
|
||||
function QuickfitHelper(options) {
|
||||
this.options = options;
|
||||
|
||||
this.item = $('<span id="meassure"></span>');
|
||||
this.item.css({
|
||||
position: 'absolute',
|
||||
left: '-1000px',
|
||||
top: '-1000px',
|
||||
'font-size': "" + this.options.sampleFontSize + "px"
|
||||
});
|
||||
$('body').append(this.item);
|
||||
|
||||
this.meassures = {};
|
||||
}
|
||||
|
||||
QuickfitHelper.prototype.getMeassure = function (letter) {
|
||||
var currentMeassure;
|
||||
currentMeassure = this.meassures[letter];
|
||||
if (!currentMeassure) {
|
||||
currentMeassure = this.setMeassure(letter);
|
||||
}
|
||||
return currentMeassure;
|
||||
};
|
||||
|
||||
QuickfitHelper.prototype.setMeassure = function (letter) {
|
||||
var currentMeassure, index, sampleLetter, text, _ref;
|
||||
|
||||
text = '';
|
||||
sampleLetter = letter === ' ' ? ' ' : letter;
|
||||
|
||||
for (index = 0, _ref = this.options.sampleNumberOfLetters - 1; 0 <= _ref ? index <= _ref : index >= _ref; 0 <= _ref ? index++ : index--) {
|
||||
text += sampleLetter;
|
||||
}
|
||||
|
||||
this.item.html(text);
|
||||
currentMeassure = this.item.width() / this.options.sampleNumberOfLetters / this.options.sampleFontSize;
|
||||
this.meassures[letter] = currentMeassure;
|
||||
|
||||
return currentMeassure;
|
||||
};
|
||||
|
||||
return QuickfitHelper;
|
||||
|
||||
})();
|
||||
|
||||
Quickfit = (function () {
|
||||
|
||||
function Quickfit(element, options) {
|
||||
this.$element = element;
|
||||
this.options = $.extend({}, defaults, options);
|
||||
this.$element = $(this.$element);
|
||||
this._defaults = defaults;
|
||||
this._name = pluginName;
|
||||
this.quickfitHelper = QuickfitHelper.instance(this.options);
|
||||
}
|
||||
|
||||
Quickfit.prototype.fit = function () {
|
||||
var elementWidth;
|
||||
if (!this.options.width) {
|
||||
elementWidth = this.$element.width();
|
||||
this.options.width = elementWidth - this.options.tolerance * elementWidth;
|
||||
}
|
||||
if (this.text = this.$element.attr('data-quickfit')) {
|
||||
this.previouslyTruncated = true;
|
||||
} else {
|
||||
this.text = this.$element.text();
|
||||
}
|
||||
this.calculateFontSize();
|
||||
|
||||
if (this.options.truncate) this.truncate();
|
||||
|
||||
return {
|
||||
$element: this.$element,
|
||||
size: this.fontSize
|
||||
};
|
||||
};
|
||||
|
||||
Quickfit.prototype.calculateFontSize = function () {
|
||||
var letter, textWidth, i;
|
||||
|
||||
textWidth = 0;
|
||||
for (i = 0; i < this.text.length; ++i) {
|
||||
letter = this.text.charAt(i);
|
||||
textWidth += this.quickfitHelper.getMeassure(letter);
|
||||
}
|
||||
|
||||
this.targetFontSize = parseInt(this.options.width / textWidth);
|
||||
return this.fontSize = Math.max(this.options.min, Math.min(this.options.max, this.targetFontSize));
|
||||
};
|
||||
|
||||
Quickfit.prototype.truncate = function () {
|
||||
var index, lastLetter, letter, textToAdd, textWidth;
|
||||
|
||||
if (this.fontSize > this.targetFontSize) {
|
||||
textToAdd = '';
|
||||
textWidth = 3 * this.quickfitHelper.getMeassure('.') * this.fontSize;
|
||||
|
||||
index = 0;
|
||||
while (textWidth < this.options.width && index < this.text.length) {
|
||||
letter = this.text[index++];
|
||||
if (lastLetter) textToAdd += lastLetter;
|
||||
textWidth += this.fontSize * this.quickfitHelper.getMeassure(letter);
|
||||
lastLetter = letter;
|
||||
}
|
||||
|
||||
if (textToAdd.length + 1 === this.text.length) {
|
||||
textToAdd = this.text;
|
||||
} else {
|
||||
textToAdd += '...';
|
||||
}
|
||||
this.textWasTruncated = true;
|
||||
|
||||
return this.$element.attr('data-quickfit', this.text).html(textToAdd);
|
||||
|
||||
} else {
|
||||
if (this.previouslyTruncated) {
|
||||
return this.$element.html(this.text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Quickfit;
|
||||
|
||||
})();
|
||||
|
||||
return $.fn.quickfit = function (options) {
|
||||
var measurements = [];
|
||||
|
||||
// Separate measurements from repaints
|
||||
// First calculate all measurements...
|
||||
var $elements = this.each(function () {
|
||||
var measurement = new Quickfit(this, options).fit();
|
||||
measurements.push(measurement);
|
||||
return measurement.$element;
|
||||
});
|
||||
|
||||
// ... then apply the measurements.
|
||||
for (var i = 0; i < measurements.length; i++) {
|
||||
var measurement = measurements[i];
|
||||
|
||||
measurement.$element.css({ fontSize: measurement.size + 'px' });
|
||||
}
|
||||
|
||||
return $elements;
|
||||
};
|
||||
|
||||
})(jQuery, window);
|
||||
@@ -3,7 +3,8 @@ Template.registerHelper("canEditCharacter", function(charId) {
|
||||
});
|
||||
|
||||
canEditCharacter = function(charId) {
|
||||
var char = Characters.findOne(charId)
|
||||
var char = Characters.findOne(charId);
|
||||
if (!char) return false;
|
||||
var userId = Meteor.userId();
|
||||
return char.owner === userId ||
|
||||
_.contains(char.writers, userId);
|
||||
|
||||
12
rpg-docs/client/lib/printing.js
Normal file
12
rpg-docs/client/lib/printing.js
Normal file
@@ -0,0 +1,12 @@
|
||||
Session.setDefault("isPrinting", false);
|
||||
if (window.matchMedia) {
|
||||
var mediaQueryList = window.matchMedia("print");
|
||||
mediaQueryList.addListener(function(mql) {
|
||||
if (mql.matches) {
|
||||
Session.set("isPrinting", true);
|
||||
Tracker.flush();
|
||||
} else {
|
||||
Session.set("isPrinting", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
17
rpg-docs/client/lib/removeDuplicateProficiencies.js
Normal file
17
rpg-docs/client/lib/removeDuplicateProficiencies.js
Normal file
@@ -0,0 +1,17 @@
|
||||
removeDuplicateProficiencies = function(proficiencies) {
|
||||
dict = {};
|
||||
proficiencies.forEach(function(prof) {
|
||||
if (prof.name in dict) { //if we have already gone over another proficiency for the same thing
|
||||
if (dict[prof.name].value < prof.value) {
|
||||
dict[prof.name] = prof; //then take the new one if it's higher, otherwise leave it
|
||||
}
|
||||
} else {
|
||||
dict[prof.name] = prof; //if it wasn't already there, store it
|
||||
}
|
||||
});
|
||||
profs = []
|
||||
_.forEach(dict, function(prof) {
|
||||
profs.push(prof);
|
||||
})
|
||||
return profs;
|
||||
};
|
||||
17
rpg-docs/client/style/bounce.css
Normal file
17
rpg-docs/client/style/bounce.css
Normal file
@@ -0,0 +1,17 @@
|
||||
@keyframes bounce {
|
||||
from {
|
||||
transform: translate(0px,0px);
|
||||
}
|
||||
to {
|
||||
transform: translate(0px,-16px);
|
||||
}
|
||||
}
|
||||
|
||||
.bounce{
|
||||
animation-name: bounce;
|
||||
animation-duration: 0.3s;
|
||||
animation-direction: alternate;
|
||||
animation-timing-function: cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
animation-delay: 0s;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ Template.deleteCharacterConfirmation.helpers({
|
||||
if (Template.instance().canDelete.get()) {
|
||||
return "background: #d23f31; color: white;";
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Template.deleteCharacterConfirmation.events({
|
||||
@@ -20,9 +20,7 @@ Template.deleteCharacterConfirmation.events({
|
||||
},
|
||||
"click #deleteButton": function(event, instance) {
|
||||
if (instance.find("#nameInput").value === this.name) {
|
||||
popDialogStack();
|
||||
Router.go("/characterList");
|
||||
Characters.remove(this._id);
|
||||
popDialogStack(true);
|
||||
}
|
||||
},
|
||||
"click .cancelButton": function(event, instance){
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template name="characterSheet">
|
||||
<div class="fit layout vertical character-sheet">
|
||||
<app-header fixed effects="waterfall">
|
||||
<app-toolbar class="medium-tall {{colorClass}}">
|
||||
<app-toolbar class="medium-tall {{colorClass}}" style="z-index: 2;">
|
||||
<div top-item class="layout horizontal center">
|
||||
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
||||
<div class="flex character-name">
|
||||
@@ -20,6 +20,12 @@
|
||||
<iron-icon icon="social:share" item-icon></iron-icon>
|
||||
Share
|
||||
</paper-icon-item>
|
||||
<a href={{printUrl}}>
|
||||
<paper-icon-item id="printButton">
|
||||
<iron-icon icon="print" item-icon></iron-icon>
|
||||
Print
|
||||
</paper-icon-item>
|
||||
</a>
|
||||
<paper-icon-item id="characterSettings">
|
||||
<iron-icon icon="settings" item-icon></iron-icon>
|
||||
Settings
|
||||
@@ -44,17 +50,18 @@
|
||||
</div>
|
||||
<div bottom-item>
|
||||
<paper-tabs id="characterSheetTabs" selected={{selectedTab}} class="{{colorClass}}">
|
||||
<paper-tab name="stats">Stats</paper-tab>
|
||||
<paper-tab name="features">Features</paper-tab>
|
||||
<paper-tab name="stats" class="{{#if shouldBounce 0}}bounce{{/if}}">Stats</paper-tab>
|
||||
<paper-tab name="features" class="{{#if shouldBounce 1}}bounce{{/if}}">Features</paper-tab>
|
||||
<paper-tab name="inventory">Inventory</paper-tab>
|
||||
{{#unless hideSpellcasting}}
|
||||
<paper-tab name="spells">Spells</paper-tab>
|
||||
{{/unless}}
|
||||
<paper-tab name="persona">Persona</paper-tab>
|
||||
<paper-tab name="journal">Journal</paper-tab>
|
||||
<paper-tab name="journal" class="{{#if shouldBounce 5}}bounce{{/if}}">Journal</paper-tab>
|
||||
</paper-tabs>
|
||||
</div>
|
||||
</app-toolbar>
|
||||
{{#if newUserExperience}}{{> newUserStepper}}{{/if}}
|
||||
</app-header>
|
||||
<div class="flex" style="position: relative;">
|
||||
<iron-pages id="tabPages" class="fit" selected={{selectedTab}}>
|
||||
|
||||
@@ -29,7 +29,7 @@ Template.characterSheet.onRendered(function() {
|
||||
tabFabMenus = _.times(6, (n) =>
|
||||
tabPages[n].find(".mini-holder")
|
||||
);
|
||||
})
|
||||
});
|
||||
|
||||
//watch this character and make sure their encumbrance is updated
|
||||
//trackEncumbranceConditions(this.data._id, this);
|
||||
@@ -165,6 +165,12 @@ var getTab = function(charId){
|
||||
};
|
||||
|
||||
Template.characterSheet.helpers({
|
||||
printing: function(){
|
||||
return Session.get("isPrinting");
|
||||
},
|
||||
printUrl: function(){
|
||||
return `/character/${this._id}/${this.urlName || "-"}/print`
|
||||
},
|
||||
selectedTab: function(){
|
||||
return getTab(this._id);
|
||||
},
|
||||
@@ -172,6 +178,18 @@ Template.characterSheet.helpers({
|
||||
var char = Characters.findOne(this._id);
|
||||
return char && char.settings.hideSpellcasting;
|
||||
},
|
||||
newUserExperience: function(){
|
||||
var char = Characters.findOne(this._id);
|
||||
return char && char.settings.newUserExperience;
|
||||
},
|
||||
shouldBounce: function(tab){
|
||||
const selected = Session.get(this._id + ".selectedTab")
|
||||
const step = Session.get("newUserExperienceStep");
|
||||
if (selected == tab) return false;
|
||||
return (tab === 1 && step === 0) ||
|
||||
(tab === 5 && step === 1) ||
|
||||
(tab === 0 && step === 2);
|
||||
},
|
||||
});
|
||||
|
||||
Template.characterSheet.events({
|
||||
@@ -187,6 +205,12 @@ Template.characterSheet.events({
|
||||
data: this,
|
||||
template: "deleteCharacterConfirmation",
|
||||
element: event.currentTarget.parentElement.parentElement,
|
||||
callback: (result) => {
|
||||
if (result === true){
|
||||
Router.go("/characterList");
|
||||
Tracker.afterFlush(() => Characters.remove(this._id));
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
"click #shareCharacter": function(event, instance){
|
||||
|
||||
@@ -42,9 +42,20 @@
|
||||
</template>
|
||||
|
||||
<template name="featureEdit">
|
||||
{{#if showNewUserExperience}}
|
||||
{{# infoBox}}
|
||||
<p>
|
||||
Features represent all the permanent things your character can do.
|
||||
</p><p>
|
||||
A feature can change a character's stats with effects,
|
||||
or give the character proficiencies, attacks, and buffs.
|
||||
</p><p>
|
||||
Give the feature a name, and close it to continue.
|
||||
</p>
|
||||
{{/infoBox}}
|
||||
{{/if}}
|
||||
<!--name-->
|
||||
<paper-input id="featureNameInput" class="fullwidth" label="Name" value={{name}}></paper-input>
|
||||
|
||||
<div class="layout horizontal center wrap justified">
|
||||
<paper-dropdown-menu class=flex label="Enable Feature" style="flex-basis: 150px; max-width: 200px;">
|
||||
<dicecloud-selector selected={{enabledSelection}} class="dropdown-content enabled-dropdown">
|
||||
|
||||
@@ -47,6 +47,10 @@ Template.featureDetails.events({
|
||||
});
|
||||
|
||||
Template.featureEdit.helpers({
|
||||
showNewUserExperience: function(){
|
||||
return Session.get("newUserExperienceStep") === 0 ||
|
||||
Session.get("newUserExperienceStep") === 1;
|
||||
},
|
||||
usesSet: function(){
|
||||
return _.isString(this.uses);
|
||||
},
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
checked={{enabled}}
|
||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}>
|
||||
</paper-checkbox>
|
||||
<paper-tooltip position="left">Feature enabled</paper-tooltip>
|
||||
{{#simpleTooltip}}Feature enabled{{/simpleTooltip}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
@@ -101,11 +101,13 @@
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if canEditCharacter _id}}
|
||||
<paper-fab id="addFeature"
|
||||
class="floatyButton"
|
||||
icon="add">
|
||||
<paper-tooltip position="left">Add Feature</paper-tooltip>
|
||||
</paper-fab>
|
||||
<div class="floatyButton">
|
||||
<paper-fab id="addFeature"
|
||||
class="{{#if shouldFloatyButtonBounce}}bounce{{/if}}"
|
||||
icon="add">
|
||||
</paper-fab>
|
||||
{{#simpleTooltip}}Add Feature{{/simpleTooltip}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
var removeDuplicateProficiencies = function(proficiencies) {
|
||||
dict = {};
|
||||
proficiencies.forEach(function(prof) {
|
||||
if (prof.name in dict) { //if we have already gone over another proficiency for the same thing
|
||||
if (dict[prof.name].value < prof.value) {
|
||||
dict[prof.name] = prof; //then take the new one if it's higher, otherwise leave it
|
||||
}
|
||||
} else {
|
||||
dict[prof.name] = prof; //if it wasn't already there, store it
|
||||
}
|
||||
});
|
||||
profs = []
|
||||
_.forEach(dict, function(prof) {
|
||||
profs.push(prof);
|
||||
})
|
||||
return profs;
|
||||
};
|
||||
|
||||
Template.features.helpers({
|
||||
features: function(){
|
||||
var features = Features.find({charId: this._id}, {sort: {color: 1, name: 1}});
|
||||
@@ -59,6 +41,10 @@ Template.features.helpers({
|
||||
hasCharacters: function(string){
|
||||
return string && string.match(/\S/);
|
||||
},
|
||||
shouldFloatyButtonBounce: function(){
|
||||
const step = Session.get("newUserExperienceStep");
|
||||
return step === 0 && Features.find({charId: this._id}).count() <= 1;
|
||||
},
|
||||
});
|
||||
|
||||
Template.features.events({
|
||||
|
||||
@@ -110,12 +110,12 @@
|
||||
<div class="paper-font-caption" style="margin-right: 8px">
|
||||
{{round totalWeight}} lbs
|
||||
</div>
|
||||
<div>
|
||||
<div style="position: relative;">
|
||||
<paper-checkbox class="carriedCheckbox"
|
||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
||||
checked={{isCarried}}>
|
||||
</paper-checkbox>
|
||||
<paper-tooltip position="left"> Container carried</paper-tooltip>
|
||||
{{#simpleTooltip}} Container carried{{/simpleTooltip}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom list">
|
||||
@@ -136,21 +136,21 @@
|
||||
class="addContainer"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New container </paper-tooltip>
|
||||
{{#simpleTooltip class="always"}} Container {{/simpleTooltip}}
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="av:library-books"
|
||||
class="libraryItem"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> Library item </paper-tooltip>
|
||||
{{#simpleTooltip class="always"}} Item from library {{/simpleTooltip}}
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="note-add"
|
||||
class="addItem"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New item </paper-tooltip>
|
||||
{{#simpleTooltip class="always"}} Item {{/simpleTooltip}}
|
||||
</div>
|
||||
{{/fabMenu}}
|
||||
{{/if}}
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
<div class="bottom list">
|
||||
<div class="item-slot">
|
||||
<div class="item race layout horizontal center">
|
||||
<div class="item race layout horizontal center {{#if shouldRaceBounce}}bounce{{/if}}">
|
||||
{{race}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -83,9 +83,12 @@
|
||||
</div>
|
||||
<div class="fab-buffer"></div>
|
||||
{{#if canEditCharacter _id}}
|
||||
<paper-fab id="addNote"
|
||||
class="floatyButton"
|
||||
icon="add"
|
||||
title="Add"></paper-fab>
|
||||
<div class="floatyButton">
|
||||
<paper-fab id="addNote"
|
||||
icon="add"
|
||||
title="Add">
|
||||
</paper-fab>
|
||||
{{#simpleTooltip}}Add Note{{/simpleTooltip}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@@ -50,6 +50,9 @@ Template.journal.helpers({
|
||||
var char = Characters.findOne(this._id, {fields: {race: 1}});
|
||||
return char && char.race;
|
||||
},
|
||||
shouldRaceBounce: function(){
|
||||
return Session.get("newUserExperienceStep") === 1;
|
||||
},
|
||||
});
|
||||
|
||||
Template.journal.events({
|
||||
|
||||
@@ -1,11 +1,34 @@
|
||||
<template name="raceDialog">
|
||||
{{#baseDialog title="Race" class=color hideColor="true" hideDelete="true" startEditing=startEditing}}
|
||||
{{#if showNewUserExperience}}
|
||||
{{#infoBox}}
|
||||
{{#if stepComplete}}
|
||||
<p>You can add all the effects you need to represent how your race affects your character's attributes.</p>
|
||||
{{else}}
|
||||
<p>Click the edit button to edit your race and add a racial effect</p>
|
||||
{{/if}}
|
||||
{{/infoBox}}
|
||||
{{/if}}
|
||||
<div class="horizontal layout center-justified paper-font-display2">
|
||||
{{race}}
|
||||
</div>
|
||||
{{> effectsViewList charId=charId parentId=charId parentGroup="racial"}}
|
||||
{{> proficiencyViewList charId=charId parentId=charId parentGroup="racial"}}
|
||||
{{else}}
|
||||
{{#if showNewUserExperience}}
|
||||
{{#infoBox}}
|
||||
{{#if stepComplete}}
|
||||
<p>You can add all the effects you need to represent how your race affects your character's attributes.</p>
|
||||
{{else}}
|
||||
<p>
|
||||
Add an effect with the following options: <br>
|
||||
Attribute: <b>stats > speed</b> <br>
|
||||
Operation: <b>Base Value</b> <br>
|
||||
Value: <b>30</b> (might be different for some races)
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/infoBox}}
|
||||
{{/if}}
|
||||
<paper-input id="raceInput" label="Race" value={{race}}></paper-input>
|
||||
{{> effectsEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
||||
{{> proficiencyEditList parentId=charId parentCollection="Characters" charId=charId parentGroup="racial"}}
|
||||
|
||||
@@ -19,4 +19,10 @@ Template.raceDialog.helpers({
|
||||
var char = Characters.findOne(this.charId, {fields: {color: 1}});
|
||||
if (char) return getColorClass(char.color);
|
||||
},
|
||||
stepComplete: function(){
|
||||
return Session.get("newUserExperienceStep") > 1;
|
||||
},
|
||||
showNewUserExperience: function(){
|
||||
return Session.get("newUserExperienceStep") >= 1;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.newUserStepper {
|
||||
height: 180px !important;
|
||||
}
|
||||
|
||||
.newUserStepper paper-step .invalid-step-message {
|
||||
color: #d13b2e;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.newUserStepper paper-step[invalid] .invalid-step-message {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<template name="newUserStepper">
|
||||
<paper-stepper linear selected="0" class="newUserStepper">
|
||||
<paper-step id="step0" label="Add a feature">
|
||||
<p>
|
||||
To get started, add a feature
|
||||
</p>
|
||||
</paper-step>
|
||||
<paper-step id="step1" label="Add an effect">
|
||||
<p>
|
||||
Add a racial effect to set your speed
|
||||
</p>
|
||||
</paper-step>
|
||||
<paper-step id="step2" label="See the effect in action">
|
||||
<p>
|
||||
View your speed stat
|
||||
</p>
|
||||
</paper-step>
|
||||
<paper-step id="step3" label="Finish">
|
||||
Done! If you get stuck, be sure to check out the <a href="/guide">guide</a>, or ask for help using the feedback form
|
||||
<div class="layout vertical end">
|
||||
<paper-button class="done-button" style="color: #d13b2e">Finish</paper-button>
|
||||
</div>
|
||||
</paper-step>
|
||||
</paper-stepper>
|
||||
</template>
|
||||
|
||||
<template name="newUserStepperPlaceholder">
|
||||
<div style="height: 300px"></div>
|
||||
</template>
|
||||
@@ -0,0 +1,58 @@
|
||||
Template.newUserStepper.onRendered(function(){
|
||||
Session.set("newUserExperienceStep", 0);
|
||||
let stepper = this.find("paper-stepper");
|
||||
_.defer(() => {
|
||||
this.autorun((c) => {
|
||||
var step = Session.get("newUserExperienceStep");
|
||||
var hasFeatures = Features.find({charId: this.data._id}).count() > 1;
|
||||
if (step === 0 && hasFeatures){
|
||||
stepper.continue();
|
||||
}
|
||||
});
|
||||
this.autorun((c) => {
|
||||
var step = Session.get("newUserExperienceStep");
|
||||
var hasEffect = !!Effects.find({
|
||||
charId: this.data._id,
|
||||
stat: "speed",
|
||||
"parent.group": "racial",
|
||||
operation: "base",
|
||||
value: {$gt: 0},
|
||||
}).count();
|
||||
if (step === 1 && hasEffect){
|
||||
stepper.continue();
|
||||
}
|
||||
});
|
||||
this.autorun((c) => {
|
||||
var step = Session.get("newUserExperienceStep");
|
||||
if (step === 2 && Session.get("viewedSpeed")){
|
||||
Session.set("viewedSpeed", undefined);
|
||||
stepper.continue();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Template.newUserStepper.events({
|
||||
"paper-stepper-progressed paper-stepper": function(event, template){
|
||||
const step = template.find("paper-stepper").selected;
|
||||
Session.set("newUserExperienceStep", step);
|
||||
},
|
||||
"paper-stepper-completed paper-stepper": function(event, template){
|
||||
Session.set("newUserExperienceStep", undefined);
|
||||
Session.set("showNewUserExperience", undefined);
|
||||
Characters.update(this._id, {$unset: {"settings.newUserExperience": 1}});
|
||||
},
|
||||
"click .done-button": function(event, instance){
|
||||
const stepper = instance.find("paper-stepper");
|
||||
stepper.continue();
|
||||
},
|
||||
});
|
||||
|
||||
Template.stats.events({
|
||||
"click .stat-card": function(event, instance){
|
||||
var step = Session.get("newUserExperienceStep");
|
||||
if (this.stat === "speed" && step === 2){
|
||||
Session.set("viewedSpeed", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
.printedAbility .title.paper-font-subhead {
|
||||
font-size: 2.5mm !important;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<template name="printedAbility">
|
||||
<div class="printedAbility layout vertical center double-border">
|
||||
<div class="paper-font-subhead title flex layout horizontal center">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="paper-font-display1 stat">
|
||||
{{characterCalculate "attributeValue" ../_id ability}}
|
||||
</div>
|
||||
<div class="paper-font-subhead modifier">
|
||||
{{abilityMod}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
Template.printedAbility.helpers({
|
||||
abilityMod: function() {
|
||||
return signedString(
|
||||
Characters.calculate.abilityMod(
|
||||
Template.parentData()._id, this.ability
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<template name="printedAttack">
|
||||
<div class="printedAttack" style="margin-bottom: 2mm">
|
||||
<div class="layout horizontal">
|
||||
<div class="paper-font-headline layout horizontal center"
|
||||
style="margin-right: 1mm; min-width: 32px; text-align: right;">
|
||||
{{evaluateAttackBonus charId attack}}
|
||||
</div>
|
||||
<div class="flex layout vertical">
|
||||
<div class="paper-font-body2">
|
||||
{{attack.name}}
|
||||
</div>
|
||||
<div>
|
||||
{{evaluateDamage charId attack}} {{attack.damageType}}
|
||||
</div>
|
||||
{{#if attack.details}}
|
||||
<div>
|
||||
{{attack.details}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,30 @@
|
||||
Template.printedAttack.helpers({
|
||||
evaluateAttackBonus: function(charId, attack) {
|
||||
if (attack.parent.collection == "Spells") {
|
||||
var spell = Spells.findOne(attack.parent.id);
|
||||
if (spell) {
|
||||
bonus = evaluate(charId, attack.attackBonus, {
|
||||
"spellListId": spell.parent.id
|
||||
});
|
||||
}
|
||||
} else {
|
||||
var bonus = evaluate(charId, attack.attackBonus);
|
||||
}
|
||||
|
||||
if (_.isFinite(bonus)) {
|
||||
return bonus > 0 ? "+" + bonus : "" + bonus;
|
||||
} else {
|
||||
return bonus;
|
||||
}
|
||||
},
|
||||
evaluateDamage: function(charId, attack) {
|
||||
if (attack.parent.collection == "Spells") {
|
||||
var spell = Spells.findOne(attack.parent.id);
|
||||
if (spell) {
|
||||
return evaluateSpellString(charId, spell.parent.id, attack.damage);
|
||||
}
|
||||
} else {
|
||||
return evaluateString(charId, attack.damage);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,190 @@
|
||||
.printed .page {
|
||||
width: 100%;
|
||||
padding: 6mm;
|
||||
page-break-inside: avoid;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.printed .shrink-to-fit {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.printed p {
|
||||
margin-bottom: 1mm;
|
||||
}
|
||||
|
||||
.printed .double-border {
|
||||
position: relative;
|
||||
padding: 11px 10px;
|
||||
}
|
||||
|
||||
.printed .double-border > * {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.printed .double-border:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: 16px solid transparent;
|
||||
border-image-source: url(/png/doubleLineImageBorder.png);
|
||||
border-image-slice: 110 126 fill;
|
||||
border-image-repeat: stretch;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.printed .double-border.printedAbility {
|
||||
padding: 11px 6px 0;
|
||||
margin-bottom: 3mm;
|
||||
}
|
||||
|
||||
.printed .double-border.printedAbility:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.printed .printedAbility .modifier {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
z-index: 1;
|
||||
padding: 2px 18px;
|
||||
background-image: url(/png/upwardPointingBorder.png);
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
.printed .octogon-border {
|
||||
position: relative;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.printed .octogon-border:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: 22px solid transparent;
|
||||
border-image: url(/png/octogonBorder.png) 124 118 fill;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.printed iron-icon {
|
||||
width: 16px;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.printed .proficiencies, .printed .attacks, .printed .background {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.printed .shield-background {
|
||||
background: url(/png/shieldBorder.png);
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
padding: 4px 8px 8px;
|
||||
width: 80px;
|
||||
height: 91px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.printed .shield-background .paper-font-subhead {
|
||||
width: 64px;
|
||||
text-align: center;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.printed {
|
||||
font-size: 3mm;
|
||||
}
|
||||
|
||||
.printed .paper-font-body2 {
|
||||
font-size: 3mm;
|
||||
line-height: 4mm;
|
||||
}
|
||||
|
||||
.printed .paper-font-subhead {
|
||||
font-size: 3mm !important;
|
||||
line-height: 3.5mm !important;
|
||||
font-weight: bold !important;
|
||||
text-transform: uppercase !important;
|
||||
}
|
||||
|
||||
.printed .paper-font-subhead.modifier {
|
||||
font-size: 4mm !important;
|
||||
line-height: 6mm !important;
|
||||
}
|
||||
|
||||
.printed .paper-font-display1 {
|
||||
font-size: 7mm !important;
|
||||
line-height: 12mm !important;
|
||||
}
|
||||
|
||||
.printed .paper-font-headline {
|
||||
font-size: 5mm !important;
|
||||
line-height: 6mm !important;
|
||||
}
|
||||
|
||||
.printed .lined-background {
|
||||
background-image: url(/png/horizontalLine.png);
|
||||
background-size: 100% 4mm;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
.printed .page {
|
||||
width: 210mm;
|
||||
height: 297mm;
|
||||
background: white;
|
||||
margin: 8px;
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||
0 1px 5px 0 rgba(0, 0, 0, 0.12),
|
||||
0 3px 1px -2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.printed .page-holder {
|
||||
width: calc(210mm + 16px);
|
||||
}
|
||||
|
||||
.printed {
|
||||
overflow: auto;
|
||||
padding-left:
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
app-drawer {
|
||||
display: none;
|
||||
}
|
||||
app-header {
|
||||
display: none;
|
||||
}
|
||||
.printed {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99;
|
||||
background: #fff;
|
||||
}
|
||||
.printed .page-holder {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.printed .page {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
<template name="printedCharacterSheet">
|
||||
<div class="fit printed-character-sheet layout vertical">
|
||||
<app-header fixed effects="waterfall">
|
||||
<app-toolbar class="medium {{colorClass}} layout horizontal center" style="z-index: 2;">
|
||||
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
|
||||
<paper-icon-button icon="arrow-back" class="backButton"></paper-icon-button>
|
||||
<div class="flex character-name">
|
||||
{{name}}
|
||||
</div>
|
||||
<div style="position: relative;">
|
||||
<paper-icon-button icon="print" class="printButton"></paper-icon-button>
|
||||
{{#simpleTooltip}} Print {{/simpleTooltip}}
|
||||
</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="printed flex">
|
||||
<div class="page-holder">
|
||||
<div class="page">
|
||||
<div class="layout vertical" style="height: 100%;">
|
||||
<div class="layout horizontal center" style="margin-bottom: 4mm">
|
||||
<img src="http://localhost:3000/crown-dice-logo-cropped-transparent.png" style="width: 60px; margin-right: 2mm">
|
||||
<div class="characterName paper-font-title" style="margin-right: 4mm">
|
||||
{{name}}
|
||||
</div>
|
||||
<div class="paper-font-body2">
|
||||
<div>
|
||||
{{#each classes}}
|
||||
<span style="margin-right: 2mm;">
|
||||
{{name}} {{level}}
|
||||
</span>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div>
|
||||
{{character.alignment}} {{character.gender}} {{character.race}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex layout vertical end" style="margin-right: 2mm;">
|
||||
<div class="paper-font-body2 " style="font-size: 5mm !important;">
|
||||
dicecloud.com
|
||||
</div>
|
||||
<div>
|
||||
{{characterUrl}}
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="qrCode"></canvas>
|
||||
</div>
|
||||
<div class="columns layout horizontal flex">
|
||||
<div class="col1 flex layout vertical">
|
||||
<div class="layout vertical center-justified" style="min-height: 100px; margin-bottom: 4mm;">
|
||||
<div class="initiative" style="margin-bottom: 2mm;">
|
||||
{{> printedLongStat stat="" name="Inspiration" prefix=""}}
|
||||
</div>
|
||||
<div class="proficiencyBonus">
|
||||
{{> printedLongStat stat="proficiencyBonus" name="Proficiency Bonus" prefix="+"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout horizontal">
|
||||
<div class="abilities layout vertical justified" style="margin-right: 4mm;">
|
||||
{{> printedAbility ability="strength" title="Strength"}}
|
||||
{{> printedAbility ability="dexterity" title="Dexterity"}}
|
||||
{{> printedAbility ability="constitution" title="Constitution"}}
|
||||
{{> printedAbility ability="intelligence" title="Intelligence"}}
|
||||
{{> printedAbility ability="wisdom" title="Wisdom"}}
|
||||
{{> printedAbility ability="charisma" title="Charisma"}}
|
||||
</div>
|
||||
<div class="flex layout vertical">
|
||||
<div class="saves double-border" style="margin-bottom: 2mm">
|
||||
<div>
|
||||
{{> printedSkillRow name="Strength" skill="strengthSave"}}
|
||||
{{> printedSkillRow name="Dexterity" skill="dexteritySave"}}
|
||||
{{> printedSkillRow name="Constitution" skill="constitutionSave"}}
|
||||
{{> printedSkillRow name="Intelligence" skill="intelligenceSave"}}
|
||||
{{> printedSkillRow name="Wisdom" skill="wisdomSave"}}
|
||||
{{> printedSkillRow name="Charisma" skill="charismaSave"}}
|
||||
</div>
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Saving Throws
|
||||
</div>
|
||||
</div>
|
||||
<div class="skills double-border">
|
||||
<div>
|
||||
{{> printedSkillRow name="Acrobatics" skill="acrobatics"}}
|
||||
{{> printedSkillRow name="Animal Handling" skill="animalHandling"}}
|
||||
{{> printedSkillRow name="Arcana" skill="arcana"}}
|
||||
{{> printedSkillRow name="Athletics" skill="athletics"}}
|
||||
{{> printedSkillRow name="Deception" skill="deception"}}
|
||||
{{> printedSkillRow name="History" skill="history"}}
|
||||
{{> printedSkillRow name="Insight" skill="insight"}}
|
||||
{{> printedSkillRow name="Intimidation" skill="intimidation"}}
|
||||
{{> printedSkillRow name="Investigation" skill="investigation"}}
|
||||
{{> printedSkillRow name="Medicine" skill="medicine"}}
|
||||
{{> printedSkillRow name="Nature" skill="nature"}}
|
||||
{{> printedSkillRow name="Perception" skill="perception" showPassive="true"}}
|
||||
{{> printedSkillRow name="Performance" skill="performance"}}
|
||||
{{> printedSkillRow name="Persuasion" skill="persuasion"}}
|
||||
{{> printedSkillRow name="Religion" skill="religion"}}
|
||||
{{> printedSkillRow name="Sleight of Hand" skill="sleightOfHand"}}
|
||||
{{> printedSkillRow name="Stealth" skill="stealth"}}
|
||||
{{> printedSkillRow name="Survival" skill="survival"}}
|
||||
</div>
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Skills
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="proficiencies flex double-border" style="margin-top: 4mm">
|
||||
<div class="paper-font-subhead layout vertical center" style="margin-bottom: 2mm;">
|
||||
Proficiencies
|
||||
</div>
|
||||
<div class="layout horizontal">
|
||||
<div class="flex" style="margin-right: 2mm">
|
||||
{{#if armorProfs.length}}
|
||||
<div class="paper-font-subhead" style="margin-bottom: 1mm;">Armor</div>
|
||||
{{/if}}
|
||||
{{#each armorProfs}}
|
||||
{{> printedProficiency}}
|
||||
{{/each}}
|
||||
{{#if weaponProfs.length}}
|
||||
<div class="paper-font-subhead" style="margin: 2mm 0 1mm;">Weapons</div>
|
||||
{{/if}}
|
||||
{{#each weaponProfs}}
|
||||
{{> printedProficiency}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if toolProfs.length}}
|
||||
<div class="flex">
|
||||
<div class="paper-font-subhead" style="margin-bottom: 1mm;">Tools</div>
|
||||
{{#each toolProfs}}
|
||||
{{> printedProficiency}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col2 flex layout vertical" style="margin-left: 4mm; margin-right: 4mm;">
|
||||
<div class="layout horizontal center justified" style="min-height: 100px; margin-bottom: 4mm;">
|
||||
<div class="armor">
|
||||
{{> printedSquareStat stat="armor" name="Armor Class" class="shield-background"}}
|
||||
</div>
|
||||
<div class="inititive">
|
||||
{{> printedSquareStat stat="initiative" name="Initiative" isSkill="true" class="double-border"}}
|
||||
</div>
|
||||
<div class="speed">
|
||||
{{> printedSquareStat stat="speed" name="Speed" class="double-border"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hitpoints layout vertical double-border" style="margin-bottom: 2mm;">
|
||||
<div>
|
||||
Hit point maximum:
|
||||
<span class="paper-font-subhead">
|
||||
{{characterCalculate "attributeBase" _id "hitPoints"}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex" style="width: 3cm; height: 2cm;">
|
||||
<!-- Space for writing -->
|
||||
</div>
|
||||
<div class="layout vertical center paper-font-subhead">
|
||||
Hit Points
|
||||
</div>
|
||||
</div>
|
||||
<div class="tempHitpoints layout vertical double-border" style="margin-bottom: 2mm;">
|
||||
<div style="width: 3cm; height: 1.5cm;">
|
||||
<!-- Space for writing -->
|
||||
</div>
|
||||
<div class="layout vertical center paper-font-subhead">
|
||||
Temporary Hit Points
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout horizontal" style="margin-bottom: 4mm;">
|
||||
<div class="hitDice double-border flex layout vertical" style="margin-right: 2mm;">
|
||||
<div>
|
||||
Total:
|
||||
<span class="paper-font-subhead" style="text-transform: none !important;">
|
||||
{{hitDiceTotal}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex" style="min-height: 1cm;">
|
||||
<!-- Space for writing -->
|
||||
</div>
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Hit Dice
|
||||
</div>
|
||||
</div>
|
||||
<div class="deathSaves layout vertical center double-border">
|
||||
<div class="" style="margin-bottom: 1mm;">
|
||||
Successes
|
||||
</div>
|
||||
<div class="layout horizontal center">
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
</div>
|
||||
<div class="" style="margin: 1mm 0;">
|
||||
Failures
|
||||
</div>
|
||||
<div class="layout horizontal center">
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
<iron-icon icon="radio-button-unchecked"></iron-icon>
|
||||
</div>
|
||||
<div class="paper-font-subhead layout vertical center" style="margin-top: 2mm;">
|
||||
Death Saves
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="attacks double-border flex">
|
||||
<div class="paper-font-subhead layout vertical center" style="margin-bottom: 2mm;">
|
||||
Attacks
|
||||
</div>
|
||||
{{#each attack in attacks}}
|
||||
{{> printedAttack attack=attack charId=_id}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col3 flex layout vertical">
|
||||
<div class="Languages double-border" style="min-height: 100px; margin-bottom: 4mm;">
|
||||
<div class="paper-font-subhead layout vertical center" style="margin-bottom: 2mm;">
|
||||
Languages
|
||||
</div>
|
||||
<div class="layout horizontal">
|
||||
<div class="flex" style="margin-right: 2mm;">
|
||||
{{#each languageProfs.left}}
|
||||
{{> printedProficiency}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{#if languageProfs.right.length}}
|
||||
<div class="flex">
|
||||
{{#each languageProfs.right}}
|
||||
{{> printedProficiency}}
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="traits double-border">
|
||||
{{#markdown}}{{evaluateShortString character._id character.personality}}{{/markdown}}
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Personality traits
|
||||
</div>
|
||||
</div>
|
||||
<div class="ideals double-border" style="margin-top: 2mm">
|
||||
{{#markdown}}{{evaluateShortString character._id character.ideals}}{{/markdown}}
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Ideals
|
||||
</div>
|
||||
</div>
|
||||
<div class="bonds double-border" style="margin-top: 2mm">
|
||||
{{#markdown}}{{evaluateShortString character._id character.bonds}}{{/markdown}}
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Bonds
|
||||
</div>
|
||||
</div>
|
||||
<div class="flaws double-border" style="margin-top: 2mm">
|
||||
{{#markdown}}{{evaluateShortString character._id character.flaws}}{{/markdown}}
|
||||
<div class="paper-font-subhead layout vertical center">
|
||||
Flaws
|
||||
</div>
|
||||
</div>
|
||||
<div class="background double-border flex layout vertical" style="margin-top: 2mm">
|
||||
<div class="paper-font-subhead layout vertical center" style="margin-bottom: 4mm">
|
||||
Notes
|
||||
</div>
|
||||
<div class="flex lined-background">
|
||||
<!-- lined space for writing -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,80 @@
|
||||
import QRCode from "qrcode"
|
||||
|
||||
Template.printedCharacterSheet.onRendered(function(){
|
||||
// Quickfit is only called once on rendering, text will not resize reactively
|
||||
this.$(".shrink-to-fit").quickfit({
|
||||
min: 7,
|
||||
max: 36,
|
||||
truncate: true,
|
||||
});
|
||||
let url = `https://dicecloud.com/character/${this.data._id}`;
|
||||
let canvas = this.find("#qrCode");
|
||||
QRCode.toCanvas(canvas, url, {
|
||||
margin: 0,
|
||||
width: 200,
|
||||
}, function(error){
|
||||
$(canvas).css("width", "60px").css("height", "60px");
|
||||
if (error) console.error(error)
|
||||
});
|
||||
});
|
||||
|
||||
Template.printedCharacterSheet.helpers({
|
||||
character(){
|
||||
return Characters.findOne(this._id);
|
||||
},
|
||||
classes: function(){
|
||||
return Classes.find({charId: this._id}, {sort: {createdAt: 1}});
|
||||
},
|
||||
weaponProfs: function(){
|
||||
var profs = Proficiencies.find({charId: this._id, type: "weapon"});
|
||||
return removeDuplicateProficiencies(profs);
|
||||
},
|
||||
armorProfs: function(){
|
||||
var profs = Proficiencies.find({charId: this._id, type: "armor"});
|
||||
return removeDuplicateProficiencies(profs);
|
||||
},
|
||||
toolProfs: function(){
|
||||
var profs = Proficiencies.find({charId: this._id, type: "tool"});
|
||||
return removeDuplicateProficiencies(profs);
|
||||
},
|
||||
languageProfs: function(){
|
||||
var profs = Proficiencies.find({charId: this._id, type: "language"});
|
||||
profs = removeDuplicateProficiencies(profs);
|
||||
if (profs.length > 3){
|
||||
var halfway = Math.floor(profs.length / 2);
|
||||
var left = profs.slice(0, halfway);
|
||||
var right = profs.slice(halfway);
|
||||
return {left, right};
|
||||
} else {
|
||||
return {left: profs, right: []};
|
||||
}
|
||||
},
|
||||
attacks: function(){
|
||||
return Attacks.find(
|
||||
{charId: this._id, enabled: true},
|
||||
{sort: {color: 1, name: 1}});
|
||||
},
|
||||
hitDiceTotal: function(){
|
||||
let d6 = Characters.calculate.attributeValue(this._id, "d6HitDice");
|
||||
let d8 = Characters.calculate.attributeValue(this._id, "d8HitDice");
|
||||
let d10 = Characters.calculate.attributeValue(this._id, "d10HitDice");
|
||||
let d12 = Characters.calculate.attributeValue(this._id, "d12HitDice");
|
||||
d6 = d6 ? d6 + "d6" : "";
|
||||
d8 = d8 ? d8 + "d8" : "";
|
||||
d10 = d10 ? d10 + "d10" : "";
|
||||
d12 = d12 ? d12 + "d12" : "";
|
||||
return [d6, d8, d10, d12].filter(Boolean).join(" ");
|
||||
},
|
||||
characterUrl: function(){
|
||||
return `/character/${this._id}`
|
||||
},
|
||||
});
|
||||
|
||||
Template.printedCharacterSheet.events({
|
||||
"click .printButton": function(event, instance){
|
||||
print();
|
||||
},
|
||||
"click .backButton": function(event, instance){
|
||||
history && history.back();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
.printedLongStat .title {
|
||||
white-space: nowrap;
|
||||
margin-left: 2mm;
|
||||
}
|
||||
|
||||
.printedLongStat .numbers {
|
||||
z-index: 1;
|
||||
min-width: 74px;
|
||||
min-height: 45px;
|
||||
}
|
||||
|
||||
.printed .printedLongStat.double-border{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.printed .printedLongStat.double-border:before {
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
left: 33px;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<template name="printedLongStat">
|
||||
<div class="printedLongStat layout horizontal double-border">
|
||||
<div class="numbers paper-font-display1 octogon-border">
|
||||
{{#if stat}}
|
||||
{{#if isSkill}}
|
||||
{{prefix}}{{skillMod}}
|
||||
{{else}}
|
||||
{{prefix}}{{characterCalculate "attributeValue" ../_id stat}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="paper-font-subhead title flex layout horizontal center">
|
||||
{{name}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
Template.printedLongStat.helpers({
|
||||
skillMod: function() {
|
||||
return signedString(
|
||||
Characters.calculate.skillMod(
|
||||
Template.parentData()._id, this.stat
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
.printedProficiency iron-icon {
|
||||
margin-right: 2mm;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<template name="printedProficiency">
|
||||
<div class="printedProficiency layout horizontal center">
|
||||
<iron-icon icon="{{profIcon}}"></iron-icon>
|
||||
<div>{{getName}}</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,40 @@
|
||||
Template.printedProficiency.helpers({
|
||||
profIcon: function(){
|
||||
var prof = this.value;
|
||||
if (prof > 0 && prof < 1) return "image:brightness-2";
|
||||
if (prof === 1) return "image:brightness-1";
|
||||
if (prof > 1) return "av:album";
|
||||
return "radio-button-off";
|
||||
},
|
||||
getName: function(){
|
||||
if (this.type === "skill") return skills[this.name];
|
||||
if (this.type === "save") return saves[this.name];
|
||||
return this.name;
|
||||
},
|
||||
});
|
||||
|
||||
Template.printedProficiency.events({
|
||||
"click .proficiency": function(event, instance){
|
||||
if (this.parent.collection == "Characters") {
|
||||
if (this.parent.group == "background") {
|
||||
pushDialogStack({
|
||||
template: "backgroundDialog",
|
||||
data: {
|
||||
"charId": this.charId,
|
||||
"field":"background",
|
||||
"title":"Background",
|
||||
"color":"j",
|
||||
},
|
||||
element: event.currentTarget,
|
||||
})
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
openParentDialog({
|
||||
parent: this.parent,
|
||||
charId: this.charId,
|
||||
element: event.currentTarget,
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
.printedSkillRow {
|
||||
height: 24px;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.printedSkillRow .skill-mod {
|
||||
width: 36px;
|
||||
text-align: center;
|
||||
font-size: 3.5mm;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<template name="printedSkillRow">
|
||||
<div class="printedSkillRow layout horizontal center">
|
||||
<iron-icon icon="{{profIcon}}"></iron-icon>
|
||||
{{#if failSkill}}
|
||||
<div class="fail skill-mod">fail</div>
|
||||
{{else}}
|
||||
<div class="{{advantage}} skill-mod">
|
||||
{{skillMod}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div flex>
|
||||
{{name}}
|
||||
{{#if conditionalCount}}
|
||||
*
|
||||
{{/if}}
|
||||
{{#if showPassive}}
|
||||
({{characterCalculate "passiveSkill" ../_id skill}})
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,41 @@
|
||||
Template.printedSkillRow.helpers({
|
||||
skillMod: function() {
|
||||
return signedString(
|
||||
Characters.calculate.skillMod(
|
||||
Template.parentData()._id, this.skill
|
||||
)
|
||||
);
|
||||
},
|
||||
profIcon: function(){
|
||||
var charId = Template.parentData()._id;
|
||||
var prof = Characters.calculate.proficiency(charId, this.skill);
|
||||
if (prof > 0 && prof < 1) return "image:brightness-2";
|
||||
if (prof === 1) return "image:brightness-1";
|
||||
if (prof > 1) return "av:album";
|
||||
return "radio-button-unchecked";
|
||||
},
|
||||
failSkill: function(){
|
||||
var charId = Template.parentData()._id;
|
||||
return Effects.find({
|
||||
charId: charId,
|
||||
stat: this.skill,
|
||||
enabled: true,
|
||||
operation: "fail",
|
||||
}).count();
|
||||
},
|
||||
advantage: function(){
|
||||
var charId = Template.parentData()._id;
|
||||
var advantage = Characters.calculate.advantage(charId, this.skill);
|
||||
if (advantage > 0) return "advantage";
|
||||
if (advantage < 0) return "disadvantage";
|
||||
},
|
||||
conditionalCount: function(){
|
||||
var charId = Template.parentData()._id;
|
||||
return Effects.find({
|
||||
charId: charId,
|
||||
stat: this.skill,
|
||||
enabled: true,
|
||||
operation: "conditional",
|
||||
}).count();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,7 @@
|
||||
.printedSquareStat {
|
||||
min-width: 67px;
|
||||
}
|
||||
|
||||
.printedSquareStat .title.paper-font-subhead {
|
||||
font-size: 2.5mm !important;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<template name="printedSquareStat">
|
||||
<div class="printedSquareStat layout vertical center {{class}}">
|
||||
<div class="numbers paper-font-display1">
|
||||
{{#if isSkill}}
|
||||
{{prefix}}{{skillMod}}
|
||||
{{else}}
|
||||
{{prefix}}{{characterCalculate "attributeValue" ../_id stat}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="paper-font-subhead title">
|
||||
{{name}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,9 @@
|
||||
Template.printedSquareStat.helpers({
|
||||
skillMod: function() {
|
||||
return signedString(
|
||||
Characters.calculate.skillMod(
|
||||
Template.parentData()._id, this.stat
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -53,22 +53,22 @@
|
||||
{{numPrepared}} / {{evaluate charId maxPrepared}}
|
||||
</div>
|
||||
{{/if}}
|
||||
<div>
|
||||
<paper-tooltip position="left">
|
||||
Done
|
||||
</paper-tooltip>
|
||||
<div style="position: relative;">
|
||||
<paper-icon-button class="finishPrep" icon="done">
|
||||
</paper-icon-button>
|
||||
{{#simpleTooltip}}
|
||||
Done
|
||||
{{/simpleTooltip}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
<paper-tooltip position="left">
|
||||
Change prepared spells
|
||||
</paper-tooltip>
|
||||
<div style="position: relative;">
|
||||
<paper-icon-button class="prepSpells"
|
||||
disabled={{#unless canEditCharacter charId}}true{{/unless}}
|
||||
icon="book">
|
||||
</paper-icon-button>
|
||||
{{#simpleTooltip}}
|
||||
Change prepared spells
|
||||
{{/simpleTooltip}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
@@ -124,32 +124,31 @@
|
||||
{{#if canEditCharacter _id}}
|
||||
{{#fabMenu}}
|
||||
<div>
|
||||
<paper-tooltip position="left">
|
||||
New spell list
|
||||
</paper-tooltip>
|
||||
<paper-fab icon="work"
|
||||
class="addSpellList"
|
||||
mini>
|
||||
</paper-fab>
|
||||
|
||||
{{#simpleTooltip class="always"}}
|
||||
Spell list
|
||||
{{/simpleTooltip}}
|
||||
</div>
|
||||
<div>
|
||||
<paper-tooltip position="left">
|
||||
Spell library
|
||||
</paper-tooltip>
|
||||
<paper-fab icon="av:library-books"
|
||||
class="librarySpell"
|
||||
mini>
|
||||
</paper-fab>
|
||||
{{#simpleTooltip class="always"}}
|
||||
Spell from library
|
||||
{{/simpleTooltip}}
|
||||
</div>
|
||||
<div>
|
||||
<paper-tooltip position="left">
|
||||
New spell
|
||||
</paper-tooltip>
|
||||
<paper-fab icon="note-add"
|
||||
class="addSpell"
|
||||
mini>
|
||||
</paper-fab>
|
||||
{{#simpleTooltip class="always"}}
|
||||
Spell
|
||||
{{/simpleTooltip}}
|
||||
</div>
|
||||
{{/fabMenu}}
|
||||
{{/if}}
|
||||
|
||||
@@ -279,23 +279,23 @@ Template.spells.events({
|
||||
collection: "SpellLists",
|
||||
};
|
||||
spell.prepared = "prepared";
|
||||
Spells.insert(spell);
|
||||
let insertedSpellId = Spells.insert(spell);
|
||||
// Copy over attacks and effects
|
||||
_.each(rawSpell.attacks, (attack) => {
|
||||
if (!("attackBonus" in attack)) {attack.attackBonus = "attackBonus"} //if no attack bonus provided, use spell list's
|
||||
attack.charId = charId;
|
||||
attack.parent = {id: spellId, collection: "Spells"};
|
||||
attack.parent = {id: insertedSpellId, collection: "Spells"};
|
||||
Attacks.insert(attack);
|
||||
});
|
||||
_.each(rawSpell.effects, (effect) => {
|
||||
effect.charId = charId;
|
||||
effect.parent = {id: spellId, collection: "Spells"};
|
||||
effect.parent = {id: insertedSpellId, collection: "Spells"};
|
||||
Effects.insert(effect);
|
||||
});
|
||||
|
||||
_.each(rawSpell.buffs, (buff) => {
|
||||
buff.charId = charId;
|
||||
buff.parent = {id: spellId, collection: "Spells"};
|
||||
buff.parent = {id: insertedSpellId, collection: "Spells"};
|
||||
buffId = Buffs.insert(buff);
|
||||
|
||||
_.each(buff.attacks, (attack) => {
|
||||
|
||||
@@ -6,6 +6,16 @@
|
||||
</template>
|
||||
|
||||
<template name="attributeDialogView">
|
||||
{{#if showNewUserExperience}}
|
||||
{{#infoBox}}
|
||||
<p>
|
||||
This dialog shows how your speed is set by the effect you added to your character's race.
|
||||
</p>
|
||||
<p>
|
||||
In DiceCloud you don't change stats directly, rather you add effects which impact your stats in different ways. This way, you can always tell where your stats came from, and how they got to their current value.
|
||||
</p>
|
||||
{{/infoBox}}
|
||||
{{/if}}
|
||||
<div class="layout horizontal center-justified end">
|
||||
<div class="paper-font-display2">
|
||||
{{attributeValue}}
|
||||
|
||||
@@ -157,4 +157,9 @@ Template.attributeDialogView.helpers({
|
||||
statValue: function(){
|
||||
return evaluateEffect(this.charId, this);
|
||||
},
|
||||
showNewUserExperience: function(){
|
||||
if (this.statName === "speed"){
|
||||
return Session.get("newUserExperienceStep") >= 2;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template name="statCard">
|
||||
<div>
|
||||
<paper-material class="stat-card layout horizontal">
|
||||
<paper-material class="stat-card layout horizontal {{#if bounce}}bounce{{/if}}">
|
||||
<div class="numbers paper-font-display1">
|
||||
{{#if isSkill}}
|
||||
{{prefix}}{{skillMod}}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!--Armor-->
|
||||
{{> statCard stat="armor" name="Armor Class" color="teal"}}
|
||||
<!--Speed-->
|
||||
{{> statCard stat="speed" name="Speed" color="teal"}}
|
||||
{{> statCard stat="speed" name="Speed" color="teal" bounce=shouldSpeedBounce}}
|
||||
<!--Initiative-->
|
||||
{{> statCard stat="initiative" name="Initiative" color="indigo" isSkill="true"}}
|
||||
<!--Proficiency Bonus-->
|
||||
|
||||
@@ -8,6 +8,10 @@ Template.stats.helpers({
|
||||
};
|
||||
return Buffs.find(selector);
|
||||
},
|
||||
// New user experience
|
||||
shouldSpeedBounce: function(){
|
||||
return Session.get("newUserExperienceStep") === 2;
|
||||
},
|
||||
})
|
||||
|
||||
Template.stats.events({
|
||||
@@ -84,8 +88,7 @@ Template.stats.events({
|
||||
callback: (result) => {
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
else Meteor.call("giveCondition", this._id, result)
|
||||
} else Meteor.call("giveCondition", this._id, result)
|
||||
},
|
||||
//returnElement: () => $(`[data-id='${itemId}']`).get(0),
|
||||
})
|
||||
|
||||
@@ -49,14 +49,14 @@
|
||||
class="addParty"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New Party </paper-tooltip>
|
||||
{{#simpleTooltip class="always"}} New Party {{/simpleTooltip}}
|
||||
</div>
|
||||
<div>
|
||||
<paper-fab icon="face"
|
||||
class="addCharacter"
|
||||
mini>
|
||||
</paper-fab>
|
||||
<paper-tooltip position="left"> New Character </paper-tooltip>
|
||||
{{#simpleTooltip class="always"}} New Character {{/simpleTooltip}}
|
||||
</div>
|
||||
{{/fabMenu}}
|
||||
</div>
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<div>
|
||||
On the official subreddit
|
||||
</div>
|
||||
<a href="http://www.reddit.com/r/dicecloud/">
|
||||
<a href="http://www.reddit.com/r/dicecloud/" target="_blank">
|
||||
<paper-button class="redditButton">
|
||||
/r/dicecloud
|
||||
</paper-button>
|
||||
@@ -93,14 +93,14 @@
|
||||
</div>
|
||||
<div class="layout vertical center">
|
||||
<div class="paper-font-headline">
|
||||
Get involved
|
||||
Open Source
|
||||
</div>
|
||||
<div>
|
||||
Shape upcoming features and track bugs on the DiceCloud Trello board
|
||||
Shape upcoming features, track bugs, and contribute to the DiceCloud codebase
|
||||
</div>
|
||||
<a href="https://trello.com/b/94M0SCnq/dicecloud-roadmap">
|
||||
<paper-button class="trelloButton">
|
||||
Trello Roadmap
|
||||
<a href="https://github.com/ThaumRystra/DiceCloud1/" target="_blank">
|
||||
<paper-button class="githubButton">
|
||||
GitHub Repo
|
||||
</paper-button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -23,4 +23,17 @@
|
||||
<meta name="msapplication-TileColor" content="#b91d1d">
|
||||
<meta name="msapplication-TileImage" content="/mstile-144x144.png?v=lk6WXp6Pmj">
|
||||
<meta name="theme-color" content="#d12929">
|
||||
|
||||
<style type="text/css" media="print">
|
||||
@page {
|
||||
margin: 0mm;
|
||||
}
|
||||
html {
|
||||
margin: 0px;
|
||||
}
|
||||
* {
|
||||
-webkit-transition: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
transform-origin: top left;
|
||||
transition: top 400ms ease, left 400ms ease;
|
||||
z-index: 3;
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dialog-stack .dialog .testButton {
|
||||
|
||||
15
rpg-docs/client/views/paperTemplates/infoBox/infoBox.css
Normal file
15
rpg-docs/client/views/paperTemplates/infoBox/infoBox.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.infoBox iron-icon {
|
||||
color: #747474;
|
||||
color: rgba(0,0,0,0.54);
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.infoBox > div > p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.infoBox > div > p + p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
10
rpg-docs/client/views/paperTemplates/infoBox/infoBox.html
Normal file
10
rpg-docs/client/views/paperTemplates/infoBox/infoBox.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<template name="infoBox">
|
||||
<div class="layout horizontal center infoBox">
|
||||
<div>
|
||||
<iron-icon icon="info-outline"></iron-icon>
|
||||
</div>
|
||||
<div class="flex">
|
||||
{{> Template.contentBlock}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -25,7 +25,6 @@
|
||||
{{/ simpleTooltip}}
|
||||
</div>
|
||||
<div class="brackets" style="position: relative">
|
||||
<!--<paper-tooltip position="left" animation-delay="0">This field accepts formulae in {curly brackets}</paper-tooltip>-->
|
||||
<iron-icon icon="dicecloud:code-braces"></iron-icon>
|
||||
{{# simpleTooltip}}
|
||||
This field accepts formulae in {curly brackets}
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
.simple-tooltip:hover .tooltip {
|
||||
.simple-tooltip {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.simple-tooltip:active {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Show the tooltip if a older sibling is hovered */
|
||||
*:hover ~ .simple-tooltip > .tooltip {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* Show the tooltip if parent is hovered */
|
||||
*:hover > .simple-tooltip > .tooltip {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@@ -16,3 +30,7 @@
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.tooltip.always {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template name="simpleTooltip">
|
||||
<div class="simple-tooltip fit">
|
||||
<div class="tooltip">
|
||||
<div class="simple-tooltip fit layout vertical center-justified">
|
||||
<div class="tooltip {{class}}">
|
||||
{{> Template.contentBlock}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
.profile #at-nav-button {
|
||||
color: #212121;
|
||||
color: rgba(0,0,0,0.87);
|
||||
.profile paper-button, .profile a, .profile #at-nav-button {
|
||||
color: #d13b2e;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{{#if verified}}
|
||||
<span>
|
||||
<iron-icon icon="check"></iron-icon>
|
||||
<paper-tooltip>Verified</paper-tooltip>
|
||||
{{#simpleTooltip}}Verified{{/simpleTooltip}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
@@ -35,9 +35,36 @@
|
||||
</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<a href="/change-password">
|
||||
<paper-button>Change password</paper-button>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
API Key
|
||||
</td>
|
||||
<td class="apiKey">
|
||||
{{#if apiKey}}
|
||||
{{#unless showApiKey}}
|
||||
<paper-button class="showApiKey">
|
||||
Show
|
||||
</paper-button>
|
||||
{{else}}
|
||||
{{apiKey}}
|
||||
{{/unless}}
|
||||
{{else}}
|
||||
<paper-button class="generateMyApiKey">
|
||||
Generate
|
||||
</paper-button>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="max-width: 250px">
|
||||
{{> atForm}}
|
||||
{{> atForm state="signIn"}}
|
||||
</div>
|
||||
{{> atNavButton }}
|
||||
</paper-material>
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
Template.profile.onCreated(function(){
|
||||
this.showApiKey = new ReactiveVar(false);
|
||||
});
|
||||
|
||||
Template.profile.helpers({
|
||||
profileName: function() {
|
||||
var user = Meteor.user();
|
||||
return user.profile && user.profile.username ||
|
||||
user.username ||
|
||||
"Tap to set username";
|
||||
}
|
||||
},
|
||||
showApiKey: function(){
|
||||
return Template.instance().showApiKey.get();
|
||||
},
|
||||
});
|
||||
|
||||
Template.profile.events({
|
||||
@@ -25,4 +32,11 @@ Template.profile.events({
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
"click .showApiKey": function(event, instance){
|
||||
instance.showApiKey.set(!instance.showApiKey.get());
|
||||
},
|
||||
"click .generateMyApiKey": function(event, instance){
|
||||
Meteor.call("generateMyApiKey");
|
||||
instance.showApiKey.set(true);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<app-header-layout has-scrolling-region class="feedback flex">
|
||||
<app-header fixed effects="waterfall">
|
||||
<app-toolbar>
|
||||
<div main-title>Feedback</div>
|
||||
<div main-title>Change Username</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
<div class="form flex">
|
||||
@@ -49,6 +49,7 @@
|
||||
"/custom_components/dicecloud-wrapper/dicecloud-wrapper.html",
|
||||
"/custom_components/paper-checkbox/paper-checkbox.html",
|
||||
"/custom_components/paper-diff-slider/paper-diff-slider.html",
|
||||
"/custom_components/paper-stepper/paper-stepper.html",
|
||||
"/custom_components/app-theme.html"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6,3 +6,14 @@ abilities = [
|
||||
"wisdom",
|
||||
"charisma",
|
||||
];
|
||||
|
||||
ABILITIES = abilities;
|
||||
|
||||
ABILITY_MODS = [
|
||||
"strengthMod",
|
||||
"dexterityMod",
|
||||
"constitutionMod",
|
||||
"intelligenceMod",
|
||||
"wisdomMod",
|
||||
"charismaMod",
|
||||
];
|
||||
|
||||
34
rpg-docs/lib/constants/attributes.js
Normal file
34
rpg-docs/lib/constants/attributes.js
Normal file
@@ -0,0 +1,34 @@
|
||||
ATTRIBUTES = [
|
||||
"strength",
|
||||
"dexterity",
|
||||
"constitution",
|
||||
"intelligence",
|
||||
"wisdom",
|
||||
"charisma",
|
||||
"hitPoints",
|
||||
"tempHP",
|
||||
"experience",
|
||||
"proficiencyBonus",
|
||||
"speed",
|
||||
"armor",
|
||||
"carryMultiplier",
|
||||
"level1SpellSlots",
|
||||
"level2SpellSlots",
|
||||
"level3SpellSlots",
|
||||
"level4SpellSlots",
|
||||
"level5SpellSlots",
|
||||
"level6SpellSlots",
|
||||
"level7SpellSlots",
|
||||
"level8SpellSlots",
|
||||
"level9SpellSlots",
|
||||
"ki",
|
||||
"sorceryPoints",
|
||||
"rages",
|
||||
"superiorityDice",
|
||||
"expertiseDice",
|
||||
"rageDamage",
|
||||
"d6HitDice",
|
||||
"d8HitDice",
|
||||
"d10HitDice",
|
||||
"d12HitDice",
|
||||
];
|
||||
@@ -19,3 +19,32 @@ SKILLS = [
|
||||
"survival",
|
||||
"initiative",
|
||||
];
|
||||
|
||||
ALL_SKILLS = [
|
||||
"strengthSave",
|
||||
"dexteritySave",
|
||||
"constitutionSave",
|
||||
"intelligenceSave",
|
||||
"wisdomSave",
|
||||
"charismaSave",
|
||||
"acrobatics",
|
||||
"animalHandling",
|
||||
"arcana",
|
||||
"athletics",
|
||||
"deception",
|
||||
"history",
|
||||
"insight",
|
||||
"intimidation",
|
||||
"investigation",
|
||||
"medicine",
|
||||
"nature",
|
||||
"perception",
|
||||
"performance",
|
||||
"persuasion",
|
||||
"religion",
|
||||
"sleightOfHand",
|
||||
"stealth",
|
||||
"survival",
|
||||
"initiative",
|
||||
"dexterityArmor",
|
||||
];
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
subsManager = new SubsManager();
|
||||
subsManager = new SubsManager({
|
||||
// maximum number of cache subscriptions
|
||||
cacheLimit: 5,
|
||||
// any subscription will be expire after 1 minute, if it's not subscribed again
|
||||
expireIn: 1,
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ AccountsTemplates.configure({
|
||||
//behaviour
|
||||
confirmPassword: true,
|
||||
enablePasswordChange: true,
|
||||
enforceEmailVerification: true,
|
||||
enforceEmailVerification: false,
|
||||
overrideLoginErrors: false,
|
||||
sendVerificationEmail: true,
|
||||
lowercaseUsername: true,
|
||||
@@ -21,35 +21,35 @@ AccountsTemplates.configure({
|
||||
|
||||
AccountsTemplates.configureRoute("changePwd", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("enrollAccount", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("forgotPwd", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("resetPwd", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("signIn", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("signUp", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("verifyEmail", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
AccountsTemplates.configureRoute("resendVerificationEmail", {
|
||||
template: "titledAtForm",
|
||||
layoutTemplate: 'layout',
|
||||
layoutTemplate: "layout",
|
||||
});
|
||||
|
||||
if (Meteor.isServer){
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
characterExport = function(charId){
|
||||
var char = Characters.findOne(charId);
|
||||
var {
|
||||
character, classes, effects, proficiencies,
|
||||
} = getCharacterForComputation(charId);
|
||||
var char = character;
|
||||
computedCharacter = computeCharacter({
|
||||
character, classes, effects, proficiencies,
|
||||
});
|
||||
if (!char) {
|
||||
return {
|
||||
error: charId + " character not found"
|
||||
@@ -11,25 +17,87 @@ characterExport = function(charId){
|
||||
};
|
||||
}
|
||||
var baseValue = function(attributeName){
|
||||
return Characters.calculate.attributeBase(charId, attributeName);
|
||||
var attribute = computedCharacter[attributeName];
|
||||
return attribute && attribute.value;
|
||||
};
|
||||
var attributeValue = function(attributeName){
|
||||
return Characters.calculate.attributeValue(charId, attributeName);
|
||||
var base = baseValue(attributeName);
|
||||
var adjustment = char[attributeName] && char[attributeName].adjustment;
|
||||
return base + adjustment;
|
||||
};
|
||||
var abilityMod = function(attributeName){
|
||||
return signedString(
|
||||
Characters.calculate.abilityMod(charId, attributeName)
|
||||
);
|
||||
return signedString(getMod(attributeValue(attributeName)));
|
||||
};
|
||||
var skillMod = function(skillName){
|
||||
return signedString(
|
||||
Characters.calculate.skillMod(charId, skillName)
|
||||
);
|
||||
return signedString(baseValue(skillName));
|
||||
};
|
||||
var proficiency = function(skillName){
|
||||
return Characters.calculate.proficiency(charId, skillName);
|
||||
var skill = computedCharacter[skillName];
|
||||
return skill && skill.proficiency;
|
||||
};
|
||||
var passiveSkill = function(skillName){
|
||||
var attribute = computedCharacter[skillName];
|
||||
if (!attribute) return;
|
||||
return 10 + baseValue(skillName) + attribute.passiveAdd;
|
||||
};
|
||||
var experience = function(){
|
||||
var xp = 0;
|
||||
Experiences.find(
|
||||
{charId: charId},
|
||||
{fields: {value: 1}}
|
||||
).forEach(function(e){
|
||||
xp += e.value;
|
||||
});
|
||||
return xp;
|
||||
};
|
||||
var getClasses = function(){
|
||||
return _.map(classes, c => `${c.name} ${c.level}`).join(", ");
|
||||
};
|
||||
var getHitDiceString = function(){
|
||||
var d6 = baseValue("d6HitDice");
|
||||
var d8 = baseValue("d8HitDice");
|
||||
var d10 = baseValue("d10HitDice");
|
||||
var d12 = baseValue("d12HitDice");
|
||||
var con = abilityMod("constitution");
|
||||
var string = "" +
|
||||
(d6 ? `${d6}d6 + ` : "") +
|
||||
(d8 ? `${d8}d8 + ` : "") +
|
||||
(d10 ? `${d10}d10 + ` : "") +
|
||||
(d12 ? `${d12}d12 + ` : "") +
|
||||
con;
|
||||
return string;
|
||||
}
|
||||
var damageMods = getDamageMods(charId);
|
||||
var getSkills = function(charId){
|
||||
var allSkills = [
|
||||
{name: "acrobatics", attribute: "dexterity"},
|
||||
{name: "animalHandling", attribute: "wisdom"},
|
||||
{name: "arcana", attribute: "intelligence"},
|
||||
{name: "athletics", attribute: "strength"},
|
||||
{name: "deception", attribute: "charisma"},
|
||||
{name: "history", attribute: "intelligence"},
|
||||
{name: "insight", attribute: "wisdom"},
|
||||
{name: "intimidation", attribute: "charisma"},
|
||||
{name: "investigation", attribute: "intelligence"},
|
||||
{name: "medicine", attribute: "wisdom"},
|
||||
{name: "nature", attribute: "intelligence"},
|
||||
{name: "perception", attribute: "wisdom"},
|
||||
{name: "performance", attribute: "charisma"},
|
||||
{name: "persuasion", attribute: "charisma"},
|
||||
{name: "religion", attribute: "intelligence"},
|
||||
{name: "sleightOfHand", attribute: "dexterity"},
|
||||
{name: "stealth", attribute: "dexterity"},
|
||||
{name: "survival", attribute: "wisdom"},
|
||||
];
|
||||
var skills = {};
|
||||
_.each(allSkills, skill => {
|
||||
var value = skillMod(skill.name);
|
||||
var prof = proficiency(skill.name);
|
||||
var name = skill.name.charAt(0).toUpperCase() + skill.name.slice(1);
|
||||
skills[name] = value;
|
||||
skills[name + "Proficiency"] = prof;
|
||||
});
|
||||
return skills;
|
||||
};
|
||||
var character = {
|
||||
"Id": char._id,
|
||||
"Name": char.name,
|
||||
@@ -37,8 +105,8 @@ characterExport = function(charId){
|
||||
"Alignment": char.alignment || "",
|
||||
"Gender": char.gender || "",
|
||||
"Race": char.race || "",
|
||||
"Level": Characters.calculate.level(charId),
|
||||
"Experience": Characters.calculate.experience(charId),
|
||||
"Level": _.reduce(classes, (memo, cls) => memo + cls.level, 0),
|
||||
"Experience": experience(),
|
||||
"Class": getClasses(charId),
|
||||
"HPBase": baseValue("hitPoints"),
|
||||
"HPValue": attributeValue("hitPoints"),
|
||||
@@ -47,7 +115,7 @@ characterExport = function(charId){
|
||||
"Initiative": skillMod("initiative"),
|
||||
"Speed": attributeValue("speed"),
|
||||
"ProficiencyBonus": attributeValue("proficiencyBonus"),
|
||||
"passivePerception": Characters.calculate.passiveSkill(charId, "perception"),
|
||||
"passivePerception": passiveSkill("perception"),
|
||||
|
||||
"Languages": getLanguages(charId),
|
||||
"Description": char.description || "",
|
||||
@@ -72,9 +140,9 @@ characterExport = function(charId){
|
||||
"WisdomMod": abilityMod("wisdom"),
|
||||
"CharismaMod": abilityMod("charisma"),
|
||||
|
||||
"DamageVulnerabilities": damageMods.vulnerabilities,
|
||||
"DamageResistances": damageMods.resistances,
|
||||
"DamageImmunities": damageMods.immunities,
|
||||
//"DamageVulnerabilities": damageMods.vulnerabilities,
|
||||
//"DamageResistances": damageMods.resistances,
|
||||
//"DamageImmunities": damageMods.immunities,
|
||||
|
||||
"StrengthSave": skillMod("strengthSave"),
|
||||
"StrengthSaveProficiency": proficiency("strengthSave"),
|
||||
@@ -112,21 +180,6 @@ characterExport = function(charId){
|
||||
return character;
|
||||
}
|
||||
|
||||
var getHitDiceString = function(charId){
|
||||
var d6 = Characters.calculate.attributeBase(charId, "d6HitDice");
|
||||
var d8 = Characters.calculate.attributeBase(charId, "d8HitDice");
|
||||
var d10 = Characters.calculate.attributeBase(charId, "d10HitDice");
|
||||
var d12 = Characters.calculate.attributeBase(charId, "d12HitDice");
|
||||
var con = Characters.calculate.abilityMod(charId,"constitution");
|
||||
var string = "" +
|
||||
(d6 ? `${d6}d6 + ` : "") +
|
||||
(d8 ? `${d8}d8 + ` : "") +
|
||||
(d10 ? `${d10}d10 + ` : "") +
|
||||
(d12 ? `${d12}d12 + ` : "") +
|
||||
con;
|
||||
return string;
|
||||
}
|
||||
|
||||
var getArmorString = function(charId){
|
||||
var bases = Effects.find({
|
||||
charId: charId,
|
||||
@@ -148,7 +201,7 @@ var getArmorString = function(charId){
|
||||
strings = strings.concat(effects);
|
||||
return strings.join(", ");
|
||||
}
|
||||
|
||||
/*
|
||||
var getDamageMods = function(charId){
|
||||
// jscs:disable maximumLineLength
|
||||
var multipliers = [
|
||||
@@ -175,40 +228,7 @@ var getDamageMods = function(charId){
|
||||
"vulnerabilities": _.map(multipliers["2"], names).join(", "),
|
||||
};
|
||||
}
|
||||
|
||||
var getSkills = function(charId){
|
||||
var allSkills = [
|
||||
{name: "acrobatics", attribute: "dexterity"},
|
||||
{name: "animalHandling", attribute: "wisdom"},
|
||||
{name: "arcana", attribute: "intelligence"},
|
||||
{name: "athletics", attribute: "strength"},
|
||||
{name: "deception", attribute: "charisma"},
|
||||
{name: "history", attribute: "intelligence"},
|
||||
{name: "insight", attribute: "wisdom"},
|
||||
{name: "intimidation", attribute: "charisma"},
|
||||
{name: "investigation", attribute: "intelligence"},
|
||||
{name: "medicine", attribute: "wisdom"},
|
||||
{name: "nature", attribute: "intelligence"},
|
||||
{name: "perception", attribute: "wisdom"},
|
||||
{name: "performance", attribute: "charisma"},
|
||||
{name: "persuasion", attribute: "charisma"},
|
||||
{name: "religion", attribute: "intelligence"},
|
||||
{name: "sleightOfHand", attribute: "dexterity"},
|
||||
{name: "stealth", attribute: "dexterity"},
|
||||
{name: "survival", attribute: "wisdom"},
|
||||
];
|
||||
var skills = {};
|
||||
_.each(allSkills, skill => {
|
||||
var value = signedString(
|
||||
Characters.calculate.skillMod(charId, skill.name)
|
||||
);
|
||||
var prof = Characters.calculate.proficiency(charId, skill.name);
|
||||
var name = skill.name.charAt(0).toUpperCase() + skill.name.slice(1);
|
||||
skills[name] = value;
|
||||
skills[name + "Proficiency"] = prof;
|
||||
});
|
||||
return skills;
|
||||
};
|
||||
*/
|
||||
|
||||
var getLanguages = function(charId){
|
||||
return Proficiencies.find({
|
||||
@@ -218,10 +238,6 @@ var getLanguages = function(charId){
|
||||
}).map(l => l.name).join(", ");
|
||||
};
|
||||
|
||||
var getClasses = function(charId){
|
||||
return Classes.find({charId}).map(c => `${c.name} ${c.level}`).join(", ");
|
||||
};
|
||||
|
||||
var getAttacks = function(charId){
|
||||
var attacks = {};
|
||||
var i = 1;
|
||||
|
||||
224
rpg-docs/lib/functions/computeCharacter.js
Normal file
224
rpg-docs/lib/functions/computeCharacter.js
Normal file
@@ -0,0 +1,224 @@
|
||||
getCharacterForComputation = function(charId){
|
||||
const character = Characters.findOne(charId);
|
||||
const classes = Classes.find({charId}).fetch();
|
||||
const effects = Effects.find({charId, enabled: true}).fetch();
|
||||
const proficiencies = Proficiencies.find({
|
||||
charId,
|
||||
enabled: true,
|
||||
type: {$in: ["skill", "save"]},
|
||||
}).fetch();
|
||||
return {character, classes, effects, proficiencies};
|
||||
}
|
||||
|
||||
computeCharacter = function({character, classes, effects, proficiencies}){
|
||||
var charId = character._id;
|
||||
let computedClasses = computeCharacterClasses(charId, classes);
|
||||
let changed = false;
|
||||
computedCharacter = {};
|
||||
let i;
|
||||
for (i = 0; i < 15; i++){
|
||||
[computedCharacter, changed] = compute({
|
||||
classes: computedClasses,
|
||||
oldChar: computedCharacter,
|
||||
charId,
|
||||
character,
|
||||
effects,
|
||||
proficiencies,
|
||||
});
|
||||
if (!changed) break;
|
||||
}
|
||||
return computedCharacter;
|
||||
};
|
||||
|
||||
var ensureCharacterExists = (character) => {
|
||||
if (!character) {
|
||||
throw new Meteor.Error("Character doesn't exist",
|
||||
"You can't recompute a character that doesn't exist");
|
||||
}
|
||||
};
|
||||
|
||||
var ensureWritePermissions = (character, userId) => {
|
||||
if (
|
||||
userId &&
|
||||
userId !== character.owner &&
|
||||
!_.contains(character.writers, userId)
|
||||
){
|
||||
throw new Meteor.Error("Character write denied",
|
||||
"You don't have permission to recompute this character");
|
||||
}
|
||||
};
|
||||
|
||||
var computeCharacterClasses = function(charId, classes){
|
||||
let computedClasses = {};
|
||||
_.each(classes, (cls) => {
|
||||
if (computedClasses[cls.name]){
|
||||
computedClasses[cls.name].level += cls.level;
|
||||
} else {
|
||||
computedClasses[cls.name] = cls;
|
||||
}
|
||||
});
|
||||
return computedClasses;
|
||||
}
|
||||
|
||||
var compute = function({
|
||||
charId, oldChar, character, classes, effects, proficiencies,
|
||||
}){
|
||||
let newChar = {};
|
||||
_.each(effects, (effect, index) => {
|
||||
if (!effect.stat || effect.operation === "conditional") return;
|
||||
if (!newChar[effect.stat]) newChar[effect.stat] = defaultStat();
|
||||
let value = effect.calculation ?
|
||||
computeEffect(effect.calculation, oldChar, classes) :
|
||||
effect.value || 0;
|
||||
let stat = newChar[effect.stat];
|
||||
if (!_.isNumber(value)) return;
|
||||
switch (effect.operation) {
|
||||
case "base":
|
||||
if (value > stat.base) stat.base = value;
|
||||
break;
|
||||
case "proficiency":
|
||||
if (value > stat.proficiency) stat.proficiency = value;
|
||||
break;
|
||||
case "add":
|
||||
stat.add += value;
|
||||
break;
|
||||
case "mul":
|
||||
stat.mul *= value;
|
||||
break;
|
||||
case "min":
|
||||
if (value > stat.min) stat.min = value;
|
||||
break;
|
||||
case "max":
|
||||
if (value < stat.max) stat.max = value;
|
||||
break;
|
||||
case "advantage":
|
||||
stat.advantage++;
|
||||
break;
|
||||
case "disadvantage":
|
||||
stat.disadvantage++;
|
||||
break;
|
||||
case "passiveAdd":
|
||||
stat.passiveAdd += value;
|
||||
break;
|
||||
case "fail":
|
||||
stat.fail = true;
|
||||
break;
|
||||
}
|
||||
});
|
||||
_.each(proficiencies, proficiency => {
|
||||
if (!proficiency.name) return;
|
||||
if (!newChar[proficiency.name]) newChar[proficiency.name] = defaultStat();
|
||||
let stat = newChar[proficiency.name];
|
||||
let value = proficiency.value;
|
||||
if (value > stat.proficiency) stat.proficiency = value;
|
||||
});
|
||||
let changed = false;
|
||||
_.each(ATTRIBUTES, function(statName) {
|
||||
if (!newChar[statName]) newChar[statName] = defaultStat();
|
||||
let stat = newChar[statName];
|
||||
stat.value = (stat.base + stat.add) * stat.mul;
|
||||
if (stat.value < stat.min) stat.value = stat.min;
|
||||
if (stat.value > stat.max) stat.value = stat.max;
|
||||
if (!_.isEqual(stat.value, oldChar[statName] && oldChar[statName].value)){
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
_.each(ALL_SKILLS, function(statName) {
|
||||
if (!newChar[statName]) newChar[statName] = defaultStat();
|
||||
let stat = newChar[statName];
|
||||
stat.value = characterAbilityMod(
|
||||
oldChar, character[statName] && character[statName].ability
|
||||
);
|
||||
stat.value += stat.base + stat.add;
|
||||
stat.value += stat.proficiency *
|
||||
characterFieldValue(oldChar, "proficiencyBonus");
|
||||
stat.value *= stat.mul;
|
||||
if (stat.value < stat.min) stat.value = stat.min;
|
||||
if (stat.value > stat.max) stat.value = stat.max;
|
||||
if (!_.isEqual(stat.value, oldChar[statName] && oldChar[statName].value)){
|
||||
changed = true;
|
||||
}
|
||||
});
|
||||
return [newChar, changed];
|
||||
};
|
||||
|
||||
var defaultStat = function(){
|
||||
return {
|
||||
base: 0,
|
||||
proficiency: 0,
|
||||
add: 0,
|
||||
mul: 1,
|
||||
min: Number.NEGATIVE_INFINITY,
|
||||
max: Number.POSITIVE_INFINITY,
|
||||
advantage: 0,
|
||||
disadvantage: 0,
|
||||
passiveAdd: 0,
|
||||
fail: false,
|
||||
}
|
||||
}
|
||||
|
||||
var computeEffect = function(string, character, classes){
|
||||
if (!string) return string;
|
||||
string = string.replace(/\b[a-z][\w]+/gi, function(sub){
|
||||
//fields
|
||||
if (character[sub]){
|
||||
return characterFieldValue(character, sub);
|
||||
}
|
||||
//ability modifiers
|
||||
if (_.contains(ABILITY_MODS, sub)){
|
||||
var slice = sub.slice(0, -3);
|
||||
return getMod(
|
||||
character[slice] ? characterFieldValue(character, slice) : 0
|
||||
);
|
||||
}
|
||||
//class levels
|
||||
if (/\w+levels?\b/gi.test(sub)){
|
||||
//strip out "level"
|
||||
var className = sub.replace(/levels?\b/gi, "");
|
||||
return characterClassLevel(classes, className)
|
||||
}
|
||||
//character level
|
||||
if (sub.toUpperCase() === "LEVEL"){
|
||||
return characterTotalLevel(classes);
|
||||
}
|
||||
// exclude math functions
|
||||
if (math[sub]){
|
||||
return sub;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
try {
|
||||
var result = math.eval(string);
|
||||
return result;
|
||||
} catch (e){
|
||||
return string;
|
||||
}
|
||||
};
|
||||
|
||||
var characterFieldValue = function(character, field){
|
||||
if (_.isNumber(character[field] && character[field].value)){
|
||||
return character[field].value;
|
||||
} else {
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
var characterClassLevel = function(classes, className){
|
||||
if (_.isNumber(classes[className] && classes[className].level)){
|
||||
return classes[className].level;
|
||||
} else {
|
||||
return className;
|
||||
}
|
||||
};
|
||||
|
||||
var characterTotalLevel = function(classes){
|
||||
return _.reduce(classes, (memo, cls) => memo + cls.level, 0);
|
||||
};
|
||||
|
||||
var characterAbilityMod = function(character, abilityName){
|
||||
if (_.isNumber(character[abilityName] && character[abilityName].value)){
|
||||
return getMod(character[abilityName].value);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
var childSchema = new SimpleSchema({
|
||||
parent: {type: Object},
|
||||
"parent.collection": {type: String},
|
||||
"parent.id": {type: String, regEx: SimpleSchema.RegEx.Id},
|
||||
"parent.id": {type: String, regEx: SimpleSchema.RegEx.Id, index: 1},
|
||||
"parent.group": {type: String, optional: true},
|
||||
"removedWith": {
|
||||
optional: true,
|
||||
|
||||
1901
rpg-docs/package-lock.json
generated
Normal file
1901
rpg-docs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,10 @@
|
||||
"dependencies": {
|
||||
"@polymer/polymer": "^1.2.5-npm-test.2",
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bcrypt": "^1.0.2",
|
||||
"bower": "^1.7.9"
|
||||
"bcrypt": "^1.0.3",
|
||||
"bower": "^1.7.9",
|
||||
"core-js": "^2.5.1",
|
||||
"meteor-node-stubs": "^0.3.2",
|
||||
"qrcode": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../components/neon-animation/neon-animation-behavior.html">
|
||||
<link rel="import" href="../../../components/neon-animation/web-animations.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'fade-in-slide-from-left-animation',
|
||||
|
||||
behaviors: [
|
||||
Polymer.NeonAnimationBehavior
|
||||
],
|
||||
|
||||
configure: function(config) {
|
||||
var node = config.node;
|
||||
|
||||
this._effect = new KeyframeEffect(node, [
|
||||
{'transform': 'translateX(-100%)', 'opacity': '0'},
|
||||
{'transform': 'translateX(-50%)', 'opacity': '0'},
|
||||
{'transform': 'none', 'opacity': '1'}
|
||||
], this.timingFromConfig(config));
|
||||
|
||||
if (config.transformOrigin) {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
||||
} else {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
||||
}
|
||||
|
||||
return this._effect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../components/neon-animation/neon-animation-behavior.html">
|
||||
<link rel="import" href="../../../components/neon-animation/web-animations.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'fade-in-slide-from-right-animation',
|
||||
|
||||
behaviors: [
|
||||
Polymer.NeonAnimationBehavior
|
||||
],
|
||||
|
||||
configure: function(config) {
|
||||
var node = config.node;
|
||||
|
||||
this._effect = new KeyframeEffect(node, [
|
||||
{'transform': 'translateX(100%)', 'opacity': '0'},
|
||||
{'transform': 'translateX(50%)', 'opacity': '0'},
|
||||
{'transform': 'none', 'opacity': '1'}
|
||||
], this.timingFromConfig(config));
|
||||
|
||||
if (config.transformOrigin) {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
||||
} else {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
||||
}
|
||||
|
||||
return this._effect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../components/neon-animation/neon-animation-behavior.html">
|
||||
<link rel="import" href="../../../components/neon-animation/web-animations.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'fade-out-slide-left-animation',
|
||||
|
||||
behaviors: [
|
||||
Polymer.NeonAnimationBehavior
|
||||
],
|
||||
|
||||
configure: function(config) {
|
||||
var node = config.node;
|
||||
|
||||
this._effect = new KeyframeEffect(node, [
|
||||
{'transform': 'none', 'opacity': '1'},
|
||||
{'transform': 'translateX(-50%)', 'opacity': '0'},
|
||||
{'transform': 'translateX(-100%)', 'opacity': '0'},
|
||||
], this.timingFromConfig(config));
|
||||
|
||||
if (config.transformOrigin) {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
||||
} else {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
||||
}
|
||||
|
||||
return this._effect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,45 @@
|
||||
<!--
|
||||
@license
|
||||
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
<link rel="import" href="../../../components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../components/neon-animation/neon-animation-behavior.html">
|
||||
<link rel="import" href="../../../components/neon-animation/web-animations.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
|
||||
is: 'fade-out-slide-right-animation',
|
||||
|
||||
behaviors: [
|
||||
Polymer.NeonAnimationBehavior
|
||||
],
|
||||
|
||||
configure: function(config) {
|
||||
var node = config.node;
|
||||
|
||||
this._effect = new KeyframeEffect(node, [
|
||||
{'transform': 'none', 'opacity': '1'},
|
||||
{'transform': 'translateX(50%)', 'opacity': '1'},
|
||||
{'transform': 'translateX(100%)', 'opacity': '0'}
|
||||
], this.timingFromConfig(config));
|
||||
|
||||
if (config.transformOrigin) {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', config.transformOrigin);
|
||||
} else {
|
||||
this.setPrefixedProperty(node, 'transformOrigin', '0 50%');
|
||||
}
|
||||
|
||||
return this._effect;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
48
rpg-docs/public/custom_components/paper-stepper/bower.json
Normal file
48
rpg-docs/public/custom_components/paper-stepper/bower.json
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "paper-stepper",
|
||||
"version": "2.0-beta.5",
|
||||
"authors": [
|
||||
"Zecat <jullienfelix@gmail.com>"
|
||||
],
|
||||
"description": "Material paper-stepper element.",
|
||||
"keywords": [
|
||||
"web-component",
|
||||
"polymer",
|
||||
"seed"
|
||||
],
|
||||
"main": "paper-stepper.html",
|
||||
"license": "http://polymer.github.io/LICENSE.txt",
|
||||
"homepage": "https://github.com/zecat/paper-stepper/",
|
||||
"ignore": [
|
||||
"/.*",
|
||||
"/test/"
|
||||
],
|
||||
"dependencies": {
|
||||
"polymer": "Polymer/polymer#^1.2.0",
|
||||
"paper-button": "PolymerElements/paper-button#^1.0.11",
|
||||
"iron-icons": "PolymerElements/iron-icons#^1.1.3",
|
||||
"paper-styles": "PolymerElements/paper-styles#^1.1.4",
|
||||
"paper-ripple": "PolymerElements/paper-ripple#^1.0.5",
|
||||
"iron-selector": "PolymerElements/iron-selector#^1.3.0",
|
||||
"iron-icon": "PolymerElements/iron-icon#^1.0.8",
|
||||
"iron-flex-layout": "PolymerElements/iron-flex-layout#^1.3.1",
|
||||
"neon-animation": "PolymerElements/neon-animation#^1.1.1",
|
||||
"iron-validatable-behavior": "PolymerElements/iron-validatable-behavior#^1.0.5",
|
||||
"iron-collapse": "PolymerElements/iron-collapse#^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"paper-input": "PolymerElements/paper-input#^1.1.10",
|
||||
"paper-material": "PolymerElements/paper-material#^1.0.6",
|
||||
"iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
|
||||
"web-component-tester": "*",
|
||||
"iron-form": "PolymerElements/iron-form#^1.0.16",
|
||||
"iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.2",
|
||||
"paper-toggle-button": "PolymerElements/paper-toggle-button#^1.2.0",
|
||||
"app-layout": "PolymerElements/app-layout#^0.10.2",
|
||||
"paper-menu": "PolymerElements/paper-menu#^1.2.2",
|
||||
"iron-scroll-spy": "Zecat/iron-scroll-spy#^2.1.0",
|
||||
"paper-item": "PolymerElements/paper-item#^1.2.1",
|
||||
"paper-toast": "PolymerElements/paper-toast#^1.3.0",
|
||||
"paper-checkbox": "PolymerElements/paper-checkbox#^1.4.0"
|
||||
}
|
||||
}
|
||||
27
rpg-docs/public/custom_components/paper-stepper/hero.svg
Normal file
27
rpg-docs/public/custom_components/paper-stepper/hero.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 225 126" enable-background="new 0 0 225 126" xml:space="preserve">
|
||||
<g id="background" display="none">
|
||||
<rect display="inline" fill="#B0BEC5" width="225" height="126"/>
|
||||
</g>
|
||||
<g id="label">
|
||||
</g>
|
||||
<g id="art">
|
||||
<g id="ic_x5F_add_x0D_">
|
||||
</g>
|
||||
<circle cx="78" cy="98" r="4"/>
|
||||
<circle cx="171" cy="90" r="4"/>
|
||||
<circle cx="132" cy="61" r="4"/>
|
||||
<circle cx="53" cy="61" r="4"/>
|
||||
<circle cx="126" cy="28" r="4"/>
|
||||
<circle cx="91" cy="67" r="4"/>
|
||||
<circle cx="132" cy="90" r="4"/>
|
||||
<circle cx="65" cy="32" r="4"/>
|
||||
<path d="M77.7,99.4L51.9,61.1L64.3,31l64.1-4.2L92.6,66.7l16.1,9l23.3-16L174,91h-42.3l-23-12.9L77.7,99.4z M54.1,60.9l24.1,35.7
|
||||
L106.8,77l-17.4-9.8l34.2-38.1L65.7,33L54.1,60.9z M132.3,89H168l-36-26.8l-21.4,14.6L132.3,89z"/>
|
||||
</g>
|
||||
<g id="Guides">
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
424
rpg-docs/public/custom_components/paper-stepper/paper-step.html
Normal file
424
rpg-docs/public/custom_components/paper-stepper/paper-step.html
Normal file
@@ -0,0 +1,424 @@
|
||||
<link rel="import" href="../../components/iron-validatable-behavior/iron-validatable-behavior.html">
|
||||
<link rel="import" href="../../components/paper-item/paper-item-behavior.html">
|
||||
<link rel="import" href="../../components/paper-behaviors/paper-ripple-behavior.html">
|
||||
<link rel="import" href="../../components/neon-animation/neon-animatable-behavior.html">
|
||||
<link rel="import" href="../../components/paper-styles/default-theme.html">
|
||||
<link rel="import" href="step-horizontal-label.html">
|
||||
<link rel="import" href="step-vertical.html">
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
|
||||
<!--
|
||||
Missing Doc
|
||||
@element paper-step
|
||||
@demo demo/index.html
|
||||
@hero hero.svg
|
||||
-->
|
||||
<dom-module id="paper-step">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
}
|
||||
:host(:not([vertical])) {
|
||||
overflow: hidden;
|
||||
@apply(--layout);
|
||||
@apply(--layout-flex);
|
||||
}
|
||||
:host(:not([vertical])[opened]){
|
||||
overflow: visible;
|
||||
}
|
||||
:host(:not([opened]):not(.neon-animating)) #slideshowViewport{
|
||||
display: none;
|
||||
}
|
||||
#slideshowViewport {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#contentWrapper {
|
||||
padding: 16px 0 16px 16px;
|
||||
@apply(--layout-scroll);
|
||||
@apply(--layout-fit);
|
||||
pointer-events: auto;
|
||||
}
|
||||
:host(:focus:not([opened]):not([saved])) step-horizontal-label ::content #badge {
|
||||
background: var(--paper-step-selectable-hovered-badge-background, --paper-grey-500);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- horizontal step layout -->
|
||||
|
||||
<template is="dom-if" if="[[!vertical]]">
|
||||
<step-horizontal-label id="horizontalStepLabel" editable="[[editable]]" label="[[label]]"
|
||||
alternative-label="[[_alternativeLabel]]" optional="[[optional]]"
|
||||
opened="[[opened]]" selectable="[[selectable]]" index="[[index]]"
|
||||
saved="[[saved]]" stepper-data="[[_stepperData]]">
|
||||
</step-horizontal-label>
|
||||
<div id="slideshowViewport">
|
||||
<div id="contentWrapper">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- vertical step layout -->
|
||||
|
||||
<template is="dom-if" if="[[vertical]]">
|
||||
<step-vertical id="verticalStepLabel" editable="[[editable]]" label="[[label]]"
|
||||
optional="[[optional]]" opened="[[opened]]"
|
||||
selectable="[[selectable]]" stepper-data="[[_stepperData]]" index="[[index]]"
|
||||
saved="[[saved]]" _attr-for-primary-button-text="[[_attrForPrimaryButtonText]]"
|
||||
can-skip="[[_canSkip]]" has-back-step="[[_hasBackStep]]">
|
||||
</step-vertical>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
Polymer({
|
||||
|
||||
is: 'paper-step',
|
||||
|
||||
behaviors: [
|
||||
Polymer.IronValidatableBehavior,
|
||||
Polymer.NeonAnimatableBehavior,
|
||||
Polymer.PaperItemBehavior,
|
||||
Polymer.PaperRippleBehavior
|
||||
],
|
||||
|
||||
/**
|
||||
* Fired when the step has been saved.
|
||||
*
|
||||
* @event paper-step-saved
|
||||
*/
|
||||
/**
|
||||
* Fired when the step has been updated
|
||||
*
|
||||
* @event paper-step-updated
|
||||
*/
|
||||
properties: {
|
||||
/**
|
||||
* MISSING Doc
|
||||
*/
|
||||
saved: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
notify: true,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
editable: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
index: {
|
||||
type: Number,
|
||||
notify: true,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
_previousSaved: {
|
||||
type: Boolean,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
optional: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
selectable: {
|
||||
type: Boolean,
|
||||
computed: '_computeSelectable(_stepperData.linear, saved, editable, _previousSaved)',
|
||||
reflectToAttribute: true,
|
||||
notify: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
disabled: {
|
||||
computed: '_computeDisabled(selectable)'
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
label: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
opened: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
animationConfig: {
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
entryAnimation: {
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
exitAnimation: {
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
readOnly: true,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
horizontalHigherEntryAnimation: {
|
||||
type: String
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
horizontalHigherExitAnimation: {
|
||||
type: String
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
horizontalLowerEntryAnimation: {
|
||||
type: String
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
horizontalLowerExitAnimation: {
|
||||
type: String
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
_alternativeLabel: {
|
||||
type: Boolean,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
_optionalText: {
|
||||
type: Boolean,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
_attrForPrimaryButtonText: {
|
||||
type: String,
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* A reference to the parent stepper
|
||||
*/
|
||||
_stepper: {
|
||||
type: Object,
|
||||
readOnly: true
|
||||
},
|
||||
_stepperData: {
|
||||
type: Object,
|
||||
readOnly: true
|
||||
},
|
||||
_canSkip: {
|
||||
type: Boolean,
|
||||
readOnly: true
|
||||
},
|
||||
_hasBackStep: {
|
||||
type: Boolean,
|
||||
readOnly: true
|
||||
}
|
||||
},
|
||||
|
||||
listeners: {
|
||||
'paper-step-vertical-skip-tapped': 'skip',
|
||||
'paper-step-vertical-back-tapped': 'back',
|
||||
'paper-step-vertical-continue-tapped': 'continue',
|
||||
'tap': '_tapHandler'
|
||||
},
|
||||
|
||||
observers: [
|
||||
'_toggleClassPosition(index, _stepperData.stepNumber, vertical)',
|
||||
'_updateSlideshowViewportTop(optional, _alternativeLabel, vertical)',
|
||||
'_verticalChange(vertical)',
|
||||
'_focusedChanged(receivedFocusFromKeyboard)',
|
||||
'_labelElementChanged(_labelElement)'
|
||||
],
|
||||
|
||||
_focusedChanged: function(receivedFocusFromKeyboard) {
|
||||
if (receivedFocusFromKeyboard) {
|
||||
this.ensureRipple();
|
||||
// generate a ripple effect from the center of the badge
|
||||
var badge = Polymer.dom(this._rippleContainer).querySelector('#badge');
|
||||
var badgePos = badge.getBoundingClientRect();
|
||||
var rippleX = badgePos.left + 12;
|
||||
var rippleY = badgePos.top + 12;
|
||||
this._ripple.downAction({detail: {x: rippleX, y: rippleY}});
|
||||
}
|
||||
if (this.hasRipple()) {
|
||||
this._ripple.holdDown = receivedFocusFromKeyboard;
|
||||
}
|
||||
},
|
||||
|
||||
_tapHandler: function(e) {
|
||||
var rootTarget = Polymer.dom(e).rootTarget;
|
||||
if (rootTarget !== this && rootTarget !== this._rippleContainer) {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
skip: function() {
|
||||
// would it be better to send an event?
|
||||
this._stepper.progress();
|
||||
},
|
||||
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
back: function() {
|
||||
// would it be better to send an event?
|
||||
this._stepper.back();
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark the step as saved and fire `paper-step-saved` if it is valid.
|
||||
* @return {Boolean} True if the step has been saved.
|
||||
*/
|
||||
save: function() {
|
||||
if ((!this.saved || this.editable) && this.validate()) {
|
||||
if (this.saved) {
|
||||
this.fire('paper-step-updated');
|
||||
} else {
|
||||
this._setSaved(true);
|
||||
this.fire('paper-step-saved');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Atempte to save the step and select and to progress into the stepper.
|
||||
* @return {Boolean} True if the step is valid for saving.
|
||||
*/
|
||||
continue: function() {
|
||||
if (this.save()) {
|
||||
// would it be better to send an event?
|
||||
this._stepper.progress();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_removeAnimatingClass: function() {
|
||||
if (this._animationCanceled) {
|
||||
this._set_animationCanceled(false);
|
||||
} else {
|
||||
this.toggleClass('neon-animating', false);
|
||||
}
|
||||
},
|
||||
|
||||
_cancelAnimation: function() {
|
||||
this._set_animationCanceled(true);
|
||||
this.toggleClass('neon-animating', false);
|
||||
},
|
||||
|
||||
_updateSlideshowViewportTop: function(optional, _alternativeLabel, vertical) {
|
||||
if (!vertical) {
|
||||
this.async(function() {
|
||||
this.$$('#slideshowViewport').style.top = this.clientHeight+'px';
|
||||
this.fire('step-horizontal-label-resize');
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
_toggleClassPosition: function(index, stepNumber, vertical) {
|
||||
this.async(function() {
|
||||
var stepLabel = this.$$(vertical ? '#verticalStepLabel' : '#horizontalStepLabel');
|
||||
this.toggleClass('first-step', (index == 1), stepLabel);
|
||||
this.toggleClass('last-step', (index == stepNumber), stepLabel);
|
||||
});
|
||||
},
|
||||
|
||||
_updateAnimationConfig: function() {
|
||||
/* TODO: call this method when horizontalHigher/LowerEntry/ExitAnimation change */
|
||||
var animatedNode = this.$$('#contentWrapper');
|
||||
this._setAnimationConfig({
|
||||
'higher-step-entry': {
|
||||
node: animatedNode,
|
||||
name: this.horizontalHigherEntryAnimation
|
||||
},
|
||||
'higher-step-exit': {
|
||||
node: animatedNode,
|
||||
name: this.horizontalHigherExitAnimation
|
||||
},
|
||||
'lower-step-entry': {
|
||||
node: animatedNode,
|
||||
name: this.horizontalLowerEntryAnimation
|
||||
},
|
||||
'lower-step-exit': {
|
||||
node: animatedNode,
|
||||
name: this.horizontalLowerExitAnimation
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_verticalChange: function(vertical) {
|
||||
this.async(function() {
|
||||
// move or create the content tag
|
||||
Polymer.dom(this.$$(vertical ? '#verticalStepLabel' : '#contentWrapper')).appendChild(this.$$('content') || this.create('content'));
|
||||
// reset the ripple
|
||||
this._ripple = false;
|
||||
this._rippleContainer = vertical ? this.$$('#verticalStepLabel').$.label : this.$$('#horizontalStepLabel').$.label;
|
||||
if (!vertical) {
|
||||
this._updateAnimationConfig();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
_computeSelectable: function(linear, saved, editable, previousSaved) {
|
||||
// TODO: factorize the expression
|
||||
return (!linear || previousSaved) && (!saved || editable) || (editable && saved);
|
||||
},
|
||||
|
||||
_computeDisabled: function(selectable) {
|
||||
// disabled is used by IronMenuBehavior in paper-stepper
|
||||
// TODO: remove selectable attribute and use disabled instead
|
||||
return !selectable;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,735 @@
|
||||
<link rel="import" href="../../components/iron-flex-layout/iron-flex-layout.html">
|
||||
<link rel="import" href="../../components/iron-selector/iron-selectable.html">
|
||||
<link rel="import" href="../../components/iron-menu-behavior/iron-menu-behavior.html">
|
||||
<link rel="import" href="../../components/neon-animation/neon-animation-runner-behavior.html">
|
||||
<link rel="import" href="../../components/paper-button/paper-button.html">
|
||||
<link rel="import" href="../../components/paper-styles/default-theme.html">
|
||||
<link rel="import" href="../../components/paper-styles/shadow.html">
|
||||
<link rel="import" href="../../components/iron-collapse/iron-collapse.html">
|
||||
<link rel="import" href="../../components/iron-resizable-behavior/iron-resizable-behavior.html">
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
<link rel="import" href="animations/fade-in-slide-from-right-animation.html">
|
||||
<link rel="import" href="animations/fade-out-slide-right-animation.html">
|
||||
<link rel="import" href="animations/fade-in-slide-from-left-animation.html">
|
||||
<link rel="import" href="animations/fade-out-slide-left-animation.html">
|
||||
<link rel="import" href="paper-step.html">
|
||||
|
||||
<!--
|
||||
Missing Doc
|
||||
|
||||
|
||||
## Styling
|
||||
|
||||
The default color values respect the material specifications. For basic configuration, just defines the `--primary-color` for your app.
|
||||
|
||||
The stepper has a complexe interface so many custom properties are available, use them this way :
|
||||
|
||||
```html
|
||||
<style is="custom-style">
|
||||
|
||||
paper-stepper {
|
||||
--paper-stepper-custom-property: value;
|
||||
}
|
||||
|
||||
paper-step {
|
||||
--paper-step-custom-property: value;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<paper-stepper>
|
||||
<paper-step></paper-step>
|
||||
<paper-step></paper-step>
|
||||
</paper-stepper>
|
||||
```
|
||||
|
||||
Note, for sizing the stepper and steps depending on the layout, use the following instead of setting 'height' and 'max-height' :
|
||||
- `--paper-stepper-horizontal-opened-height`
|
||||
- `--paper-stepper-vertical-max-height`
|
||||
- `--paper-vertical-step-max-height`
|
||||
|
||||
### Stepper
|
||||
|
||||
Custom property | Description | Default
|
||||
------|-------------|----------
|
||||
`--paper-stepper-horizontal-opened-height` | The horizontal opened stepper height | 450px
|
||||
`--paper-stepper-horizontal-opening-transition-duration`| The horizontal stepper opening transition duration (ms) | 500
|
||||
`--paper-stepper-vertical-max-height`| The vertical stepper max height so it has a scrollable area | undefined
|
||||
|
||||
### Step
|
||||
|
||||
Custom property | Description | Default
|
||||
------|-------------|----------
|
||||
`--paper-step-connector-line-background` | The connector lines background color. | `--divider-color`
|
||||
`--paper-step-label-hover-background` | The steps label background when hovered. | rgba(0,0,0,0.04)
|
||||
`--paper-step-ink-color` | The steps ripple effect ink color. | `--divider-color`
|
||||
|
||||
### Step text label
|
||||
|
||||
Custom property | Description | Default
|
||||
------|-------------|----------
|
||||
`--paper-step-disabled-label-text-color` | The no-selectable steps label text color. | `--paper-grey-400`
|
||||
`--paper-step-selectable-label-text-color` | The selectable steps label text color. | `--paper-grey-600`
|
||||
`--paper-step-opened-label-text-color` | The opened steps label text color. | `--light-theme-text-color`
|
||||
`--paper-step-label-text-font-size` | The steps label text font size. | 14px
|
||||
`--paper-step-label-optional-text-font-size` | The steps label optional text font size. | 12px
|
||||
|
||||
### Step badge
|
||||
|
||||
Custom property | Description | Default
|
||||
------|-------------|----------
|
||||
`--paper-step-badge-background` | The badge background. | `--paper-grey-300`
|
||||
`--paper-step-badge-color` | The badge color. | `--dark-theme-text-color`
|
||||
`--paper-step-badge-icon-width` | The badge icon width. | 16px
|
||||
`--paper-step-badge-icon-height` | The badge icon height. | 16px
|
||||
`--paper-step-opened-badge-background` | The opened steps badge background. | `--primary-color`
|
||||
`--paper-step-saved-badge-background` | The saved steps badge background. | `--primary-color`
|
||||
`--paper-step-selectable-hovered-badge-background` | The no-opened no-saved selectable steps badge background. | `--paper-grey-500`
|
||||
|
||||
### Vertical step
|
||||
|
||||
Custom property | Description | Default
|
||||
------|-------------|----------
|
||||
`--paper-vertical-step-continue-button-background` | The continue button background. | `--primary-color`
|
||||
`--paper-vertical-step-continue-button-color` | The continue button background. | `--dark-theme-text-color`
|
||||
`--paper-vertical-step-max-height` | The unrolled step content max-height. | 400px
|
||||
`--paper-vertical-step-transition-duration` | The step opening transition duration | 500ms
|
||||
|
||||
|
||||
@element paper-stepper
|
||||
@demo demo/index.html
|
||||
@hero hero.svg
|
||||
-->
|
||||
<dom-module id="paper-stepper">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
@apply(--layout-vertical);
|
||||
@apply(--shadow-elevation-2dp);
|
||||
background: white;
|
||||
border-radius: 1px;
|
||||
}
|
||||
#verticalResponsiveWidth {
|
||||
width: var(--paper-stepper-vertical-responsive-width, 0px);
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Horizontal layout styles */
|
||||
|
||||
:host([vertical]) {
|
||||
padding: 4px 0;
|
||||
@apply(--layout-scroll);
|
||||
max-height: var(--paper-stepper-vertical-max-height);
|
||||
}
|
||||
:host(:not([vertical])) {
|
||||
position: relative;
|
||||
/* Note: this variable is updated by the stepper itself, don't use it on user side */
|
||||
height: var(--label-wrapper-height);
|
||||
max-height: var(--label-wrapper-height);
|
||||
transition: max-height var(--paper-stepper-horizontal-opening-transition-duration, 300ms), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
:host(:not([vertical]):not([opened]):not(.collapsing)) {
|
||||
@apply(--shadow-none);
|
||||
}
|
||||
:host(:not([vertical])[opened]) {
|
||||
max-height: var(--paper-stepper-horizontal-opened-height, 450px);
|
||||
}
|
||||
:host(:not([vertical])[opened]), :host(:not([vertical]).collapsing) {
|
||||
height: var(--paper-stepper-horizontal-opened-height, 450px);
|
||||
}
|
||||
:host(:not([vertical])) #content-wrapper {
|
||||
@apply(--shadow-elevation-2dp);
|
||||
@apply(--layout-horizontal);
|
||||
@apply(--layout-flex-none);
|
||||
@apply(--layout-justified);
|
||||
background: var(--paper-stepper-horizontal-label-wrapper-background, --primary-background-color);
|
||||
border-radius: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.flex {
|
||||
@apply(--layout-flex);
|
||||
}
|
||||
#buttonsWrapper {
|
||||
@apply(--layout-horizontal);
|
||||
overflow: hidden;
|
||||
}
|
||||
#continueButton:not([disabled]) {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
paper-button[disabled] {
|
||||
background-color: transparent;
|
||||
}
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
:host(:not[vertical]) #content-wrapper ::content paper-step[opened] {
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Hidden block with width equals to the responsive threshold -->
|
||||
<div id="verticalResponsiveWidth"></div>
|
||||
|
||||
<!-- This wrapper contains the horizontal stepper header or the full vertical stepper -->
|
||||
<div id="content-wrapper">
|
||||
<content select="[[selectable]]"></content>
|
||||
</div>
|
||||
|
||||
<!-- Slideshow and buttons area for horizontal layout -->
|
||||
<template is="dom-if" if="[[!vertical]]">
|
||||
<div class="flex"></div>
|
||||
<!--
|
||||
<div id="buttonsWrapper">
|
||||
<paper-button hidden$="[[!hasBackButton]]" disabled=[[!_hasBackStep]] on-tap="back">[[backText]]</paper-button>
|
||||
<span class="flex"></span>
|
||||
<paper-button hidden$="[[!hasSkipButton]]" disabled="[[!_canSkip]]" on-tap="progress">[[skipText]]</paper-button>
|
||||
<paper-button id="continueButton" on-tap="continue">{{_choosePrimaryButtonText(_attrForSelectedStepPrimaryButtonText, finishText, continueText, updateText)}}</paper-button>
|
||||
</div>
|
||||
-->
|
||||
</template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict';
|
||||
Polymer({
|
||||
|
||||
is: 'paper-stepper',
|
||||
|
||||
behaviors: [
|
||||
Polymer.IronMenuBehavior,
|
||||
Polymer.NeonAnimationRunnerBehavior,
|
||||
Polymer.IronResizableBehavior
|
||||
],
|
||||
|
||||
/**
|
||||
* Fired when the stepper progress.
|
||||
*
|
||||
* @event paper-stepper-progressed
|
||||
*/
|
||||
/**
|
||||
* Fired when all the steps are saved.
|
||||
*
|
||||
* @event paper-stepper-completed
|
||||
*/
|
||||
properties: {
|
||||
opened: {
|
||||
type: Boolean,
|
||||
computed: '_computeOpened(_selectedIndex)',
|
||||
observer: '_openedChanged',
|
||||
notify: true,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
alternativeLabel: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
backText: {
|
||||
type: String,
|
||||
value: 'BACK'
|
||||
},
|
||||
finishText: {
|
||||
type: String,
|
||||
value: 'FINISH'
|
||||
},
|
||||
continueText: {
|
||||
type: String,
|
||||
value: 'CONTINUE'
|
||||
},
|
||||
skipText: {
|
||||
type: String,
|
||||
value: 'SKIP'
|
||||
},
|
||||
optionalText: {
|
||||
type: String,
|
||||
value: 'Optional'
|
||||
},
|
||||
updateText: {
|
||||
type: String,
|
||||
value: 'UPDATE'
|
||||
},
|
||||
linear: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
completed: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
notify: true,
|
||||
computed: '_computeCompleted(stepNumber, savedStepNumber)'
|
||||
},
|
||||
hasSkipButton: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
hasBackButton: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
stepNumber: {
|
||||
type: Number,
|
||||
notify: true,
|
||||
computed: '_computeStepNumber(items.length)'
|
||||
},
|
||||
savedStepNumber: {
|
||||
type: Number,
|
||||
notify: true,
|
||||
readOnly: true
|
||||
},
|
||||
selectedAttribute: {
|
||||
value: 'opened',
|
||||
readOnly: true
|
||||
},
|
||||
/**
|
||||
* Note: if you decide to change this attribute, take care to only include `<paper-step>` elements in your `<paper-stepper>`
|
||||
*/
|
||||
selectable: {
|
||||
value: 'paper-step'
|
||||
},
|
||||
/**
|
||||
* Multi mode is not allowed for now in paper-stepper.
|
||||
*/
|
||||
mutli: {
|
||||
value: false,
|
||||
readOnly: true
|
||||
},
|
||||
responsiveCheckFrequence: {
|
||||
type: Number,
|
||||
value: 200
|
||||
},
|
||||
animateInitialSelection: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
},
|
||||
horizontalHigherEntryAnimation: {
|
||||
type: String,
|
||||
value: 'fade-in-slide-from-right-animation'
|
||||
},
|
||||
horizontalHigherExitAnimation: {
|
||||
type: String,
|
||||
value: 'fade-out-slide-right-animation'
|
||||
},
|
||||
horizontalLowerEntryAnimation: {
|
||||
type: String,
|
||||
value: 'fade-in-slide-from-left-animation'
|
||||
},
|
||||
horizontalLowerExitAnimation: {
|
||||
type: String,
|
||||
value: 'fade-out-slide-left-animation'
|
||||
},
|
||||
_skipStepIndex: {
|
||||
type: Number,
|
||||
computed: '_compute_skipStepIndex(_selectedIndex)'
|
||||
},
|
||||
_canSkip: {
|
||||
type: Boolean,
|
||||
notify: true,
|
||||
computed: '_isntNull(_skipStepIndex)'
|
||||
},
|
||||
_backStepIndex: {
|
||||
type: Number,
|
||||
computed: '_compute_backStepIndex(_selectedIndex)'
|
||||
},
|
||||
_hasBackStep: {
|
||||
type: Boolean,
|
||||
computed: '_isntNull(_backStepIndex)'
|
||||
},
|
||||
_selectedIndex: {
|
||||
type: Number,
|
||||
observer: '_selectedIndexChanged',
|
||||
readOnly: true,
|
||||
value: -1
|
||||
},
|
||||
_attrForSelectedStepPrimaryButtonText: {
|
||||
type: String,
|
||||
computed: '_compute__attrForSelectedStepPrimaryButtonText(_selectedIndex, stepNumber)'
|
||||
},
|
||||
_previousAnimatedStep: {
|
||||
type: Object,
|
||||
value: null,
|
||||
readOnly: true
|
||||
},
|
||||
_previousSelected: {
|
||||
type: Object,
|
||||
readOnly: true
|
||||
}
|
||||
},
|
||||
|
||||
keyBindings: {
|
||||
'left': '_onLeftKey',
|
||||
'right': '_onRightKey'
|
||||
},
|
||||
|
||||
listeners: {
|
||||
'iron-items-changed': '_initializeSteps',
|
||||
'paper-step-saved': '_stepSaved',
|
||||
'transitionend': '_transitionEnd',
|
||||
'step-horizontal-label-resize': '_updateStepperClosedMaxHeight',
|
||||
'iron-resize': '_resizeHandler',
|
||||
'neon-animation-finish': '_onNeonAnimationFinish'
|
||||
},
|
||||
|
||||
observers: [
|
||||
'_forwardCanSkip(_canSkip, selectedItem)',
|
||||
'_forwardHasBackStep(_hasBackStep, selectedItem)',
|
||||
'_forwardVertical(vertical)',
|
||||
'_forwardAlternativeLabel(alternativeLabel)',
|
||||
'_forwardStepperData(linear, backText, optionalText, finishText, continueText, skipText, updateText, hasSkipButton, hasBackButton)'
|
||||
],
|
||||
|
||||
attached: function() {
|
||||
this._responsiveCheck();
|
||||
},
|
||||
|
||||
/**
|
||||
* Missing Doc
|
||||
*/
|
||||
back: function() {
|
||||
this.selectIndex(this._backStepIndex);
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {Boolean} Try to continue the current step (if no step opened, use the first one).
|
||||
*/
|
||||
continue: function() {
|
||||
if (this.selectedItem) {
|
||||
if (this.selectedItem.save()) {
|
||||
this.progress();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Loops around the steps from the current (if no step opened, from the first one)
|
||||
* in order to open the next selectable unsaved step. Returns true if a step has been opened.
|
||||
*/
|
||||
progress: function() {
|
||||
if (!this.stepNumber) {
|
||||
return false;
|
||||
}
|
||||
if (this.completed) {
|
||||
this.selected = null;
|
||||
return true;
|
||||
}
|
||||
for (var i = (this._selectedIndex+1)%this.stepNumber; i != this._selectedIndex; i = (i+1)%this.stepNumber) {
|
||||
if (this.items[i].selectable && !this.items[i].saved) {
|
||||
this.selectIndex(i);
|
||||
this.fire('paper-stepper-progressed');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/* Deselect and set the steps as unsaved*/
|
||||
reset: function() {
|
||||
this._setSavedStepNumber(0);
|
||||
this.selected = null;
|
||||
if (!this.items.length) {
|
||||
return;
|
||||
}
|
||||
this.items.map(function(step) {
|
||||
step._setSaved(false);
|
||||
step._set_previousSaved(false);
|
||||
});
|
||||
this.items[0]._set_previousSaved(true);
|
||||
},
|
||||
|
||||
get _isRTL() {
|
||||
return window.getComputedStyle(this)['direction'] === 'rtl';
|
||||
},
|
||||
|
||||
_onLeftKey: function(event) {
|
||||
if (this._isRTL) {
|
||||
this._focusNext();
|
||||
} else {
|
||||
this._focusPrevious();
|
||||
}
|
||||
event.detail.keyboardEvent.preventDefault();
|
||||
},
|
||||
|
||||
_onRightKey: function(event) {
|
||||
if (this._isRTL) {
|
||||
this._focusPrevious();
|
||||
} else {
|
||||
this._focusNext();
|
||||
}
|
||||
event.detail.keyboardEvent.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Work around: Override the method from IronSelectableBehavior to only allow the selection of selectable step. https://github.com/PolymerElements/iron-selector/issues/99
|
||||
*/
|
||||
_selectSelected: function(selected) {
|
||||
var item = this._valueToItem(this.selected);
|
||||
if (item) {
|
||||
var selectable = item.selectable;
|
||||
if (selectable == undefined) {
|
||||
// if selectable isn't define it means the step is not yet ready for selection
|
||||
// and this method will be recalled by the initialization method.
|
||||
return;
|
||||
} else if (!selectable) {
|
||||
//reset previous selected if non null and selectable or deselect
|
||||
if (this._previousSelected && this._previousSelected.selectable) {
|
||||
this.selected = this._valueForItem(this.previousSelected);
|
||||
} else {
|
||||
this.selected = null;
|
||||
}
|
||||
this._set_previousSelected(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._selection.select(item);
|
||||
this._set_previousSelected(item);
|
||||
this._set_selectedIndex(this.indexOf(item));
|
||||
// Check for items, since this array is populated only when attached
|
||||
// Since Number(0) is falsy, explicitly check for undefined
|
||||
if (this.fallbackSelection && this.items.length && (this._selection.get() === undefined)) {
|
||||
this.selected = this.fallbackSelection;
|
||||
}
|
||||
},
|
||||
|
||||
_updateStepperClosedMaxHeight: function() {
|
||||
this.debounce('updateStepperClosedMaxHeight', function() {
|
||||
this.customStyle['--label-wrapper-height'] = this.$$('#content-wrapper').clientHeight + 'px';
|
||||
this.updateStyles('--label-wrapper-height');
|
||||
});
|
||||
},
|
||||
|
||||
_openedChanged: function(newValue, oldValue) {
|
||||
if (!this.vertical && oldValue != undefined) {
|
||||
this.toggleClass('collapsing', true);
|
||||
}
|
||||
},
|
||||
|
||||
_transitionEnd: function(e) {
|
||||
// check to ignore event fired by paper-ripple
|
||||
if (e.propertyName == 'max-height') {
|
||||
this.toggleClass('collapsing', false);
|
||||
}
|
||||
},
|
||||
|
||||
_computeOpened: function(_selectedIndex) {
|
||||
return _selectedIndex >= 0;
|
||||
},
|
||||
|
||||
_stepSaved: function(e) {
|
||||
var previousStep = this.items[this.indexOf(e.target)+1];
|
||||
if (previousStep) {
|
||||
previousStep._set_previousSaved(true);
|
||||
}
|
||||
this._setSavedStepNumber(this.savedStepNumber+1);
|
||||
},
|
||||
|
||||
_forwardVertical: function(vertical) {
|
||||
if (this.stepNumber) {
|
||||
this.items.map(function(step) {
|
||||
step._setVertical(vertical);
|
||||
});
|
||||
}
|
||||
this.setAttribute('role', vertical ? 'menu': 'menubar');
|
||||
},
|
||||
|
||||
_forwardStepperData: function(linear, backText, optionalText, finishText, continueText, skipText, updateText, hasSkipButton, hasBackButton) {
|
||||
if (this.stepNumber) {
|
||||
this.items.map(function(step) {
|
||||
step._set_stepperData({
|
||||
linear: linear,
|
||||
backText: backText,
|
||||
optionalText: optionalText,
|
||||
finishText: finishText,
|
||||
continueText: continueText,
|
||||
skipText: skipText,
|
||||
updateText: updateText,
|
||||
hasSkipButton: hasSkipButton,
|
||||
hasBackButton: hasBackButton,
|
||||
stepNumber: this.stepNumber
|
||||
});
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
_forwardAlternativeLabel: function(alternativeLabel) {
|
||||
if (this.stepNumber) {
|
||||
this.items.map(function(step) {
|
||||
step._set_alternativeLabel(alternativeLabel);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_computeStepNumber: function(length) {
|
||||
return length;
|
||||
},
|
||||
|
||||
_selectedIndexChanged: function(newValue, oldValue) {
|
||||
if (!this.vertical && newValue >=0 && oldValue >= 0) {
|
||||
var oldStep = this.items[oldValue], newStep = this.items[newValue];
|
||||
if (newStep.classList.contains('neon-animating')) {
|
||||
this.cancelAnimation();
|
||||
}
|
||||
if (this._previousAnimatedStep && this._previousAnimatedStep.classList.contains('neon-animating')) {
|
||||
this.cancelAnimation();
|
||||
this.toggleClass('neon-animating', false, this._previousAnimatedStep);
|
||||
}
|
||||
var forward = newValue - oldValue > 0;
|
||||
|
||||
this.animationConfig = {
|
||||
'new-step-entry': {
|
||||
animatable: newStep,
|
||||
type: forward ?
|
||||
newStep.horizontalHigherEntryAnimation && 'higher-step-entry' :
|
||||
newStep.horizontalLowerEntryAnimation && 'lower-step-entry'
|
||||
},
|
||||
'old-step-exit': {
|
||||
animatable: oldStep,
|
||||
type: forward ?
|
||||
oldStep.horizontalLowerExitAnimation && 'lower-step-exit' :
|
||||
oldStep.horizontalHigherExitAnimation && 'higher-step-exit'
|
||||
}
|
||||
};
|
||||
if (this.animationConfig['new-step-entry'].type) {
|
||||
this.playAnimation('new-step-entry', {step: newStep});
|
||||
this.toggleClass('neon-animating', true, newStep);
|
||||
}
|
||||
if (this.animationConfig['old-step-exit'].type) {
|
||||
this.playAnimation('old-step-exit', {step: oldStep});
|
||||
this.toggleClass('neon-animating', true, oldStep);
|
||||
}
|
||||
|
||||
this._set_previousAnimatedStep(oldStep);
|
||||
}
|
||||
},
|
||||
|
||||
_onNeonAnimationFinish: function(event) {
|
||||
var step = event.detail.step;
|
||||
if (step) {
|
||||
this.toggleClass('neon-animating', false, step);
|
||||
}
|
||||
},
|
||||
|
||||
_forwardCanSkip: function(_canSkip, selectedItem) {
|
||||
selectedItem._set_canSkip(_canSkip);
|
||||
},
|
||||
|
||||
_forwardHasBackStep: function(_hasBackStep, selectedItem) {
|
||||
selectedItem._set_hasBackStep(_hasBackStep);
|
||||
},
|
||||
|
||||
_compute__attrForSelectedStepPrimaryButtonText: function(selectedIndex) {
|
||||
/* TODO: compute from selectedItem when https://github.com/PolymerElements/iron-selector/issues/118 is fixed*/
|
||||
if (selectedIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
var _attrForPrimaryButtonText = this.selectedItem.saved ? 'updateText' :
|
||||
( (this.stepNumber - this.savedStepNumber) == 1 ? 'finishText' : 'continueText' );
|
||||
this.selectedItem._set_attrForPrimaryButtonText(_attrForPrimaryButtonText);
|
||||
return _attrForPrimaryButtonText;
|
||||
},
|
||||
|
||||
_initializeSteps: function() {
|
||||
var savedStepNumber = 0;
|
||||
var data = {
|
||||
linear: this.linear,
|
||||
backText: this.backText,
|
||||
optionalText: this.optionalText,
|
||||
finishText: this.finishText,
|
||||
continueText: this.continueText,
|
||||
skipText: this.skipText,
|
||||
updateText: this.updateText,
|
||||
hasSkipButton: this.hasSkipButton,
|
||||
hasBackButton: this.hasBackButton,
|
||||
stepNumber: this.stepNumber
|
||||
};
|
||||
this.items.map(function(step, i) {
|
||||
if (this.horizontalHigherEntryAnimation && !step.horizontalHigherEntryAnimation) {
|
||||
step.horizontalHigherEntryAnimation = this.horizontalHigherEntryAnimation;
|
||||
}
|
||||
if (this.horizontalHigherExitAnimation && !step.horizontalHigherExitAnimation) {
|
||||
step.horizontalHigherExitAnimation = this.horizontalHigherExitAnimation;
|
||||
}
|
||||
if (this.horizontalLowerEntryAnimation && !step.horizontalLowerEntryAnimation) {
|
||||
step.horizontalLowerEntryAnimation = this.horizontalLowerEntryAnimation;
|
||||
}
|
||||
if (this.horizontalLowerExitAnimation && !step.horizontalLowerExitAnimation) {
|
||||
step.horizontalLowerExitAnimation = this.horizontalLowerExitAnimation;
|
||||
}
|
||||
step._setIndex(i + 1);
|
||||
step._set_stepper(this);
|
||||
step._setVertical(this.vertical);
|
||||
step._set_alternativeLabel(this.alternativeLabel);
|
||||
step._set_stepperData(data);
|
||||
// true for index 0
|
||||
step._set_previousSaved(!i);
|
||||
if (step.saved) {
|
||||
savedStepNumber++;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this._setSavedStepNumber(savedStepNumber);
|
||||
// method from IronSelectableBehavior
|
||||
this._updateSelected();
|
||||
},
|
||||
|
||||
_compute_skipStepIndex: function(_selectedIndex) {
|
||||
if (_selectedIndex >= 0 && !this.completed) {
|
||||
for (var i=(_selectedIndex+1)%this.stepNumber; i!=_selectedIndex; i=(i+1)%this.stepNumber) {
|
||||
if (this.items[i].selectable && !this.items[i].saved) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_compute_backStepIndex: function(_selectedIndex) {
|
||||
if (_selectedIndex >= 0) {
|
||||
for (var i=_selectedIndex - 1; i >= 0; i--) {
|
||||
if (this.items[i].selectable) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
_isntNull: function(n) {
|
||||
return n != null;
|
||||
},
|
||||
|
||||
_computeCompleted: function(savedStepNumber, stepNumber) {
|
||||
var completed = stepNumber == savedStepNumber;
|
||||
if (completed) {
|
||||
this.fire('paper-stepper-completed');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_choosePrimaryButtonText: function(_attrForSelectedStepPrimaryButtonText) {
|
||||
return this[_attrForSelectedStepPrimaryButtonText];
|
||||
},
|
||||
|
||||
_resizeHandler: function() {
|
||||
this.debounce('paper-stepper-responsive-check', function() {
|
||||
this._responsiveCheck();
|
||||
}, this.responsiveCheckFrequence);
|
||||
},
|
||||
|
||||
_responsiveCheck: function() {
|
||||
var verticalResponsiveWidth = this.$.verticalResponsiveWidth.clientWidth;
|
||||
if (verticalResponsiveWidth) {
|
||||
this.vertical = !(this.clientWidth > verticalResponsiveWidth);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,124 @@
|
||||
<link rel="import" href="../../components/iron-flex-layout/iron-flex-layout.html">
|
||||
<link rel="import" href="../../components/iron-icons/iron-icons.html">
|
||||
<link rel="import" href="../../components/iron-icons/editor-icons.html">
|
||||
<link rel="import" href="../../components/paper-styles/color.html">
|
||||
<link rel="import" href="../../components/paper-styles/typography.html">
|
||||
<link rel="import" href="../../components/paper-styles/default-theme.html">
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
<link rel="import" href="step-label-behavior.html">
|
||||
<link rel="import" href="step-label-shared-styles.html">
|
||||
|
||||
<dom-module id="step-horizontal-label">
|
||||
<template>
|
||||
<style include="step-label-shared-styles">
|
||||
:host{
|
||||
overflow: hidden;
|
||||
}
|
||||
:host([alternative-label]) {
|
||||
@apply(--layout);
|
||||
}
|
||||
#textWrapper {
|
||||
@apply(--layout-vertical);
|
||||
padding-right: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#textLabel, #optional {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#badge {
|
||||
margin: 0 8px;
|
||||
}
|
||||
#badgeWrapper, #textWrapper {
|
||||
pointer-events: none;
|
||||
/* to be above paper-ripple*/
|
||||
z-index: 1;
|
||||
}
|
||||
:host(:not([alternative-label])) #label {
|
||||
@apply(--layout-horizontal);
|
||||
@apply(--layout-center);
|
||||
height: 72px;
|
||||
}
|
||||
:host(.firstStep:not([alternative-label])) #badge {
|
||||
margin-left: 24px;
|
||||
}
|
||||
:host(.lastStep:not([alternative-label])) #textWrapper {
|
||||
padding-right: 24px;
|
||||
}
|
||||
:host(:not([alternative-label]):not(.first-step)) #label::before,
|
||||
:host(:not([alternative-label]):not(.last-step)) #label::after,
|
||||
:host([alternative-label]) #badgeWrapper::before,
|
||||
:host([alternative-label]) #badgeWrapper::after {
|
||||
height: 1px;
|
||||
min-width: 16px;
|
||||
background: var(--paper-step-connector-line-color, --divider-color);
|
||||
@apply(--layout-flex);
|
||||
content: '';
|
||||
}
|
||||
:host([alternative-label].first-step) #badgeWrapper::before,
|
||||
:host([alternative-label].last-step) #badgeWrapper::after {
|
||||
visibility: hidden;
|
||||
}
|
||||
:host([alternative-label]) #textWrapper{
|
||||
padding: 0 16px;
|
||||
@apply(--layout-vertical);
|
||||
@apply(--layout-center);
|
||||
}
|
||||
:host([alternative-label]) #textLabel, :host([alternative-label]) #optional{
|
||||
text-align: center;
|
||||
@apply(--layout-self-stretch);
|
||||
}
|
||||
:host([alternative-label]) #label{
|
||||
@apply(--layout-vertical);
|
||||
@apply(--layout-self-stretch);
|
||||
padding: 24px 0;
|
||||
width: 100%;
|
||||
}
|
||||
:host([alternative-label]) #badgeWrapper {
|
||||
@apply(--layout-horizontal);
|
||||
@apply(--layout-center);
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
:host(.first-step:not([alternative-label])) #label {
|
||||
padding-left: 16px;
|
||||
}
|
||||
:host(.last-step:not([alternative-label])) #label {
|
||||
padding-right: 16px;
|
||||
}
|
||||
</style>
|
||||
<!-- use a "label" wrapper to use the same shared css rules with step-vertical -->
|
||||
<div id="label">
|
||||
<div id="badgeWrapper">
|
||||
<div id="badge">
|
||||
<iron-icon hidden$="{{!_computeIsIconBadge(icon)}}" icon="{{icon}}"></iron-icon>
|
||||
<span hidden$="{{_computeIsIconBadge(icon)}}">{{index}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="textWrapper">
|
||||
<span id="textLabel">[[label]]</span>
|
||||
<template is="dom-if" if="[[optional]]">
|
||||
<span id="optional">[[stepperData.optionalText]]</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
Polymer({
|
||||
is: 'step-horizontal-label',
|
||||
|
||||
behaviors: [
|
||||
Stepper.StepLabelBehavior
|
||||
],
|
||||
|
||||
properties: {
|
||||
alternativeLabel: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
reflectToAttribute: true
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,58 @@
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
|
||||
<script>
|
||||
|
||||
window.Stepper = window.Stepper || {};
|
||||
|
||||
/*
|
||||
* @polymerBehavior Stepper.StepLabelBehavior
|
||||
*/
|
||||
Stepper.StepLabelBehavior = {
|
||||
|
||||
properties: {
|
||||
icon: {
|
||||
type: String,
|
||||
computed: '_computeIcon(saved, editable)'
|
||||
},
|
||||
opened: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
selectable: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
editable: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
notify: true
|
||||
},
|
||||
optional: {
|
||||
type: Boolean,
|
||||
notify: true
|
||||
},
|
||||
saved: {
|
||||
type: Boolean,
|
||||
reflectToAttribute: true
|
||||
},
|
||||
index: {
|
||||
type: Number
|
||||
},
|
||||
stepperData: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
|
||||
_computeIcon: function(saved, editable) {
|
||||
return saved ? ( editable ? 'editor:mode-edit' : 'done' ) : '';
|
||||
},
|
||||
|
||||
_computeIsIconBadge: function(icon) {
|
||||
return icon.length > 0;
|
||||
}
|
||||
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,74 @@
|
||||
<link rel="import" href="../../components/iron-flex-layout/iron-flex-layout.html">
|
||||
<link rel="import" href="../../components/paper-styles/default-theme.html">
|
||||
<link rel="import" href="../../components/paper-styles/color.html">
|
||||
<link rel="import" href="../../components/paper-styles/typography.html">
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
|
||||
<dom-module id="step-label-shared-styles">
|
||||
<template>
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
@apply(--paper-font-common-base);
|
||||
@apply(--layout-flex);
|
||||
}
|
||||
#textWrapper {
|
||||
color: var(--paper-step-disabled-label-text-color, --paper-grey-400);
|
||||
}
|
||||
#textLabel {
|
||||
font-size: var(--paper-step-label-text-font-size, 14px);
|
||||
}
|
||||
#optional{
|
||||
font-size: var(--paper-step-label-optional-text-font-size, 12px);
|
||||
}
|
||||
:host([selectable]) #textWrapper {
|
||||
color: var(--paper-step-selectable-label-text-color, --paper-grey-600);
|
||||
}
|
||||
:host([opened]) #textLabel {
|
||||
color: var(--paper-step-opened-label-text-color, --light-theme-text-color);
|
||||
font-weight: 500;
|
||||
}
|
||||
#label {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
/* For paper-ripple */
|
||||
position: relative;
|
||||
}
|
||||
:host([selectable]) #label {
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
#label:hover {
|
||||
/* using alpha chanel for .connectorLine to grow dark */
|
||||
background: var(--paper-step-label-hover-background, rgba(0,0,0,0.04));
|
||||
}
|
||||
#badge {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: var(--paper-step-badge-background, --paper-grey-300);
|
||||
border-radius: 50%;
|
||||
color: var(--paper-step-badge-color, --dark-theme-text-color);
|
||||
font-size: 12px;
|
||||
@apply(--layout);
|
||||
@apply(--layout-center-center);
|
||||
}
|
||||
#badge iron-icon {
|
||||
--iron-icon-height: var(--paper-step-badge-icon-width, 16px);
|
||||
--iron-icon-width: var(--paper-step-badge-icon-height, 16px);
|
||||
}
|
||||
:host([opened]) #badge {
|
||||
background: var(--paper-step-opened-badge-background, --primary-color);
|
||||
}
|
||||
:host([saved]) #badge {
|
||||
background: var(--paper-step-saved-badge-background, --primary-color);
|
||||
}
|
||||
:host([selectable]:not([opened]):not([saved])) #label:hover #badge {
|
||||
background: var(--paper-step-selectable-hovered-badge-background, --paper-grey-500);
|
||||
}
|
||||
paper-ripple {
|
||||
color: var(--paper-step-ink-color, --paper-grey-400);
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
</dom-module>
|
||||
@@ -0,0 +1,177 @@
|
||||
<link rel="import" href="../../components/iron-flex-layout/iron-flex-layout.html">
|
||||
<link rel="import" href="../../components/paper-button/paper-button.html">
|
||||
<link rel="import" href="../../components/paper-styles/color.html">
|
||||
<link rel="import" href="../../components/paper-styles/default-theme.html">
|
||||
<link rel="import" href="../../components/paper-styles/typography.html">
|
||||
<link rel="import" href="../../components/polymer/polymer.html">
|
||||
<link rel="import" href="../../components/iron-icons/iron-icons.html">
|
||||
<link rel="import" href="../../components/iron-icon/iron-icon.html">
|
||||
<link rel="import" href="../../components/iron-icons/editor-icons.html">
|
||||
<link rel="import" href="../../components/iron-collapse/iron-collapse.html">
|
||||
<link rel="import" href="step-label-behavior.html">
|
||||
<link rel="import" href="step-label-shared-styles.html">
|
||||
|
||||
<dom-module id="step-vertical">
|
||||
<template>
|
||||
<style include="step-label-shared-styles">
|
||||
:host {
|
||||
@apply(--layout-vertical);
|
||||
}
|
||||
#connectedBadge, #textWrapper {
|
||||
pointer-events: none;
|
||||
/* to be above paper-ripple*/
|
||||
z-index: 1;
|
||||
}
|
||||
#collapse {
|
||||
--iron-collapse-transition-duration: var(--paper-vertical-step-transition-duration, 500ms);
|
||||
@apply(--layout-horizontal);
|
||||
}
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
#connectedStep {
|
||||
@apply(--layout-horizontal);
|
||||
}
|
||||
#contentConnectorLine {
|
||||
width: 1px;
|
||||
background: var(--divider-color, --paper-grey-300);
|
||||
margin-left: 36px;
|
||||
margin-right: 24px;
|
||||
}
|
||||
#stepWrapper {
|
||||
@apply(--layout-flex);
|
||||
/*should be 48px on large screen?*/
|
||||
padding-right: 24px;
|
||||
}
|
||||
#paperStepWrapper {
|
||||
max-height: calc(var(--paper-vertical-step-max-height, 400px) - 92px);
|
||||
@apply(--layout-scroll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buttons
|
||||
*/
|
||||
#buttonsWrapper {
|
||||
height: 48px;
|
||||
@apply(--layout-horizontal);
|
||||
@apply(--layout-center);
|
||||
@apply(--layout-flex-none);
|
||||
}
|
||||
#buttonsWrapper > paper-button {
|
||||
margin-right: 8px;
|
||||
margin-left: 0;
|
||||
}
|
||||
#continueButton {
|
||||
--paper-button: {
|
||||
background: var(--paper-vertical-step-continue-button-background, --primary-color);
|
||||
color: var(--paper-vertical-step-continue-button-color, --dark-theme-text-color);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Label
|
||||
*/
|
||||
#textWrapper {
|
||||
@apply(--layout-vertical);
|
||||
padding: 8px 0 8px 8px;
|
||||
}
|
||||
#label {
|
||||
@apply(--layout-horizontal);
|
||||
}
|
||||
#connectedBadge {
|
||||
@apply(--layout-vertical);
|
||||
@apply(--layout-center);
|
||||
margin-left: 24px;
|
||||
}
|
||||
#beforeConnectorLine, #afterConnectorLine {
|
||||
width: 1px;
|
||||
background: var(--paper-step-connector-line-color, --divider-color);
|
||||
}
|
||||
#beforeConnectorLine {
|
||||
height: 10px;
|
||||
}
|
||||
#afterConnectorLine {
|
||||
@apply(--layout-flex);
|
||||
}
|
||||
#badge {
|
||||
margin: 8px 0;
|
||||
}
|
||||
:host(.first-step) #beforeConnectorLine, :host(.lastStep) #contentConnectorLine{
|
||||
visibility: hidden;
|
||||
}
|
||||
:host(.last-step:not([opened])) #afterConnectorLine {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="label">
|
||||
<div id="connectedBadge">
|
||||
<div id="beforeConnectorLine"></div>
|
||||
<div id="badge">
|
||||
<iron-icon hidden$="{{!_computeIsIconBadge(icon)}}" icon="{{icon}}"></iron-icon>
|
||||
<span hidden$="{{_computeIsIconBadge(icon)}}">{{index}}</span>
|
||||
</div>
|
||||
<div id="afterConnectorLine" class="connectorLine"></div>
|
||||
</div>
|
||||
<div id="textWrapper">
|
||||
<span id="textLabel">[[label]]</span>
|
||||
<template is="dom-if" if="[[optional]]">
|
||||
<span id="optional">[[stepperData.optionalText]]</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<iron-collapse id="collapse" opened="[[opened]]">
|
||||
<div id="contentConnectorLine"></div>
|
||||
<div id="stepWrapper">
|
||||
<div id="paperStepWrapper">
|
||||
<content></content>
|
||||
</div>
|
||||
<div id="buttonsWrapper">
|
||||
<paper-button id="continueButton" on-tap="continue">{{choosePrimaryButtonText(_attrForPrimaryButtonText, stepperData.continueText, stepperData.finishText, stepperData.updateText)}}</paper-button>
|
||||
<paper-button id="backButton" disabled="[[!hasBackStep]]" on-tap="back" hidden$="[[!stepperData.hasBackButton]]">
|
||||
[[stepperData.backText]]
|
||||
</paper-button>
|
||||
<paper-button id="skipButton" disabled="[[!canSkip]]" on-tap="skip" hidden$="[[!stepperData.hasSkipButton]]">
|
||||
[[stepperData.skipText]]
|
||||
</paper-button>
|
||||
</div>
|
||||
</div>
|
||||
</iron-collapse>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
Polymer({
|
||||
is: 'step-vertical',
|
||||
|
||||
properties: {
|
||||
canSkip: {
|
||||
type: Boolean
|
||||
},
|
||||
_attrForPrimaryButtonText: {
|
||||
type: String,
|
||||
value: false
|
||||
},
|
||||
hasBackStep: Boolean
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Stepper.StepLabelBehavior
|
||||
],
|
||||
|
||||
skip: function() {
|
||||
this.fire('paper-step-vertical-skip-tapped');
|
||||
},
|
||||
|
||||
back: function() {
|
||||
this.fire('paper-step-vertical-back-tapped');
|
||||
},
|
||||
|
||||
continue: function() {
|
||||
this.fire('paper-step-vertical-continue-tapped');
|
||||
},
|
||||
|
||||
choosePrimaryButtonText: function(_attrForPrimaryButtonText) {
|
||||
return this.stepperData[_attrForPrimaryButtonText];
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</dom-module>
|
||||
BIN
rpg-docs/public/png/doubleLineImageBorder.png
Normal file
BIN
rpg-docs/public/png/doubleLineImageBorder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
BIN
rpg-docs/public/png/horizontalLine.png
Normal file
BIN
rpg-docs/public/png/horizontalLine.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 180 B |
BIN
rpg-docs/public/png/octogonBorder.png
Normal file
BIN
rpg-docs/public/png/octogonBorder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
rpg-docs/public/png/shieldBorder.png
Normal file
BIN
rpg-docs/public/png/shieldBorder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
rpg-docs/public/png/upwardPointingBorder.png
Normal file
BIN
rpg-docs/public/png/upwardPointingBorder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -25,3 +25,10 @@ Meteor.publish("characterList", function(){
|
||||
Parties.find({owner: userId}),
|
||||
];
|
||||
});
|
||||
|
||||
DDPRateLimiter.addRule({
|
||||
name: "characterList",
|
||||
type: "subscription",
|
||||
userId(){ return true; },
|
||||
connectionId(){ return true; },
|
||||
}, 8, 5000);
|
||||
|
||||
@@ -35,9 +35,16 @@ Meteor.publish("singleCharacter", function(characterId){
|
||||
}
|
||||
});
|
||||
|
||||
DDPRateLimiter.addRule({
|
||||
name: "singleCharacter",
|
||||
type: "subscription",
|
||||
userId(){ return true; },
|
||||
connectionId(){ return true; },
|
||||
}, 8, 5000);
|
||||
|
||||
Meteor.publish("singleCharacterName", function(characterId){
|
||||
userId = this.userId;
|
||||
var char = Characters.findOne({
|
||||
return Characters.find({
|
||||
_id: characterId,
|
||||
$or: [
|
||||
{readers: userId},
|
||||
@@ -45,8 +52,7 @@ Meteor.publish("singleCharacterName", function(characterId){
|
||||
{owner: userId},
|
||||
{"settings.viewPermission": "public"},
|
||||
],
|
||||
}, {
|
||||
fields:{"name": 1}
|
||||
});
|
||||
if (char) {
|
||||
return Characters.find(characterId, {fields:{"name": 1}});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
Meteor.publish("user", function(){
|
||||
return Meteor.users.find(this.userId, {fields: {roles: 1}});
|
||||
return Meteor.users.find(this.userId, {fields: {
|
||||
roles: 1,
|
||||
username: 1,
|
||||
profile: 1,
|
||||
apiKey: 1,
|
||||
}});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user