暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

card.ts 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. module RpgCards {
  2. function normalizeTag(tag: string): string {
  3. return tag.trim().toLowerCase();
  4. }
  5. function splitParams(value: string): string[] {
  6. return value.split("|").map(function (str) { return str.trim(); });
  7. }
  8. export class Options {
  9. foreground_color: string;
  10. background_color: string;
  11. empty_color: string;
  12. default_color: string;
  13. default_icon: string;
  14. default_title_size: string;
  15. page_size: string;
  16. page_rows: number;
  17. page_columns: number;
  18. card_arrangement: string;
  19. card_size: string;
  20. card_count: number;
  21. icon_inline: boolean;
  22. constructor() {
  23. this.foreground_color = "white";
  24. this.background_color = "white";
  25. this.empty_color = "white";
  26. this.default_color = "black";
  27. this.default_icon = "ace";
  28. this.default_title_size = "13pt";
  29. this.page_size = "A4";
  30. this.page_rows = 3;
  31. this.page_columns = 3;
  32. this.card_arrangement = "doublesided";
  33. this.card_size = "25x35";
  34. this.card_count = null;
  35. this.icon_inline = true;
  36. }
  37. }
  38. export class Card {
  39. count: number;
  40. title: string;
  41. title_size: string;
  42. color: string;
  43. color_front: string;
  44. color_back: string;
  45. icon: string;
  46. icon_front: string;
  47. icon_back: string;
  48. contents: string[];
  49. tags: string[];
  50. userData: any;
  51. constructor() {
  52. this.count = 1;
  53. this.title = "New card";
  54. this.title_size = null;
  55. this.color = null;
  56. this.color_front = null;
  57. this.color_back = null;
  58. this.icon = null;
  59. this.icon_front = null;
  60. this.icon_back = null;
  61. this.contents = [];
  62. this.tags = [];
  63. this.userData = null;
  64. }
  65. static fromJSON(json: any): Card {
  66. var result = new Card;
  67. result.count = json.count || 1;
  68. result.title = json.title || "";
  69. result.title_size = json.title_size || null;
  70. result.color = json.color || null;
  71. result.color_front = json.color_front || null;
  72. result.color_back = json.color_back || null;
  73. result.icon = json.icon || null;
  74. result.icon_front = json.icon_front || null;
  75. result.icon_back = json.icon_back || null;
  76. result.contents = json.contents || [];
  77. result.tags = json.tags || [];
  78. return result;
  79. }
  80. public toJSON(): any {
  81. return {
  82. count: this.count,
  83. title: this.title,
  84. title_size: this.title_size,
  85. color: this.color,
  86. color_front: this.color_front,
  87. color_back: this.color_back,
  88. icon: this.icon,
  89. icon_front: this.icon_front,
  90. icon_back: this.icon_back,
  91. contents: this.contents.slice(),
  92. tags: this.tags.slice()
  93. }
  94. }
  95. public duplicate(): Card {
  96. var result = Card.fromJSON(this.toJSON());
  97. result.title += " (Copy)";
  98. return result;
  99. }
  100. public hasTag(tag: string): boolean {
  101. var index = this.tags.indexOf(normalizeTag(tag));
  102. return index > -1;
  103. }
  104. public addTag(tag: string): void {
  105. if (!this.hasTag(tag)) {
  106. this.tags.push(normalizeTag(tag));
  107. }
  108. }
  109. public removeTag(tag: string): void {
  110. var ntag = normalizeTag(tag);
  111. this.tags = this.tags.filter(function (t) {
  112. return ntag != t;
  113. });
  114. }
  115. public getTitle(options: Options): string {
  116. return this.title || "";
  117. }
  118. public getTitleSize(options: Options): string {
  119. return this.title_size || options.default_title_size || "13pt";
  120. }
  121. public getColorFront(options: Options): string {
  122. return this.color_front || this.color || options.default_color || "black";
  123. }
  124. public getColorBack(options: Options): string {
  125. return this.color_back || this.color || options.default_color || "black";
  126. }
  127. public getIconFront(options: Options): string {
  128. return this.icon_front || this.icon || options.default_icon || "ace";
  129. }
  130. public getIconBack(options: Options): string {
  131. return this.icon_back || this.icon || options.default_icon || "ace";
  132. }
  133. };
  134. interface CardAction {
  135. fn: string;
  136. card: Card;
  137. ref: Card;
  138. }
  139. export class CardDeck {
  140. cards: Card[];
  141. private _actions: CardAction[];
  142. constructor() {
  143. this.cards = [];
  144. this._actions = [];
  145. }
  146. public toJSON(): any {
  147. return this.cards.map((card) => card.toJSON());
  148. }
  149. public static fromJSON(data: any): CardDeck {
  150. if (Array.isArray(data)) {
  151. var result = new CardDeck;
  152. for (var i = 0; i < data.length; ++i) {
  153. result.cards.push(Card.fromJSON(data[i]));
  154. }
  155. return result;
  156. } else {
  157. throw new Error("Invalid data");
  158. }
  159. }
  160. public addCards(cards: Card[]): void {
  161. cards.forEach((card) => {
  162. this._actions.push({fn:"add", card: card, ref: null});
  163. });
  164. }
  165. public addNewCard(): Card {
  166. var newCard = new Card();
  167. this._actions.push({ fn: "add", card: newCard, ref: null });
  168. return newCard;
  169. }
  170. public duplicateCard(card: Card): Card {
  171. var newCard = card.duplicate();
  172. this._actions.push({ fn: "add", card: new Card(), ref: card });
  173. return newCard;
  174. }
  175. public deleteCard(card: Card): void {
  176. this._actions.push({ fn: "del", card: card, ref: null });
  177. }
  178. public commit() {
  179. for (var i = 0; i < this._actions.length; ++i) {
  180. var action = this._actions[i];
  181. if (action.fn === "add") {
  182. var index = this.cards.indexOf(action.ref);
  183. if (index > -1) {
  184. this.cards.splice(index + 1, 0, action.card);
  185. } else {
  186. this.cards.push(action.card);
  187. }
  188. } else if (action.fn === "del") {
  189. var index = this.cards.indexOf(action.card);
  190. if (index > -1) {
  191. this.cards.splice(index, 1);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. type ContentGeneratorFunction = (params: string[], card: Card, options: Options, ind: string, ind0: string) => string;
  198. type CardGeneratorFunction = (card: Card, options: Options, ind: string, ind0: string) => string;
  199. export class CardHtmlGenerator {
  200. constructor() {
  201. }
  202. private _icon(src: string, ind: string, ind0: string): string {
  203. return ind + '<card-icon src="/icons/' + src + '.svg"></card-icon>\n';
  204. }
  205. private _subtitle(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  206. var text = params[0] || "";
  207. return ind + '<card-subtitle>' + text + '</card-subtitle>\n';
  208. }
  209. private _ruler(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  210. return ind + '<card-rule></card-rule>\n';
  211. }
  212. private _boxes(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  213. var count = params[0] || 1;
  214. var size = params[1] || 3;
  215. return ind + '<card-boxes size="' + size + '" count="' + count + '"></card-boxes>\n';
  216. }
  217. private _property(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  218. var header = params[0] || "";
  219. var text = params[1] || "";
  220. var result = "";
  221. result += ind + '<card-property>\n';
  222. result += ind + ind0 + '<h4>' + header + '</h4>\n';
  223. result += ind + ind0 + '<p>' + text + '</p>\n';
  224. result += ind + '</card-property>\n';
  225. return result;
  226. }
  227. private _description(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  228. var header = params[0] || "";
  229. var text = params[1] || "";
  230. var result = "";
  231. result += ind + '<card-description>\n';
  232. result += ind + ind0 + '<h4>' + header + '</h4>\n';
  233. result += ind + ind0 + '<p>' + text + '</p>\n';
  234. result += ind + '</card-description>\n';
  235. return result;
  236. }
  237. private _text(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  238. var text = params[0] || "";
  239. var result = "";
  240. result += ind + '<card-description>\n';
  241. result += ind + ind0 + '<p>' + text + '</p>\n';
  242. result += ind + '</card-description>\n';
  243. return result;
  244. }
  245. private _dndstats(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  246. var stats = ["str", "dex", "con", "int", "wis", "cha"];
  247. var result = "";
  248. result += ind + '<card-dndstats';
  249. for (var i = 0; i < stats.length; ++i) {
  250. var value = parseInt(params[0], 10) || "";
  251. var stat = stats[i];
  252. result += ' ' + stat + '="' + value + '"';
  253. }
  254. result += '></card-dndstats>\n';
  255. return result;
  256. }
  257. private _bullet(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  258. var text = params[0] || "";
  259. return ind + '<card-bullet>' + text + '</card-bullet>\n';
  260. }
  261. private _section(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  262. var text = params[0] || "";
  263. return ind + '<card-section>' + text + '</card-section>\n';
  264. }
  265. private _fill(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  266. var size = params[0] || "1";
  267. return ind + '<card-fill size="' + size + '"></card-fill>\n';
  268. }
  269. private _unknown(params: string[], card: Card, options: Options, ind: string, ind0: string): string {
  270. var text = params.join(' | ');
  271. return ind + '<card-description><p>' + text + '</p></card-description>\n';
  272. }
  273. private _empty(params: string[], card: Card, options: Options, ind: string, ind0: string) {
  274. return '';
  275. }
  276. private _contents(contents: string[], card: Card, options: Options, ind: string, ind0: string): string {
  277. var result = "";
  278. result += ind + '<card-contents>\n';
  279. result += contents.map((value) => {
  280. var parts = splitParams(value);
  281. var name = parts[0];
  282. var params = parts.splice(1);
  283. var generator: ContentGeneratorFunction = null;
  284. switch (name) {
  285. case "subtitle": generator = this._subtitle; break;
  286. case "property": generator = this._property; break;
  287. case "rule": generator = this._ruler; break;
  288. case "ruler": generator = this._ruler; break;
  289. case "boxes": generator = this._boxes; break;
  290. case "description": generator = this._description; break;
  291. case "dndstats": generator = this._dndstats; break;
  292. case "text": generator = this._text; break;
  293. case "bullet": generator = this._bullet; break;
  294. case "fill": generator = this._fill; break;
  295. case "section": generator = this._section; break;
  296. case "disabled": generator = this._empty; break;
  297. case "": generator = this._empty; break;
  298. default: return this._unknown(parts, card, options, ind, ind0);
  299. }
  300. return generator(params, card, options, ind + ind0, ind);
  301. }).join("\n");
  302. result += ind + '</card-contents>\n';
  303. return result;
  304. }
  305. private _title(card: Card, options: Options, ind: string, ind0: string): string {
  306. var title = card.getTitle(options);
  307. var title_size = card.getTitleSize(options)
  308. var icon = card.getIconFront(options);
  309. var result = "";
  310. result += ind + '<card-title size="' + title_size + '">\n';
  311. result += ind + ind0 + '<h1>' + title + '</h1>\n';
  312. result += ind + ind0 + '<h2>' + "" + '</h2>\n';
  313. result += this._icon(icon, ind + ind0, ind0);
  314. result += ind + '</card-title>\n';
  315. return result;
  316. }
  317. private _card_front(card: Card, options: Options, ind: string, ind0: string): string {
  318. var result = "";
  319. result += this._title(card, options, ind + ind0, ind0);
  320. result += this._contents(card.contents, card, options, ind + ind0, ind0);
  321. return result;
  322. }
  323. private _card_back(card: Card, options: Options, ind: string, ind0: string): string {
  324. var icon = card.getIconBack(options);
  325. var result = "";
  326. result += ind + '<card-back>\n';
  327. result += this._icon(icon, ind + ind0, ind);
  328. result += ind + '</card-back>\n';
  329. return result;
  330. }
  331. private _card_empty(options: Options, ind: string, ind0: string): string {
  332. return '';
  333. }
  334. private _card(options: Options, ind: string, ind0: string, content: string, color: string): string {
  335. var size = options.card_size || "25x35";
  336. var result = "";
  337. result += ind + '<rpg-card color="' + color + '" size="' + size + '">\n';
  338. result += content;
  339. result += ind + '</rpg-card>\n';
  340. return result;
  341. }
  342. /** Generates HTML for the front side of the given card */
  343. public card_front(card: Card, options: Options, indent: string): string {
  344. var content = this._card_front(card, options, "", indent);
  345. return this._card(options, "", indent, content, card.getColorFront(options));
  346. }
  347. /** Generates HTML for the back side of the given card */
  348. public card_back(card: Card, options: Options, indent: string): string {
  349. var content = this._card_back(card, options, "", indent);
  350. return this._card(options, "", indent, content, card.getColorBack(options));
  351. }
  352. /** Generates HTML for an empty given card */
  353. public card_empty(options: Options, indent: string): string {
  354. var content = this._card_empty(options, "", indent);
  355. return this._card(options, "", indent, content, options.empty_color);
  356. }
  357. }
  358. class CardPage<T> {
  359. rows: number;
  360. cols: number;
  361. cards: T[];
  362. constructor(rows: number, cols: number) {
  363. this.rows = rows;
  364. this.cols = cols;
  365. this.cards = [];
  366. }
  367. /** Returns an empty page with the same dimensions */
  368. public newPage(): CardPage<T> {
  369. return new CardPage<T>(this.rows, this.cols);
  370. }
  371. private _posToIndex(row: number, col: number): number {
  372. return row * this.cols + col;
  373. }
  374. /** Adds one card to the page */
  375. public addCard(card: T): void {
  376. if (this.capacity() === 0) {
  377. throw new Error("This page is full.");
  378. }
  379. this.cards.push(card);
  380. }
  381. /**
  382. Adds several copies of a card to the page.
  383. Returns the number of copies that did not fit on the page.
  384. */
  385. public addCards(card: T, count: number): number {
  386. while (this.capacity() > 0 && count > 0) {
  387. this.addCard(card);
  388. --count;
  389. }
  390. return count;
  391. }
  392. /** Fills all remaining slots on the current row with the given card */
  393. public fillRow(card: T): void {
  394. while (this.capacityRow() > 0) {
  395. this.addCard(card);
  396. }
  397. }
  398. /** Fills all remaining slots on the page with empty cards */
  399. public fillPage(card: T): void {
  400. while (this.capacity() > 0) {
  401. this.addCard(card);
  402. }
  403. }
  404. /** Empty slots on the page */
  405. public capacity(): number {
  406. return this.cards.length - this.rows * this.cols;
  407. }
  408. /** Empty slots on the current line */
  409. public capacityRow(): number {
  410. return this.capacity() % this.cols;
  411. }
  412. /** Flip card slots horizontally */
  413. public flipH() {
  414. if (this.capacity() > 0) {
  415. throw new Error("Cannot perform this operation while the page is not full");
  416. }
  417. for (var r = 0; r < Math.floor(this.rows / 2); ++r) {
  418. for (var c = 0; c < this.cols; ++c) {
  419. var indexL = this._posToIndex(r, c);
  420. var indexR = this._posToIndex(this.rows - r - 1, c);
  421. var cardL = this.cards[indexL];
  422. var cardR = this.cards[indexR];
  423. this.cards[indexL] = cardR;
  424. this.cards[indexR] = cardL;
  425. }
  426. }
  427. }
  428. }
  429. class CardPageSet<T> {
  430. rows: number;
  431. cols: number;
  432. pages: CardPage<T>[];
  433. constructor(rows: number, cols: number) {
  434. this.rows = rows;
  435. this.cols = cols;
  436. this.pages = [];
  437. }
  438. public lastPage(): CardPage<T> {
  439. if (this.pages.length === 0) {
  440. return null;
  441. } else {
  442. return this.pages[this.pages.length - 1];
  443. }
  444. }
  445. public addPage(): CardPage<T> {
  446. var newPage = new CardPage<T>(this.rows, this.cols);
  447. this.pages.push(newPage);
  448. return newPage;
  449. }
  450. /**
  451. Adds one card to the last page.
  452. Adds a new pages if necessary.
  453. */
  454. public addCard(card: T): void {
  455. var page = this.lastPage();
  456. if (page === null || page.capacity() === 0) {
  457. page = this.addPage();
  458. }
  459. page.addCard(card);
  460. }
  461. /**
  462. Adds several copies of a card to the last page.
  463. Adds new pages if necessary.
  464. */
  465. public addCards(card: T, count: number): void {
  466. for (var i = 0; i < count; ++i) {
  467. this.addCard(card);
  468. }
  469. }
  470. public forEach(fn: (page: CardPage<T>)=>void) {
  471. this.pages.forEach(fn);
  472. }
  473. public merge(other: CardPageSet<T>): CardPageSet<T> {
  474. if (this.pages.length !== other.pages.length) {
  475. throw new Error("This function is only for merging two equally sized page sets");
  476. }
  477. var result = new CardPageSet<T>(this.rows, this.cols);
  478. for (var i = 0; i < this.pages.length; ++i) {
  479. result.pages.push(this.pages[i]);
  480. result.pages.push(other.pages[i]);
  481. }
  482. return result;
  483. }
  484. }
  485. export class PageHtmlGenerator {
  486. indent: string;
  487. constructor() {
  488. this.indent = " ";
  489. }
  490. private _pageColor(page: number, options: Options): string {
  491. if ((options.card_arrangement == "doublesided") && (page % 2 == 1)) {
  492. return options.background_color;
  493. } else {
  494. return options.foreground_color;
  495. }
  496. }
  497. private _wrap(pageSet: CardPageSet<string>, options: Options) {
  498. var size = options.page_size || "A4";
  499. var result = "";
  500. for (var i = 0; i < pageSet.pages.length; ++i) {
  501. var page = pageSet.pages[i];
  502. var style = 'style="background-color:' + this._pageColor(i, options) + '"';
  503. result += '<page class="page page-preview" size="' + size + '" ' + style + '>\n';
  504. result += page.cards.join("");
  505. result += '</page>\n';
  506. }
  507. return result;
  508. }
  509. private _generatePagesDoublesided(cards: Card[], options: Options, rows: number, cols: number, generator: CardHtmlGenerator): CardPageSet<string> {
  510. var front_pages: CardPageSet<string> = new CardPageSet<string>(rows, cols);
  511. var back_pages: CardPageSet<string> = new CardPageSet<string>(rows, cols);
  512. var empty = generator.card_empty(options, this.indent);
  513. // Fill pages with cards
  514. for (var i = 0; i < cards.length; ++i) {
  515. var card = cards[i];
  516. var front = generator.card_front(card, options, this.indent);
  517. var back = generator.card_back(card, options, this.indent);
  518. front_pages.addCards(front, card.count);
  519. back_pages.addCards(back, card.count);
  520. }
  521. // Fill empty slots
  522. front_pages.forEach((page) => page.fillPage(empty));
  523. back_pages.forEach((page) => page.fillPage(empty));
  524. // Shuffle back cards so that they line up with their corresponding front cards
  525. back_pages.forEach((page) => page.flipH());
  526. // Interleave front and back pages so that we can print double-sided
  527. return front_pages.merge(back_pages);
  528. }
  529. private _generatePagesFrontOnly(cards: Card[], options: Options, rows: number, cols: number, generator: CardHtmlGenerator): CardPageSet<string> {
  530. var pages: CardPageSet<string> = new CardPageSet<string>(rows, cols);
  531. var empty = generator.card_empty(options, this.indent);
  532. // Fill pages with cards
  533. for (var i = 0; i < cards.length; ++i) {
  534. var card = cards[i];
  535. var front = generator.card_front(card, options, this.indent);
  536. pages.addCards(front, card.count);
  537. }
  538. return pages;
  539. }
  540. private _generatePagesSideBySide(cards: Card[], options: Options, rows: number, cols: number, generator: CardHtmlGenerator): CardPageSet<string> {
  541. if (cols < 2) {
  542. throw new Error("Need at least two columns for side-by-side");
  543. }
  544. var pages: CardPageSet<string> = new CardPageSet<string>(rows, cols);
  545. var empty = generator.card_empty(options, this.indent);
  546. // Fill pages with cards (two at a time)
  547. for (var i = 0; i < cards.length; ++i) {
  548. var card = cards[i];
  549. var front = generator.card_front(card, options, this.indent);
  550. var back = generator.card_back(card, options, this.indent);
  551. if (pages.lastPage().capacityRow() < 2) {
  552. pages.lastPage().fillRow(empty);
  553. }
  554. pages.addCards(front, card.count);
  555. pages.addCards(back, card.count);
  556. }
  557. return pages;
  558. }
  559. private _generatePages(cards: Card[], options: Options, rows: number, cols: number, generator: CardHtmlGenerator): CardPageSet<string> {
  560. switch (options.card_arrangement) {
  561. case "doublesided": return this._generatePagesDoublesided(cards, options, rows, cols, generator);
  562. case "front_only": return this._generatePagesFrontOnly(cards, options, rows, cols, generator);
  563. case "side_by_side": return this._generatePagesSideBySide(cards, options, rows, cols, generator);
  564. default: throw new Error("Unknown card arrangement");
  565. }
  566. }
  567. private _generateStyle(options) {
  568. var size = "a4";
  569. switch (options.page_size) {
  570. case "A3": size = "A3 portrait"; break;
  571. case "A4": size = "210mm 297mm"; break;
  572. case "A5": size = "A5 portrait"; break;
  573. case "Letter": size = "letter portrait"; break;
  574. case "25x35": size = "2.5in 3.5in"; break;
  575. default: size = "auto";
  576. }
  577. var result = "";
  578. result += "<style>\n";
  579. result += "@page {\n";
  580. result += " margin: 0;\n";
  581. result += " size:" + size + ";\n";
  582. result += " -webkit-print-color-adjust: exact;\n";
  583. result += "}\n";
  584. result += "</style>\n";
  585. return result;
  586. }
  587. public generateHtml(cards: Card[], options: Options) {
  588. options = options || new Options();
  589. var rows = options.page_rows || 3;
  590. var cols = options.page_columns || 3;
  591. // Generate the HTML for each card
  592. var generator = new CardHtmlGenerator();
  593. var pages: CardPageSet<string> = this._generatePages(cards, options, rows, cols, generator);
  594. // Wrap all pages in a <page> element
  595. var document = this._wrap(pages, options);
  596. // Generate the HTML for the page layout
  597. var style = this._generateStyle(options);
  598. // Wrap all pages in a <page> element and add CSS for the page size
  599. var result = "";
  600. result += style
  601. result += document;
  602. return result;
  603. }
  604. public insertInto(cards: Card[], options: Options, container: HTMLElement) {
  605. // Clear the previous content of the document
  606. while (container.hasChildNodes()) {
  607. container.removeChild(container.lastChild);
  608. }
  609. // Insert the HTML
  610. var html = this.generateHtml(cards, options);
  611. container.innerHTML = html;
  612. }
  613. }
  614. }