Merge branch 'feature-print'
# Conflicts: # rpg-docs/package-lock.json
This commit is contained in:
@@ -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);
|
||||
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;
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
@@ -181,8 +187,8 @@ Template.characterSheet.helpers({
|
||||
const step = Session.get("newUserExperienceStep");
|
||||
if (selected == tab) return false;
|
||||
return (tab === 1 && step === 0) ||
|
||||
(tab === 5 && step === 1) ||
|
||||
(tab === 0 && step === 2);
|
||||
(tab === 5 && step === 1) ||
|
||||
(tab === 0 && step === 2);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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}});
|
||||
|
||||
@@ -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
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
1536
rpg-docs/package-lock.json
generated
1536
rpg-docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
||||
"babel-runtime": "^6.23.0",
|
||||
"bcrypt": "^1.0.3",
|
||||
"bower": "^1.7.9",
|
||||
"core-js": "^2.5.1"
|
||||
"core-js": "^2.5.1",
|
||||
"meteor-node-stubs": "^0.3.2",
|
||||
"qrcode": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
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 |
Reference in New Issue
Block a user