diff --git a/app/client/views/character/characterSheet.html b/app/client/views/character/characterSheet.html
index 8785892b..efc54123 100644
--- a/app/client/views/character/characterSheet.html
+++ b/app/client/views/character/characterSheet.html
@@ -31,9 +31,17 @@
Settings
-
+
Export to Improved Initiative
+
+
+ Make a copy
+
+
+
+ Download a backup
+
{{else}}
diff --git a/app/client/views/character/characterSheet.js b/app/client/views/character/characterSheet.js
index a2a549f4..0c43d387 100644
--- a/app/client/views/character/characterSheet.js
+++ b/app/client/views/character/characterSheet.js
@@ -234,6 +234,18 @@ Template.characterSheet.events({
element: event.currentTarget.parentElement.parentElement,
});
},
+ "click #characterCopy": function(event, instance){
+ Meteor.call("copyCharacter", this._id, (error, char) => {
+ if (error){
+ console.error(error);
+ } else {
+ Router.go(`/character/${char._id}/${char.urlName || "-"}`);
+ }
+ });
+ },
+ "click #characterDump": function(event, instance){
+ saveCharacterDump(this._id);
+ },
"click #unshareCharacter": function(event, instance){
pushDialogStack({
data: this,
diff --git a/app/lib/functions/backupRestoreCharacter.js b/app/lib/functions/backupRestoreCharacter.js
index d08ef919..9863d12f 100644
--- a/app/lib/functions/backupRestoreCharacter.js
+++ b/app/lib/functions/backupRestoreCharacter.js
@@ -1,24 +1,29 @@
-let characterCollections = [
- Actions,
- Attacks,
- Buffs,
- Classes,
- Conditions,
- CustomBuffs,
- Effects,
- Experiences,
- Features,
- Notes,
- Proficiencies,
- SpellLists,
- Spells,
- TemporaryHitPoints,
- Items,
- Containers,
-];
+import { saveAs } from 'file-saver';
-function dumpCharacter(charId){
- let characterDump = {};
+let characterCollections = [];
+Meteor.startup(() => {
+ characterCollections = [
+ Actions,
+ Attacks,
+ Buffs,
+ Classes,
+ Conditions,
+ CustomBuffs,
+ Effects,
+ Experiences,
+ Features,
+ Notes,
+ Proficiencies,
+ SpellLists,
+ Spells,
+ TemporaryHitPoints,
+ Items,
+ Containers,
+ ];
+});
+
+dumpCharacter = function(charId){
+ let characterDump = {collections: {}};
characterDump.character = Characters.findOne(charId);
characterCollections.forEach(c => {
characterDump.collections[c._name] = c.find({charId}).fetch();
@@ -26,13 +31,23 @@ function dumpCharacter(charId){
return characterDump;
};
-function giveCharacterDumpNewIds(characterDump){
+saveCharacterDump = function(charId){
+ let dump = dumpCharacter(charId);
+ let textDump = JSON.stringify(dump, null, 2);
+ let charName = dump.character.name;
+ let blob = new Blob([textDump], {type: "application/json;charset=utf-8"});
+ saveAs(blob, `${charName}.JSON`);
+};
+
+giveCharacterDumpNewIds = function(characterDump){
// Give the character a new Id
+ const oldCharId = characterDump.character._id;
const newCharId = Random.id();
characterDump.character._id = newCharId;
+ let idMap = {[oldCharId]: newCharId}; // {oldId: newId}
+
// Give all documents a new Id, and store the mapping from old to new
- let idMap = {}; // {oldId: newId}
for (let colName in characterDump.collections){
for (let doc of characterDump.collections[colName]){
let oldId = doc._id;
@@ -57,7 +72,7 @@ function giveCharacterDumpNewIds(characterDump){
}
}
-function restoreCharacter(characterDump){
+restoreCharacter = function(characterDump){
Characters.insert(characterDump.character);
for (collectionName in characterDump.collections){
let collection = Meteor.Collection.get(collectionName);
diff --git a/app/lib/methods/characterCopyPaste.js b/app/lib/methods/characterCopyPaste.js
index d078b45b..76173202 100644
--- a/app/lib/methods/characterCopyPaste.js
+++ b/app/lib/methods/characterCopyPaste.js
@@ -16,7 +16,11 @@ Meteor.methods({
characterDump.character.writers = [];
characterDump.character.owner = userId;
+ // Rename the character so it's obviously a copy
+ characterDump.character.name += " - Copy";
+
// Write the character back to the database
restoreCharacter(characterDump);
+ return characterDump.character;
},
});
diff --git a/app/package-lock.json b/app/package-lock.json
index 12e5ed0b..3c606c15 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -45,7 +45,7 @@
},
"ansi-regex": {
"version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"aproba": {
@@ -74,6 +74,7 @@
},
"block-stream": {
"version": "0.0.9",
+ "resolved": false,
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
"requires": {
"inherits": "~2.0.0"
@@ -131,7 +132,7 @@
},
"code-point-at": {
"version": "1.1.0",
- "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"core-js": {
@@ -139,6 +140,11 @@
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -209,6 +215,11 @@
"resolved": "https://registry.npmjs.org/fibers/-/fibers-2.0.2.tgz",
"integrity": "sha512-HfVRxhYG7C8Jl9FqtrlElMR2z/8YiLQVDKf67MLY25Ic+ILx3ecmklfT1v3u+7P5/4vEFjuxaAFXhr2/Afwk5g=="
},
+ "file-saver": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.0.tgz",
+ "integrity": "sha512-cYM1ic5DAkg25pHKgi5f10ziAM7RJU37gaH1XQlyNDrtUnzhC/dfoV9zf2OmF0RMKi42jG5B0JWBnPQqyj/G6g=="
+ },
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -259,7 +270,7 @@
},
"graceful-fs": {
"version": "4.1.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "resolved": false,
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"har-schema": {
@@ -361,6 +372,7 @@
},
"inherits": {
"version": "2.0.3",
+ "resolved": false,
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"invert-kv": {
@@ -383,7 +395,7 @@
},
"is-fullwidth-code-point": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "resolved": false,
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
@@ -1064,6 +1076,37 @@
"requires": {
"inherits": "~2.0.1",
"readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
}
},
"stream-http": {
@@ -1076,6 +1119,37 @@
"readable-stream": "^2.3.3",
"to-arraybuffer": "^1.0.0",
"xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
}
},
"string_decoder": {
@@ -1434,7 +1508,7 @@
},
"number-is-nan": {
"version": "1.0.1",
- "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"os-homedir": {
@@ -1578,6 +1652,7 @@
},
"minimist": {
"version": "1.2.0",
+ "resolved": false,
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"strip-json-comments": {
@@ -1815,7 +1890,7 @@
},
"set-blocking": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "resolved": false,
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"shebang-command": {
@@ -1833,7 +1908,7 @@
},
"signal-exit": {
"version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "resolved": false,
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
},
"source-map": {
@@ -1909,7 +1984,7 @@
},
"strip-ansi": {
"version": "3.0.1",
- "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
@@ -2159,7 +2234,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
- "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"requires": {
"string-width": "^1.0.1",
diff --git a/app/package.json b/app/package.json
index 13ecdf36..fbbadeae 100644
--- a/app/package.json
+++ b/app/package.json
@@ -17,6 +17,7 @@
"bower": "^1.7.9",
"core-js": "^2.5.7",
"fibers": "^2.0.2",
+ "file-saver": "^2.0.0",
"meteor-node-stubs": "^0.3.3",
"qrcode": "^1.3.0",
"source-map-support": "^0.5.9",