瀏覽代碼

demo for gopherjs done

kpmy 10 年之前
父節點
當前提交
731d87caed
共有 8 個文件被更改,包括 418 次插入10 次删除
  1. 22 0
      demo3/demo.go
  2. 22 2
      demo3/demo3.go
  3. 2 0
      demo3/pubspec.yaml
  4. 119 2
      demo3/report.go
  5. 5 1
      demo3/web/index.html
  6. 211 0
      demo3/web/js/Blob.js
  7. 1 0
      demo3/web/js/FileSaver.min.js
  8. 36 5
      demo3/web/main.dart

+ 22 - 0
demo3/demo.go

@@ -0,0 +1,22 @@
+package main
+
+import (
+	"bytes"
+	"io"
+	"odf/generators"
+	"odf/mappers"
+	"odf/model"
+	"odf/xmlns"
+)
+
+func demo() (io.Reader, error) {
+	output := bytes.NewBuffer(nil)
+	m := model.ModelFactory()
+	fm := &mappers.Formatter{}
+	fm.ConnectTo(m)
+	fm.MimeType = xmlns.MimeText
+	fm.Init()
+	fm.WriteString("Hello, World!")
+	generators.GeneratePackage(m, nil, output, fm.MimeType)
+	return output, nil
+}

+ 22 - 2
demo3/demo3.go

@@ -1,16 +1,22 @@
 package main
 
 import (
+	"bytes"
+	"encoding/base64"
 	"github.com/gopherjs/gopherjs/js"
 	"github.com/kpmy/ypk/assert"
+	"github.com/kpmy/ypk/halt"
 	"github.com/mitchellh/mapstructure"
+	"io"
 	"log"
 	_ "odf/model/stub" //don't forget pimpl
 	"sync"
 )
 
 type Msg struct {
-	Typ string
+	Typ   string
+	Param string
+	Data  string
 }
 
 type Handler func(m *Msg)
@@ -27,7 +33,21 @@ func busHandler(m *Msg) {
 func handle(m *Msg) {
 	switch m.Typ {
 	case "init":
-		panic("not implemented")
+		log.Println("message bus connected")
+	case "get":
+		var rd io.Reader
+		if m.Param == "demo" {
+			rd, _ = demo()
+		} else if m.Param == "report" {
+			rd, _ = report()
+		}
+		buf := bytes.NewBuffer(nil)
+		io.Copy(buf, rd)
+		m := &Msg{Typ: "data"}
+		m.Data = base64.StdEncoding.EncodeToString(buf.Bytes())
+		Process(m)
+	default:
+		halt.As(100, "not implemented", m.Typ)
 	}
 }
 

+ 2 - 0
demo3/pubspec.yaml

@@ -7,3 +7,5 @@ environment:
   sdk: '>=1.0.0 <2.0.0'
 dependencies:
   browser: '>=0.10.0 <0.11.0'
+  crypto:
+    git: https://github.com/dart-lang/crypto

+ 119 - 2
demo3/report.go

@@ -2,21 +2,138 @@ package main
 
 import (
 	"bytes"
+	"encoding/base64"
+	"github.com/kpmy/golorem"
+	"image/color"
 	"io"
+	"math/rand"
 	"odf/generators"
 	"odf/mappers"
+	"odf/mappers/attr"
 	"odf/model"
 	"odf/xmlns"
+	"odf/xmlns/fo"
+	"strconv"
+	"strings"
+	"time"
 )
 
+func r(suffix string, fm *mappers.Formatter) {
+	{ //first page
+		fm.WriteString("\n\n\n\n\n\n\n\n\n\n")
+		fm.SetAttr(new(attr.TextAttributes).Size(32).Bold()).SetAttr(new(attr.ParagraphAttributes).AlignCenter())
+		fm.WritePara("Periodic report")
+		fm.SetAttr(nil).SetAttr(new(attr.TextAttributes).Italic())
+		fm.WritePara("Report:\t")
+		fm.SetAttr(new(attr.TextAttributes).Bold())
+		fm.WriteString(suffix + "\n")
+		fm.SetAttr(new(attr.TextAttributes).Italic())
+		fm.WriteString("Date:\t")
+		fm.SetAttr(new(attr.TextAttributes).Bold())
+		fm.WriteString(time.Now().String())
+		fm.SetAttr(new(attr.ParagraphAttributes).PageBreak())
+		fm.WritePara("")
+		fm.SetAttr(nil)
+	}
+	para := func() {
+		fm.SetAttr(new(attr.TextAttributes).Bold().Size(18))
+		fm.WritePara(strings.ToUpper(lorem.Word(5, 15)))
+		fm.WriteLn()
+		fm.SetAttr(nil)
+		para := lorem.Paragraph(5, 10)
+		fm.WritePara("\t" + para + "\n\n")
+	}
+	for i := 0; i < 5; i++ {
+		para()
+	}
+	{ //huge table
+		fm.SetAttr(new(attr.TextAttributes).Bold().Size(18))
+		fm.WritePara("TABLE 50x5")
+		fm.SetAttr(nil)
+		fm.SetAttr(new(attr.TableCellAttributes).Border(attr.Border{Width: 0.01, Color: color.Black, Style: fo.Solid}))
+		tm := &mappers.TableMapper{}
+		tm.ConnectTo(fm)
+		tm.Write("test", 50+1, 5) //50+header row
+		tt := tm.List["test"]
+		tm.Span(tt, 0, 0, 1, 5)
+		fm.SetAttr(new(attr.ParagraphAttributes).AlignCenter()).SetAttr(new(attr.TextAttributes).Bold())
+		tm.Pos(tt, 0, 0).WriteString("Header")
+		fm.SetAttr(nil)
+		for i := 1; i < 51; i++ {
+			for j := 0; j < 5; j++ {
+				if j == 0 {
+					fm.SetAttr(new(attr.TextAttributes).Bold())
+				} else {
+					fm.SetAttr(nil)
+				}
+				tm.Pos(tt, i, j).WriteString(strconv.Itoa(i * j))
+			}
+		}
+	}
+	{ //appendix
+		fm.RegisterFont("Courier New", "Courier New") // may not work in Linux/MacOS
+		fm.SetAttr(nil).SetAttr(new(attr.ParagraphAttributes).PageBreak())
+		fm.SetAttr(new(attr.TextAttributes).Size(18).Bold())
+		fm.WritePara("Appendix A.\nListing of report.go")
+		fm.WriteLn()
+		fm.SetAttr(nil).SetAttr(new(attr.TextAttributes).FontFace("Courier New").Size(6))
+		fm.WritePara("File not found because no IO allowed in browser.")
+	}
+}
+
 func report() (io.Reader, error) {
+	src := rand.New(rand.NewSource(time.Now().UnixNano() + rand.Int63()))
+	suffix := strconv.Itoa(src.Int())
 	output := bytes.NewBuffer(nil)
 	m := model.ModelFactory()
 	fm := &mappers.Formatter{}
 	fm.ConnectTo(m)
 	fm.MimeType = xmlns.MimeText
 	fm.Init()
-	fm.WriteString("Hello, World!")
-	generators.GeneratePackage(m, nil, output, fm.MimeType)
+	embed := make(map[string]generators.Embeddable)
+	{
+		const ImagePng xmlns.Mime = "image/png"
+		if data, err := base64.StdEncoding.DecodeString(imgData); err == nil {
+			img := bytes.NewBuffer(data)
+			d := mappers.NewDraw(img, ImagePng)
+			fm.SetAttr(new(attr.ParagraphAttributes).AlignRight())
+			url := d.WriteTo(fm, "Two Gophers", 4.0, 4.0) //magic? real size of `.png` in cm
+			embed[url] = d
+		}
+	}
+	fm.SetAttr(new(attr.TextAttributes).Bold())
+	fm.WriteString("\nSo Strange inc.")
+	fm.WriteString("\n" + lorem.Email())
+	fm.WriteString("\n" + lorem.Url())
+	fm.SetAttr(nil)
+	r(suffix, fm)
+	generators.GeneratePackage(m, embed, output, fm.MimeType)
 	return output, nil
 }
+
+var imgData = `iVBORw0KGgoAAAANSUhEUgAAAaQAAAGkCAYAAAB+TFE1AAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
+WXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3wULEQENShOIkAAABSxJREFUeNrt18EJAjEURVEj01E6
+STuzTjvpJDU914IbEfUr51QwvPlwSUtyAYBvu5oAAEECAEECQJAAQJAAECQAECQABAkABAkAQQIA
+QQJAkABAkAAQJAAQJAAECQAECQBBAgBBAkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQA
+ECQABAkABAkABAmAIg4T1Df2jBXgdaufzQpeSAAgSAAIEgAIEgCCBACCBIAgAYAgASBIACBIAAgS
+AAgSAIIEAIIEgCABgCABIEgAIEgACBIACBIAggQAggSAIAGAIAEgSAAgSAAgSAAIEgAIEgCCBACC
+BIAgAYAgASBIACBIAAgSAAgSAIIEAIIEgCABgCABIEgAIEgACBIACBIAggQAggSAIAGAIAEgSAAg
+SAAIEgAIEgCCBACCBIAgAYAgASBIACBIACBIAAgSAAgSAIIEAIIEgCABgCABIEgAIEgACBIAPKEl
+KfMxY8/4JQCftfrZvJAAQJAAECQAECQABAkABAkAQQIAQQJAkABAkAAQJAAQJAAECQAECQBBAgBB
+AkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkAQQIAQQIAQQJAkABAkAAQ
+JAAQJAAECQAECQBBAgBBAkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkA
+QQIAQQJAkABAkAAQJAAQJAAECQAECQBBAgBBAgBBAkCQAECQABAkABAkAOo6TPAbVj+bFe6NPWMF
+9+JevJAAQJAAECQAECQABAkABAkAQQIAQQJAkABAkAAQJAAQJAAECQAECQAECQBBAgBBAkCQAECQ
+ABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkAQQIAQQJAkABAkAAQJAAQJAAECQAE
+CQBBAgBBAkCQAECQABAkABAkAAQJAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkAQQIAQQJA
+kABAkAAQJAAQJAAECQAECQBBAgBBAkCQAECQABAkAHijlsQKAHghAYAgASBIACBIAAgSAAgSAIIE
+AIIEgCABgCABIEgAIEgACBIACBIAggQAggSAIAGAIAEgSAAgSAAIEgAIEgCCBACCBIAgAYAgASBI
+ACBIAAgSAAgSAIIEAIIEgCCZAABBAgBBAkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQA
+ECQABAkABAkAQQIAQQJAkABAkAAQJAAQJAAECQAECQBBAgBBAkCQAECQABAkABAkAAQJAAQJAAQJ
+AEECAEECoJij0seMPeOXPLb62azgXtyLe/nne/FCAqAEQQJAkABAkAAQJAAQJAAECQAECQBBAgBB
+AkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkAQQIAQQJAkABAkAAQJAAQ
+JAAECQAECQBBAgBBAgBBAkCQAECQABAkABAkAAQJAAQJAEECAEECQJAAQJAAECQAECQABAkABAkA
+QQIAQQJAkABAkAAQJAAQJAAECQAECQBBAgBBAkCQAECQABAkABAkAAQJAAQJAEECAEECAEECQJAA
+QJAAKKglsQIAXkgAIEgACBIACBIAggQAggSAIAGAIAEgSAAgSAAIEgAIEgCCBACCBIAgAYAgASBI
+ACBIAAgSAAgSAIIEAIIEgCABgCABIEgAIEgACBIACBIAggQAggSAIAGAIAEgSCYAoIIb4ogrP3v0
+vosAAAAASUVORK5CYII=`

+ 5 - 1
demo3/web/index.html

@@ -18,8 +18,12 @@
 
 <body>
 
-  <div id="output">golang gopherjs kpmy/odf dartlang html web workers kpmy/odf/demo3</div>
+  <div id="output">golang gopherjs kpmy/odf dartlang html web workers kpmy/odf/demo3 eligrey/filesaver eligrey/blob</div>
+  In-browser ODF generator: <input id="do-demo" type="button" value="Do Demo"/>
+  <input id="do-report" type="button" value="Do Report"/>
 
+<script type="application/javascript" src="js/blob.js"></script>
+<script type="application/javascript" src="js/filesaver.min.js"></script>
 <script type="application/dart" src="main.dart"></script>
 <script data-pub-inline src="packages/browser/dart.js"></script>
 </body>

+ 211 - 0
demo3/web/js/Blob.js

@@ -0,0 +1,211 @@
+/* Blob.js
+ * A Blob implementation.
+ * 2014-07-24
+ *
+ * By Eli Grey, http://eligrey.com
+ * By Devin Samarin, https://github.com/dsamarin
+ * License: X11/MIT
+ *   See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
+ */
+
+/*global self, unescape */
+/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
+  plusplus: true */
+
+/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
+
+(function (view) {
+	"use strict";
+
+	view.URL = view.URL || view.webkitURL;
+
+	if (view.Blob && view.URL) {
+		try {
+			new Blob;
+			return;
+		} catch (e) {}
+	}
+
+	// Internally we use a BlobBuilder implementation to base Blob off of
+	// in order to support older browsers that only have BlobBuilder
+	var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
+		var
+			  get_class = function(object) {
+				return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
+			}
+			, FakeBlobBuilder = function BlobBuilder() {
+				this.data = [];
+			}
+			, FakeBlob = function Blob(data, type, encoding) {
+				this.data = data;
+				this.size = data.length;
+				this.type = type;
+				this.encoding = encoding;
+			}
+			, FBB_proto = FakeBlobBuilder.prototype
+			, FB_proto = FakeBlob.prototype
+			, FileReaderSync = view.FileReaderSync
+			, FileException = function(type) {
+				this.code = this[this.name = type];
+			}
+			, file_ex_codes = (
+				  "NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+				+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
+			).split(" ")
+			, file_ex_code = file_ex_codes.length
+			, real_URL = view.URL || view.webkitURL || view
+			, real_create_object_URL = real_URL.createObjectURL
+			, real_revoke_object_URL = real_URL.revokeObjectURL
+			, URL = real_URL
+			, btoa = view.btoa
+			, atob = view.atob
+
+			, ArrayBuffer = view.ArrayBuffer
+			, Uint8Array = view.Uint8Array
+
+			, origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/
+		;
+		FakeBlob.fake = FB_proto.fake = true;
+		while (file_ex_code--) {
+			FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
+		}
+		// Polyfill URL
+		if (!real_URL.createObjectURL) {
+			URL = view.URL = function(uri) {
+				var
+					  uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
+					, uri_origin
+				;
+				uri_info.href = uri;
+				if (!("origin" in uri_info)) {
+					if (uri_info.protocol.toLowerCase() === "data:") {
+						uri_info.origin = null;
+					} else {
+						uri_origin = uri.match(origin);
+						uri_info.origin = uri_origin && uri_origin[1];
+					}
+				}
+				return uri_info;
+			};
+		}
+		URL.createObjectURL = function(blob) {
+			var
+				  type = blob.type
+				, data_URI_header
+			;
+			if (type === null) {
+				type = "application/octet-stream";
+			}
+			if (blob instanceof FakeBlob) {
+				data_URI_header = "data:" + type;
+				if (blob.encoding === "base64") {
+					return data_URI_header + ";base64," + blob.data;
+				} else if (blob.encoding === "URI") {
+					return data_URI_header + "," + decodeURIComponent(blob.data);
+				} if (btoa) {
+					return data_URI_header + ";base64," + btoa(blob.data);
+				} else {
+					return data_URI_header + "," + encodeURIComponent(blob.data);
+				}
+			} else if (real_create_object_URL) {
+				return real_create_object_URL.call(real_URL, blob);
+			}
+		};
+		URL.revokeObjectURL = function(object_URL) {
+			if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
+				real_revoke_object_URL.call(real_URL, object_URL);
+			}
+		};
+		FBB_proto.append = function(data/*, endings*/) {
+			var bb = this.data;
+			// decode data to a binary string
+			if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
+				var
+					  str = ""
+					, buf = new Uint8Array(data)
+					, i = 0
+					, buf_len = buf.length
+				;
+				for (; i < buf_len; i++) {
+					str += String.fromCharCode(buf[i]);
+				}
+				bb.push(str);
+			} else if (get_class(data) === "Blob" || get_class(data) === "File") {
+				if (FileReaderSync) {
+					var fr = new FileReaderSync;
+					bb.push(fr.readAsBinaryString(data));
+				} else {
+					// async FileReader won't work as BlobBuilder is sync
+					throw new FileException("NOT_READABLE_ERR");
+				}
+			} else if (data instanceof FakeBlob) {
+				if (data.encoding === "base64" && atob) {
+					bb.push(atob(data.data));
+				} else if (data.encoding === "URI") {
+					bb.push(decodeURIComponent(data.data));
+				} else if (data.encoding === "raw") {
+					bb.push(data.data);
+				}
+			} else {
+				if (typeof data !== "string") {
+					data += ""; // convert unsupported types to strings
+				}
+				// decode UTF-16 to binary string
+				bb.push(unescape(encodeURIComponent(data)));
+			}
+		};
+		FBB_proto.getBlob = function(type) {
+			if (!arguments.length) {
+				type = null;
+			}
+			return new FakeBlob(this.data.join(""), type, "raw");
+		};
+		FBB_proto.toString = function() {
+			return "[object BlobBuilder]";
+		};
+		FB_proto.slice = function(start, end, type) {
+			var args = arguments.length;
+			if (args < 3) {
+				type = null;
+			}
+			return new FakeBlob(
+				  this.data.slice(start, args > 1 ? end : this.data.length)
+				, type
+				, this.encoding
+			);
+		};
+		FB_proto.toString = function() {
+			return "[object Blob]";
+		};
+		FB_proto.close = function() {
+			this.size = 0;
+			delete this.data;
+		};
+		return FakeBlobBuilder;
+	}(view));
+
+	view.Blob = function(blobParts, options) {
+		var type = options ? (options.type || "") : "";
+		var builder = new BlobBuilder();
+		if (blobParts) {
+			for (var i = 0, len = blobParts.length; i < len; i++) {
+				if (Uint8Array && blobParts[i] instanceof Uint8Array) {
+					builder.append(blobParts[i].buffer);
+				}
+				else {
+					builder.append(blobParts[i]);
+				}
+			}
+		}
+		var blob = builder.getBlob(type);
+		if (!blob.slice && blob.webkitSlice) {
+			blob.slice = blob.webkitSlice;
+		}
+		return blob;
+	};
+
+	var getPrototypeOf = Object.getPrototypeOf || function(object) {
+		return object.__proto__;
+	};
+	view.Blob.prototype = getPrototypeOf(new view.Blob());
+}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

文件差異過大導致無法顯示
+ 1 - 0
demo3/web/js/FileSaver.min.js


+ 36 - 5
demo3/web/main.dart

@@ -1,18 +1,49 @@
 import 'dart:html';
+import 'package:crypto/crypto.dart';
+import 'dart:typed_data';
+import 'dart:js';
 
 class OdfWorker{
 
   Worker inner;
 
+  void postMessage(message){
+    this.inner.postMessage(message);
+  }
+
+  void listen(handler){
+    this.inner.onMessage.listen(handler);
+  }
+
   OdfWorker(){
     this.inner = new Worker("demo3.js");
-    this.inner.onMessage.listen((m){
-      print("worker initialized, sending responce...");
-      this.inner.postMessage({'Typ': 'init'});
-    });
   }
 }
 
 void main() {
-  new OdfWorker();
+  var w = new OdfWorker();
+
+  w.listen((m){
+    switch(m.data["Typ"]){
+      case "init":
+        print("worker initialized, sending responce...");
+        w.postMessage({'Typ': 'init'});
+        break;
+      case "data":
+        print("data received");
+        Uint8List data = new Uint8List.fromList(CryptoUtils.base64StringToBytes(m.data["Data"]));
+        context.callMethod("saveAs",  [new Blob([data], "application/octet-stream"), "report.odf"]);
+        break;
+      default: throw new ArgumentError(m.data["Typ"]);
+    }
+  });
+
+  querySelector("#do-demo").onClick.listen((m){
+    w.postMessage({'Typ': 'get', 'Param': 'demo'});
+  });
+
+  querySelector("#do-report").onClick.listen((m){
+    w.postMessage({'Typ': 'get', 'Param': 'report'});
+  });
+
 }

部分文件因文件數量過多而無法顯示