diff --git a/History.md b/History.md
index dc797fd2..6fbb57bd 100644
--- a/History.md
+++ b/History.md
@@ -1,8 +1,10 @@
+- 1.8.0 feature: --action=visualize convert zugferd xml to html
- migration: testIndicator entfernen
- in ram:ExchangedDocument: ram:name entfernen
- updated javadoc
- fixed #104 nullpointerex when specifying no parameter
- closed #23
+- #112
1.7.2
=====
diff --git a/pom.xml b/pom.xml
index 4ca4bb50..c8783537 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.mustangproject.ZUGFeRD
mustang
- 1.7.3-SNAPSHOT
+ 1.8.0-SNAPSHOT
jar
Mustang
The Mustang project is a java library to read and write ZUGFeRD meta data inside your invoice PDFs
diff --git a/src/main/java/org/mustangproject/toecount/Toecount.java b/src/main/java/org/mustangproject/toecount/Toecount.java
index dd3bea00..3d1ff415 100755
--- a/src/main/java/org/mustangproject/toecount/Toecount.java
+++ b/src/main/java/org/mustangproject/toecount/Toecount.java
@@ -31,9 +31,13 @@ import javax.xml.transform.TransformerException;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
+import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -82,6 +86,7 @@ public class Toecount {
System.out.println("Mustangproject.org " + org.mustangproject.ZUGFeRD.Version.VERSION + " \r\n"
+ "A Apache Public License library and command line tool for statistics on PDF invoices with\r\n"
+ "ZUGFeRD Metadata (http://www.zugferd.org)\r\n" + "\r\n" + getUsage() + "\r\n"
+ + "* --action=visualize to convert XML to HTML or \\r\\n"
+ "* Count operations\r\n" + "\t-d, --directory\tcount ZUGFeRD files in directory to be scanned\r\n"
+ "\t\tIf it is a directory, it will recurse.\r\n"
+ "\t-l, --listfromstdin\tcount ZUGFeRD files from a list of linefeed separated files on runtime.\r\n"
@@ -381,7 +386,51 @@ public class Toecount {
}
System.out.println("Written to " + outName);
+ try {
+ ExportResource("/xrechnung-viewer.css");
+ ExportResource("/xrechnung-viewer.js");
+
+ System.out.println("xrechnung-viewer.css and xrechnung-viewer.js written as well (to local working dir)");
+ } catch (Exception e) {
+ Logger.getLogger(Toecount.class.getName()).log(Level.SEVERE, null, e);
+ }
+
+
}
+
+ /**
+ * Export a resource embedded into a Jar file to the local file path.
+ *
+ * @param resourceName ie.: "/SmartLibrary.dll"
+ * @return The path to the exported resource
+ * @throws Exception
+ */
+ static public String ExportResource(String resourceName) throws Exception {
+ InputStream stream = null;
+ OutputStream resStreamOut = null;
+ String jarFolder;
+ try {
+ stream = Toecount.class.getResourceAsStream(resourceName);//note that each / is a directory down in the "jar tree" been the jar the root of the tree
+ if(stream == null) {
+ throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
+ }
+
+ int readBytes;
+ byte[] buffer = new byte[4096];
+ jarFolder = System.getProperty("user.dir");
+ resStreamOut = new FileOutputStream(jarFolder + resourceName);
+ while ((readBytes = stream.read(buffer)) > 0) {
+ resStreamOut.write(buffer, 0, readBytes);
+ }
+ } catch (Exception ex) {
+ throw ex;
+ } finally {
+ stream.close();
+ resStreamOut.close();
+ }
+
+ return jarFolder + resourceName;
+ }
private static void performUpgrade(String xmlName, String outName) throws IOException, TransformerException {
diff --git a/src/main/resources/xrechnung-viewer.css b/src/main/resources/xrechnung-viewer.css
new file mode 100644
index 00000000..adea4c9b
--- /dev/null
+++ b/src/main/resources/xrechnung-viewer.css
@@ -0,0 +1,917 @@
+/* Grundformatierung ********************************************/
+
+*,
+*:after,
+*:before
+{
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+}
+
+.clear:after
+{
+ content: ".";
+ clear: both;
+ display: block;
+ visibility: hidden;
+ height: 0;
+}
+
+html,
+body
+{
+ height: 100%;
+ min-width: 320px;
+ margin: 0;
+ padding: 0;
+ color: #000;
+ font-size: 14px;
+}
+
+body
+{
+ overflow-y: scroll;
+ background-color: rgba(4, 101, 161, 0.08);
+}
+
+h4
+{
+ color: inherit;
+ font-size: inherit;
+ margin-bottom: 0.5rem;
+}
+
+
+/* Grundaufbau *************************************************/
+
+.menue
+{
+ position: relative;
+ z-index: 2000;
+ background-color: #000;
+ margin-bottom: 30px;
+}
+
+.innen
+{
+ max-width: 1080px;
+ margin: 0 auto;
+ padding: 0 2%;
+}
+
+
+
+/* Formatierungen *************************************************/
+
+.color2
+{
+ color: rgba(0, 0, 0, 0.6);
+}
+
+.schwarz
+{
+ color: #555 !important;
+}
+
+.normal
+{
+ font-weight: normal;
+}
+
+.bold
+{
+ font-weight: bold;
+}
+
+.abstandUnten
+{
+ margin-bottom: 5px;
+}
+
+.abstandUntenKlein
+{
+ margin-bottom: 10px;
+}
+
+.noPaddingTop
+{
+ padding-top: 0 !important;
+}
+
+.ausrichtungRechts
+{
+ text-align: right;
+}
+
+
+
+
+/* Menü ********************************************************/
+
+button
+{
+ position: relative;
+ font-family: serif;
+ padding-top: 15px;
+ padding-left: 0;
+ padding-right: 0;
+ margin-right: 2%;
+}
+
+.btnAktiv
+{
+ font-size: 22px;
+ color: #ffb619;
+ height: 50px;
+ outline: none;
+ border: none;
+ background: none;
+}
+
+.btnAktiv:after
+{
+ content: "";
+ display: block;
+ position: absolute;
+ top: 50px;
+ left: 50%;
+ z-index: 10;
+ font-size: 0;
+ line-height: 0;
+ height: 0;
+ padding: 0;
+ margin: 0;
+ transform: translateX(-50%);
+ border: 15px solid #000;
+ border-right-color: transparent;
+ border-bottom-color: transparent;
+ border-left-color: transparent;
+}
+
+.btnInaktiv,
+.tab
+{
+ font-size: 22px;
+ color: #fff;
+ height: 50px;
+ z-index: 0;
+ outline: none;
+ border: none;
+ background: none;
+ transition: color 0.3s ease;
+}
+
+.btnInaktiv:hover,
+.tab:hover
+{
+ color: #ffb619;
+ cursor: pointer;
+}
+
+.divHide
+{
+ display: none;
+}
+
+/* Content *********************************************************************/
+
+.inhalt
+{
+ font-family: sans-serif;
+ margin-bottom: 30px;
+}
+
+.haftungausschluss
+{
+ color: #000;
+ text-align: center;
+ padding: 7px;
+ margin-bottom: 30px;
+ width: 100%;
+ border: 1px solid #ffb619;
+ background-color: #fff;
+}
+
+.box
+{
+ position: relative;
+ display: table-cell;
+ padding: 0;
+ border: 1px solid rgba(4, 101, 161, 0.2);
+ background-color: #fff;
+}
+
+.subBox
+{
+ border-top: none;
+ width: 50%;
+}
+
+.subBox:last-child
+{
+ border-left: none;
+}
+
+.first > .boxzeile > .subBox
+{
+ border-top: 1px solid rgba(4, 101, 161, 0.2) !important;
+}
+
+.boxtitel
+{
+ display: inline-block;
+ background-color: #0465A1;
+ padding: 7px 10px;
+ color: #fff;
+ font-weight: bold;
+}
+
+.boxBorderTop
+{
+ border-top: none;
+}
+
+.boxBorderLeft
+{
+ border-left: none;
+}
+
+.boxtitelSub
+{
+ color: #000;
+ background-color: rgba(4, 101, 161, 0.1);
+ border-right: 1px solid rgba(4, 101, 161, 0.2);
+ border-bottom: 1px solid rgba(4, 101, 161, 0.2);
+}
+
+.boxinhalt
+{
+ padding: 15px 20px;
+}
+
+.boxtabelle
+{
+ display: table;
+ width: 100%;
+}
+
+.borderSpacing
+{
+ border-spacing: 0 5px;
+}
+
+.boxabstandtop
+{
+ margin-top: 30px;
+}
+
+.boxzeile
+{
+ display: table-row;
+}
+
+.boxzeile .box:last-child
+{
+ margin-bottom: 0;
+}
+
+.boxdaten
+{
+ display: table-cell;
+ padding: 5px 0;
+ vertical-align: middle;
+ height: 38px;
+ /*
+ -ms-word-break: break-all;
+ word-break: break-all;
+ word-break: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+*/
+}
+
+.boxdaten.wert
+{
+ padding: 5px 10px;
+}
+
+.boxcell
+{
+ display: table-cell;
+}
+
+.boxdatenBlock
+{
+ display: block;
+ padding: 3px 0;
+ /*
+ -ms-word-break: break-all;
+ word-break: break-all;
+ word-break: break-word;
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ hyphens: auto;
+*/
+}
+
+.noBreak
+{
+ -ms-word-break: keep-all;
+ word-break: keep-all;
+ word-break: keep-all;
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ hyphens: none;
+}
+
+.boxabstand
+{
+ display: table-cell;
+ width: 30px;
+}
+
+.legende
+{
+ color: rgba(0, 0, 0, 0.6);
+ width: 170px;
+ font-size: 13px;
+ line-height: 16px;
+ padding-right: 5px;
+}
+
+.wert
+{
+ background-color: rgba(4, 101, 161, 0.03);
+}
+
+.boxtabelleEinspaltig
+{
+ width: 49%;
+}
+
+.boxtabelleZweispaltig,
+.boxtabelleDreispaltig
+{
+ width: 100%;
+}
+
+.box5050
+{
+ width: 50%;
+}
+
+.boxEinspaltig
+{
+ width: 100%;
+}
+
+.boxZweispaltig
+{
+ width: 48.5%;
+}
+
+.boxSpalte1 {
+ width: 50%;
+}
+
+.boxSpalte2 {
+ width: 50%;
+ padding-left: 20px;
+}
+
+.paddingLeft {
+ padding-left: 0.1em;
+}
+
+.noPadding {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+}
+
+.rechnungsZeile
+{
+ display: table-row;
+}
+
+.rechnungsZeile .boxdaten
+{
+ height: auto;
+}
+
+.rechnungSp1
+{
+ width: 65%;
+ font-size: 16px;
+}
+
+.rechnungSp2
+{
+ width: 10%;
+}
+
+.rechnungSp3
+{
+ width: 25%;
+ font-size: 16px;
+ text-align: right;
+}
+
+.detailSp1,
+.detailSp2
+{
+ width: 50%;
+}
+
+.detailSp2
+{
+ text-align: right;
+}
+
+.line1Bottom
+{
+ border-bottom: 1px solid #000;
+}
+
+.line1BottomLight
+{
+ padding-bottom: 5px;
+ border-bottom: 1px solid #f0f0f0;
+ margin-bottom: 5px;
+}
+
+.line2Bottom
+{
+ border-bottom: 2px solid #000;
+}
+
+.paddingTop
+{
+ padding-top: 10px;
+}
+
+.paddingBottom
+{
+ padding-bottom: 10px;
+}
+
+.grund
+{
+ font-size: 16px;
+ display: block;
+ width: 100%;
+ padding: 0 20px 15px 20px;
+}
+
+.grundDetail
+{
+ display: block;
+ width: 100%;
+ padding: 0 20px 15px 20px;
+}
+
+/* Übersichtformatierungen */
+#uebersichtLastschrift.box,
+#uebersichtUeberweisung.box
+{
+ border-top: none;
+}
+
+#uebersichtUeberweisung.box
+{
+ border-left: none;
+}
+
+
+/* Formatierungen Detailseite */
+
+.detailsSpalte1,
+.detailsSpalte2
+{
+ width: 30%;
+ float: left;
+ font-size: 90%;
+ line-height: 115%;
+ margin-right: 5%;
+}
+
+.detailsSpalte3
+{
+ width: 30%;
+ float: left;
+ font-size: 90%;
+ line-height: 115%;
+}
+
+.detailsSpalte1 .legende,
+.detailsSpalte2 .legende,
+.detailsSpalte3 .legende
+{
+ width: 145px;
+}
+
+.titelPosition
+{
+ font-size: 17px;
+ font-weight: bold;
+}
+
+
+/* Laufzettelformatierungen */
+#laufzettelHistorie .boxtabelle:not(:nth-child(2))
+{
+ border-top: 1px solid rgba(4, 101, 161, 0.2);
+ padding-top: 10px;
+ margin-top: 10px;
+}
+
+
+
+
+
+/* 1023px und kleiner ************************************************/
+
+@media screen and (max-width : 1023px) {
+
+ .box
+ {
+ display: block;
+ width: 100%;
+ margin-bottom: 20px;
+ }
+
+ .boxabstandtop
+ {
+ margin-top: 15px;
+ }
+
+ .subBox:first-child
+ {
+ margin-bottom: 0 !important;
+ }
+
+ .subBox:last-child
+ {
+ border-left: 1px solid rgba(4, 101, 161, 0.2);
+ }
+
+ .first > .boxzeile > .subBox
+ {
+ border-top: none !important;
+ }
+
+ .first > .boxzeile > .subBox:first-child
+ {
+ border-top: 1px solid rgba(4, 101, 161, 0.2) !important;
+ }
+
+ .first > .boxzeile
+ {
+ margin-bottom: 0;
+ }
+
+ #uebersichtUeberweisung.box
+ {
+ border-left: 1px solid rgba(4, 101, 161, 0.2);
+ }
+
+ #uebersichtLastschrift.box
+ {
+ margin-bottom: 0;
+ }
+
+ .boxzeile
+ {
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ .boxzeile:after
+ {
+ visibility: hidden;
+ display: block;
+ font-size: 0;
+ content: " ";
+ clear: both;
+ height: 0;
+ }
+
+ #details > .boxtabelle > .boxzeile
+ {
+ margin-bottom: 0px;
+ }
+
+ .boxcell
+ {
+ display: block;
+ }
+
+ .boxcell:last-child
+ {
+ margin-top: 20px;
+ }
+
+ .boxZweispaltig
+ {
+ width: 100%;
+ }
+
+ .legende
+ {
+ display: block;
+ float: left;
+ width: 170px;
+ padding: 5px 0;
+ height: auto;
+ }
+
+ .wert
+ {
+ display: block;
+ float: left;
+ width: calc(100% - 170px);
+ padding: 11px 10px !important;
+ line-height: 1.3;
+ min-height: 38px;
+ height: auto;
+ }
+
+ .boxdaten .legende
+ {
+ height: auto;
+ }
+
+ .rechnungsZeile .boxdaten
+ {
+ padding: 5px 0;
+ }
+
+ .boxabstand
+ {
+ display: none;
+ }
+
+ .boxtabelleEinspaltig {
+ width: 100%;
+ }
+
+ .boxSpalte1 {
+ display: block;
+ width: auto;
+ }
+
+ .boxSpalte2 {
+ display: block;
+ width: auto;
+ padding-left: 0px;
+ margin-top: 1.2rem;
+ }
+
+ .detailsSpalte1,
+ .detailsSpalte2,
+ .detailsSpalte3
+ {
+ width: 100%;
+ float: none;
+ padding-right: 0px;
+ }
+
+ .detailsSpalte2,
+ .detailsSpalte3
+ {
+ margin-top: 15px;
+ }
+
+ .detailsSpalte2,
+ .detailsSpalte3
+ {
+ margin-top: 10px;
+ }
+
+ .tableNumberAlignRight
+ {
+ display: block;
+ width: 130px;
+ text-align: right;
+ }
+}
+
+
+
+/* 800px und kleiner ************************************************/
+
+@media screen and (max-width : 800px) {
+
+ button
+ {
+ padding-top: 10px;
+ }
+
+ .btnAktiv,
+ .btnInaktiv,
+ .tab
+ {
+ font-size: 20px;
+ height: 40px;
+ }
+
+ .btnAktiv:after
+ {
+ top: 40px;
+ }
+
+ .rechnungSp1
+ {
+ width: 55%;
+ font-size: 15px;
+ }
+
+ .rechnungSp2
+ {
+ width: 10%;
+ }
+
+ .rechnungSp3
+ {
+ width: 35%;
+ text-align: right;
+ font-size: 15px;
+ }
+
+ .grund
+ {
+ font-size: 15px;
+ }
+}
+
+/* 450px und kleiner ************************************************/
+
+@media screen and (max-width : 450px)
+{
+
+ html,
+ body
+ {
+ font-size: 12px;
+ }
+
+ .menue
+ {
+ margin-bottom: 20px;
+ }
+
+ button
+ {
+ padding-top: 5px;
+ }
+
+ .btnAktiv,
+ .btnInaktiv,
+ .tab
+ {
+ font-size: 17px;
+ height: 35px;
+ }
+
+ .btnAktiv:after
+ {
+ top: 35px;
+ }
+
+ .legende
+ {
+ font-size: 12px;
+ width: 100%;
+ }
+
+ .wert
+ {
+ font-size: 12px;
+ line-height: 1.3;
+ width: 100%;
+ margin-bottom: 10px
+ }
+
+ .boxzeile
+ {
+ margin-bottom: 0px
+ }
+
+ .boxdaten
+ {
+ height: auto;
+ }
+
+ .haftungausschluss
+ {
+ margin-bottom: 20px;
+ }
+
+ .boxinhalt
+ {
+ margin-top: 0px;
+ }
+
+ .boxabstandtop
+ {
+ margin-top: 20px;
+ }
+
+ .boxtitel
+ {
+ padding: 7px 8px;
+ }
+
+ .box
+ {
+ margin-bottom: 10px;
+ padding: 0;
+ }
+
+ .boxabstandtop
+ {
+ margin-top: 10px;
+ }
+
+ .boxdaten,
+ .boxdatenBlock
+ {
+ padding: 2px 0;
+ }
+
+ .rechnungSp1
+ {
+ width: 50%;
+ font-size: inherit;
+ }
+
+ .rechnungSp2
+ {
+ width: 15%;
+ }
+
+ .rechnungSp3
+ {
+ width: 35%;
+ font-size: inherit;
+ text-align: right;
+ }
+
+ .grund
+ {
+ font-size: inherit;
+ }
+
+ .titelPosition
+ {
+ font-size: 15px;
+ }
+
+ .abstandUnten
+ {
+ margin-bottom: 5px;
+ }
+
+ .detailsSpalte1,
+ .detailsSpalte2,
+ .detailsSpalte3
+ {
+ font-size: inherit;
+ line-height: inherit;
+ }
+}
+
+/* 380px und kleiner ************************************************/
+
+@media screen and (max-width : 380px) {
+
+ html,
+ body
+ {
+ font-size: 11px;
+ line-height: 100%;
+ }
+
+ .btnAktiv,
+ .btnInaktiv,
+ .tab
+ {
+ font-size: 15px;
+ }
+
+ .boxdaten
+.boxdatenBlock
+ {
+ padding: 2px 0;
+ }
+
+ .boxinhalt
+ {
+ margin-top: 0px;
+ }
+
+ .boxtitel
+ {
+ padding: 5px 7px;
+ }
+}
diff --git a/src/main/resources/xrechnung-viewer.js b/src/main/resources/xrechnung-viewer.js
new file mode 100644
index 00000000..2d547b86
--- /dev/null
+++ b/src/main/resources/xrechnung-viewer.js
@@ -0,0 +1,145 @@
+
+/* Tab-Container aufbauen **************************************************/
+
+var a = new Array("uebersicht", "details", "zusaetze", "anlagen", "laufzettel");
+var b = new Array("menueUebersicht", "menueDetails", "menueZusaetze", "menueAnlagen", "menueLaufzettel");
+
+function show(e) {
+ var i = 0;
+ var j = 1;
+ for (var t = 0; t < b.length; t++) {
+ if (b[t] === e.id) {
+ i = t;
+ if (i > 0) {
+ j = 0;
+ } else {
+ j = i + 1;
+ }
+ break;
+ }
+ }
+ e.setAttribute("class", "btnAktiv");
+ for (var k = 0; k < b.length; k++) {
+ if (k === i && (document.getElementById(a[k]) != null)) {
+ document.getElementById(a[k]).style.display = "block";
+ if (i === j)
+ j = i + 1;
+ } else {
+ if (document.getElementById(a[k]) != null) {
+ document.getElementById(a[j]).style.display = "none";
+ document.getElementById(b[j]).setAttribute("class", "btnInaktiv");
+ j += 1;
+ }
+ }
+ }
+}
+window.onload = function () {
+ document.getElementById(b[0]).setAttribute("class", "btnAktiv");
+}
+
+/* Eingebettete Binaerdaten runterladen ************************************/
+
+
+function base64_to_binary (data) {
+ var chars = atob(data);
+ var bytes = new Array(chars.length);
+ for (var i = 0; i < chars.length; i++) {
+ bytes[i] = chars.charCodeAt(i);
+ }
+ return new Uint8Array(bytes);
+}
+
+function downloadData (element_id) {
+ var data_element = document.getElementById(element_id);
+ var mimetype = data_element.getAttribute('mimeType');
+ var filename = data_element.getAttribute('filename');
+ var text = data_element.innerHTML;
+ var binary = base64_to_binary(text);
+ var blob = new Blob([binary], {
+ type: mimetype, size: binary.length
+ });
+
+ if (window.navigator && window.navigator.msSaveOrOpenBlob) {
+ // IE
+ window.navigator.msSaveOrOpenBlob(blob, filename);
+ } else {
+ // Non-IE
+ var url = window.URL.createObjectURL(blob);
+ window.open(url);
+ }
+}
+
+
+/* Polyfill IE atob/btoa ************************************/
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define([], function () {
+ factory(root);
+ });
+ } else factory(root);
+ // node.js has always supported base64 conversions, while browsers that support
+ // web workers support base64 too, but you may never know.
+})(typeof exports !== "undefined" ? exports: this, function (root) {
+ if (root.atob) {
+ // Some browsers' implementation of atob doesn't support whitespaces
+ // in the encoded string (notably, IE). This wraps the native atob
+ // in a function that strips the whitespaces.
+ // The original function can be retrieved in atob.original
+ try {
+ root.atob(" ");
+ }
+ catch (e) {
+ root.atob = (function (atob) {
+ var func = function (string) {
+ return atob(String(string).replace(/[\t\n\f\r ]+/g, ""));
+ };
+ func.original = atob;
+ return func;
+ })(root.atob);
+ }
+ return;
+ }
+
+ // base64 character set, plus padding character (=)
+ var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+ // Regular expression to check formal correctness of base64 encoded strings
+ b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
+
+ root.btoa = function (string) {
+ string = String(string);
+ var bitmap, a, b, c,
+ result = "", i = 0,
+ rest = string.length % 3; // To determine the final padding
+
+ for (; i < string.length;) {
+ if ((a = string.charCodeAt(i++)) > 255 || (b = string.charCodeAt(i++)) > 255 || (c = string.charCodeAt(i++)) > 255)
+ throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.");
+
+ bitmap = (a << 16) | (b << 8) | c;
+ result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63);
+ }
+
+ // If there's need of padding, replace the last 'A's with equal signs
+ return rest ? result.slice(0, rest - 3) + "===".substring(rest): result;
+ };
+
+ root.atob = function (string) {
+ // atob can work with strings with whitespaces, even inside the encoded part,
+ // but only \t, \n, \f, \r and ' ', which can be stripped.
+ string = String(string).replace(/[\t\n\f\r ]+/g, "");
+ if (! b64re.test(string))
+ throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
+
+ // Adding the padding if missing, for semplicity
+ string += "==".slice(2 - (string.length & 3));
+ var bitmap, result = "", r1, r2, i = 0;
+ for (; i < string.length;) {
+ bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++)));
+
+ result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255): r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255): String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255);
+ }
+ return result;
+ };
+});
\ No newline at end of file