diff --git a/zxing.appspot.com/app.yaml b/zxing.appspot.com/app.yaml
new file mode 100644
index 000000000..9f125b59e
--- /dev/null
+++ b/zxing.appspot.com/app.yaml
@@ -0,0 +1,16 @@
+application: zxing
+version: 1
+runtime: python
+api_version: 1
+
+handlers:
+- url: /generator
+ script: redirect.py
+
+- url: (.*)/
+ static_files: static\1/index.html
+ upload: static/index.html
+
+- url: /
+ static_dir: static
+
diff --git a/zxing.appspot.com/generator/.classpath b/zxing.appspot.com/generator/.classpath
new file mode 100644
index 000000000..018b040e0
--- /dev/null
+++ b/zxing.appspot.com/generator/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/zxing.appspot.com/generator/.project b/zxing.appspot.com/generator/.project
new file mode 100644
index 000000000..9eada68e6
--- /dev/null
+++ b/zxing.appspot.com/generator/.project
@@ -0,0 +1,27 @@
+
+
+ BarcodeGenerator
+ BarcodeGenerator project
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
+
+ gwt-user.jar
+ 1
+ GWT_HOME/gwt-user.jar
+
+
+ gwt.jar
+ 1
+ GWT_JAR
+
+
+
diff --git a/zxing.appspot.com/generator/Generator.launch b/zxing.appspot.com/generator/Generator.launch
new file mode 100644
index 000000000..8e6da460c
--- /dev/null
+++ b/zxing.appspot.com/generator/Generator.launch
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/zxing.appspot.com/generator/README b/zxing.appspot.com/generator/README
new file mode 100644
index 000000000..cb9891bcb
--- /dev/null
+++ b/zxing.appspot.com/generator/README
@@ -0,0 +1,11 @@
+
+
+To develop with Eclipse.
+
+Before importing the project, you need to define some variables.
+- In Eclipse Preferences, go to General->Workspace->Linked Resources.
+- Click New and add the 2 following variables:
+ - GWT_HOME that point to the directory containing gwt-user.jar
+ - GWT_JAR that point to the gwt-dev-XXX.jar file directly. (XXX will be linux, mac, etc)
+- then you can import the project by File->Import...->Existing project into workspace.
+
diff --git a/zxing.appspot.com/generator/libs/gwt-incubator.jar b/zxing.appspot.com/generator/libs/gwt-incubator.jar
new file mode 100644
index 000000000..4a9d73d71
Binary files /dev/null and b/zxing.appspot.com/generator/libs/gwt-incubator.jar differ
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/Generator.gwt.xml b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/Generator.gwt.xml
new file mode 100644
index 000000000..6508ee889
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/Generator.gwt.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/CalendarEventGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/CalendarEventGenerator.java
new file mode 100644
index 000000000..ddd3b3b97
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/CalendarEventGenerator.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.CheckBox;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.widgetideas.client.event.ChangeEvent;
+import com.google.gwt.widgetideas.client.event.ChangeHandler;
+import com.google.gwt.widgetideas.datepicker.client.DateBox;
+import com.google.gwt.widgetideas.datepicker.client.TimePicker;
+import com.google.zxing.web.generator.client.TimeZoneList.TimeZoneInfo;
+
+import java.util.Date;
+
+/**
+ * A Generator for calendar events. Output is in VCal format.
+ *
+ * @author Yohann Coppel
+ */
+public class CalendarEventGenerator implements GeneratorSource {
+ public final static String[] FULL_DAY_ONLY_IDS = { "fullDayOnlyInfo1",
+ "fullDayOnlyInfo2", "fullDayOnlyInfo3", "fullDayOnlyInfo4" };
+ private final static long ONE_HOUR = 60 * 60 * 1000;
+
+ Grid table = null;
+ TextBox eventName = new TextBox();
+ CheckBox fullDay = new CheckBox();
+ DateBox datePicker1 = new DateBox();
+ DateBox datePicker2 = new DateBox();
+ TimePicker timePicker1 = new TimePicker(new Date(), DateTimeFormat
+ .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
+ .getFormat("mm"), null);
+ TimePicker timePicker2 = new TimePicker(new Date(), DateTimeFormat
+ .getFormat("a"), DateTimeFormat.getFormat("hh"), DateTimeFormat
+ .getFormat("mm"), null);
+ CheckBox summerTime = new CheckBox();
+ ListBox timeZones = new ListBox();
+ Date timePicker1PreviousDate = null;
+
+ public CalendarEventGenerator(final ChangeListener listener) {
+ eventName.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ eventName.addChangeListener(listener);
+ datePicker1.setAnimationEnabled(true);
+ datePicker2.setAnimationEnabled(true);
+ timePicker2
+ .setDateTime(addMilliseconds(timePicker1.getDateTime(), ONE_HOUR));
+ timePicker1PreviousDate = timePicker1.getDateTime();
+
+ buildTimeZoneList();
+ timeZones.setSelectedIndex(25);
+ timePicker1.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ Date time = timePicker1PreviousDate;
+ Date time1 = timePicker1.getDateTime();
+ Date time2 = timePicker2.getDateTime();
+ if (time2.after(time)) {
+ // keep the same time difference if the interval is valid.
+ long diff = timeDifference(time, time2);
+ timePicker2.setDateTime(addMilliseconds(time1, diff));
+ } else {
+ // otherwise erase the end date and set it to startdate + one hour.
+ timePicker2.setDateTime(addMilliseconds(time, ONE_HOUR));
+ }
+ // no need to call onChange for timePicker1, since it will be called
+ // for timePicker2 when changes are made.
+ // listener.onChange(timePicker1);
+ timePicker1PreviousDate = time1;
+ }
+ });
+ timePicker2.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ listener.onChange(timePicker2);
+ }
+ });
+ }
+
+ private void buildTimeZoneList() {
+ for (TimeZoneInfo info : TimeZoneList.TIMEZONES) {
+ timeZones.addItem(info.GMTRelative + " " + info.abreviation, ""
+ + info.gmtDiff);
+ }
+ }
+
+ public String getName() {
+ return "Calendar event";
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ return table;
+ }
+ datePicker1.getDatePicker().setSelectedDate(new Date());
+ datePicker2.getDatePicker().setSelectedDate(new Date());
+ table = new Grid(8, 2);
+
+ table.setText(0, 0, "All day event");
+ table.setWidget(0, 1, fullDay);
+
+ table.setText(1, 0, "Event title");
+ table.setWidget(1, 1, eventName);
+
+ table.setText(2, 0, "Start date");
+ table.setWidget(2, 1, datePicker1);
+
+ table.setText(3, 0, "Time");
+ table.setWidget(3, 1, timePicker1);
+
+ table.setText(4, 0, "End date");
+ table.setWidget(4, 1, datePicker2);
+
+ table.setText(5, 0, "Time");
+ table.setWidget(5, 1, timePicker2);
+
+ table.setText(6, 0, "Time zone");
+ table.setWidget(6, 1, timeZones);
+
+ table.setText(7, 0, "Daylight savings");
+ table.setWidget(7, 1, summerTime);
+
+ table.getRowFormatter().getElement(3).setId(FULL_DAY_ONLY_IDS[0]);
+ table.getRowFormatter().getElement(5).setId(FULL_DAY_ONLY_IDS[1]);
+ table.getRowFormatter().getElement(6).setId(FULL_DAY_ONLY_IDS[2]);
+ table.getRowFormatter().getElement(7).setId(FULL_DAY_ONLY_IDS[3]);
+
+ fullDay.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ CheckBox cb = (CheckBox) sender;
+ setFullDay(cb.isChecked());
+ }
+ });
+
+ return table;
+ }
+
+ private void setFullDay(boolean fullDay) {
+ for (String s : FULL_DAY_ONLY_IDS) {
+ Element element = DOM.getElementById(s);
+ String style = "";
+ if (fullDay) {
+ style = "none";
+ }
+ DOM.setStyleAttribute(element, "display", style);
+ }
+ }
+
+ public String getText() throws GeneratorException {
+ String eventName = getEventNameField();
+ String dates = getDateTimeFields();
+
+ String output = "";
+ output += "BEGIN:VEVENT\n";
+ output += eventName;
+ output += dates;
+ output += "END:VEVENT\n";
+ return output;
+ }
+
+ private String getEventNameField() throws GeneratorException {
+ String inputName = eventName.getText();
+ if (inputName.length() < 1) {
+ throw new GeneratorException("Event name must be at least 1 character.");
+ }
+ if (inputName.contains("\n")) {
+ throw new GeneratorException(
+ "Event name should not contain \\n characters.");
+ }
+ return "SUMMARY:" + inputName + "\n";
+ }
+
+ private String getDateTimeFields() throws GeneratorException {
+ if (fullDay.isChecked()) {
+ return getFullDayDateFields();
+ }
+ return getDateTimeValues();
+ }
+
+ private String getFullDayDateFields() throws GeneratorException {
+ Date date1 = datePicker1.getDatePicker().getSelectedDate();
+ Date date2 = datePicker2.getDatePicker().getSelectedDate();
+ if (null == date1 || null == date2) {
+ throw new GeneratorException("Start and end dates must be set.");
+ }
+ if (date1.after(date2)) {
+ throw new GeneratorException("Ending date is after starting date.");
+ }
+ DateTimeFormat isoFormatter = DateTimeFormat.getFormat("yyyyMMdd");
+ String output = "";
+ output += "DTSTART:" + isoFormatter.format(date1) + "\n";
+ output += "DTEND:" + isoFormatter.format(date2) + "\n";
+ return output;
+ }
+
+ private String getDateTimeValues() throws GeneratorException {
+ Date date1 = datePicker1.getDatePicker().getSelectedDate();
+ Date date2 = datePicker2.getDatePicker().getSelectedDate();
+ Date time1 = timePicker1.getDateTime();
+ Date time2 = timePicker2.getDateTime();
+ if (null == date1 || null == date2 || null == time1 || null == time2) {
+ throw new GeneratorException("Start and end dates/times must be set.");
+ }
+ String timezoneDelta = timeZones.getValue(timeZones.getSelectedIndex());
+ long diffTimeZone = Long.parseLong(timezoneDelta);
+ if (summerTime.isChecked()) {
+ diffTimeZone += ONE_HOUR;
+ }
+ Date dateTime1 = addMilliseconds(mergeDateAndTime(date1, time1),
+ -diffTimeZone);
+ Date dateTime2 = addMilliseconds(mergeDateAndTime(date2, time2),
+ -diffTimeZone);
+ if (dateTime1.after(dateTime2)) {
+ throw new GeneratorException("Ending date is after starting date.");
+ }
+ DateTimeFormat isoFormatter = DateTimeFormat
+ .getFormat("yyyyMMdd'T'kkmmss'Z'");
+ String output = "";
+ output += "DTSTART:" + isoFormatter.format(dateTime1) + "\n";
+ output += "DTEND:" + isoFormatter.format(dateTime2) + "\n";
+ return output;
+ }
+
+ private Date mergeDateAndTime(Date date, Date time) {
+ // Is that the only ugly way to do with GWT ? given that we don't
+ // have java.util.Calendar for instance
+ DateTimeFormat extractDate = DateTimeFormat.getFormat("yyyyMMdd");
+ DateTimeFormat extractTime = DateTimeFormat.getFormat("kkmm");
+ DateTimeFormat merger = DateTimeFormat.getFormat("yyyyMMddkkmmss");
+ String d = extractDate.format(date);
+ String t = extractTime.format(time) + "00";
+ return merger.parse(d + t);
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == eventName)
+ getEventNameField();
+ if (widget == datePicker1 || widget == timePicker1 || widget == datePicker2
+ || widget == timePicker2)
+ getDateTimeFields();
+ }
+
+ private static Date addMilliseconds(Date time1, long milliseconds) {
+ return new Date(time1.getTime() + milliseconds);
+ }
+
+ private static long timeDifference(Date time1, Date time2) {
+ return time2.getTime() - time1.getTime();
+ }
+
+ public void setFocus() {
+ eventName.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/ContactInfoGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/ContactInfoGenerator.java
new file mode 100644
index 000000000..fd4029bdb
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/ContactInfoGenerator.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A Generator for contact informations, output is in MeCard format.
+ *
+ * @author Yohann Coppel
+ */
+public class ContactInfoGenerator implements GeneratorSource {
+ Grid table = null;
+ TextBox name = new TextBox();
+ TextBox company = new TextBox();
+ TextBox tel = new TextBox();
+ TextBox url = new TextBox();
+ TextBox email = new TextBox();
+ TextBox address = new TextBox();
+ TextBox memo = new TextBox();
+ TextBox[] widgets = {name, company, tel, url, email, address, memo};
+
+ public ContactInfoGenerator(ChangeListener changeListener) {
+ for (TextBox w: widgets) {
+ w.addChangeListener(changeListener);
+ }
+ }
+
+ public String getName() {
+ return "Contact information";
+ }
+
+ public String getText() throws GeneratorException {
+ String name = getNameField();
+ String company = getCompanyField();
+ String tel = getTelField();
+ String url = getUrlField();
+ String email = getEmailField();
+ String address = getAddressField();
+ String memo = getMemoField();
+
+ // Build the output with obtained data.
+ // note that some informations may just be "" if they were not specified.
+ //return getVCard(name, company, tel, url, email, address, memo);
+ return getMeCard(name, company, tel, url, email, address, memo);
+ }
+
+ private String getMeCard(String name, String company, String tel, String url,
+ String email, String address, String memo) {
+ String output = "MECARD:";
+ output += "N:" + name + ";";
+ output += company.length() > 0 ? "ORG:" + company + ";" : "";
+ output += tel.length() > 0 ? "TEL:" + tel + ";" : "";
+ output += url.length() > 0 ? "URL:" + url + ";" : "";
+ output += email.length() > 0 ? "EMAIL:" + email + ";" : "";
+ output += address.length() > 0 ? "ADR:" + address + ";" : "";
+ output += memo.length() > 0 ? "NOTE:" + memo + ";" : "";
+ output += ";";
+ return output;
+ }
+
+ /*// VCARD GENERATION. Keep this in case we want to go back to vcard format
+ // or have the option.
+ private String getVCard(String name, String company, String tel, String url,
+ String email, String address, String memo) {
+ String output = "BEGIN:VCARD\n";
+ output += "N:" + name + "\n";
+ output += company.length() > 0 ? "ORG:" + company + "\n" : "";
+ output += tel.length() > 0 ? "TEL:" + tel + "\n" : "";
+ output += url.length() > 0 ? "URL:" + url + "\n" : "";
+ output += email.length() > 0 ? "EMAIL:" + email + "\n" : "";
+ output += address.length() > 0 ? "ADR:" + address + "\n" : "";
+ output += memo.length() > 0 ? "NOTE:" + memo + "\n" : "";
+ output += "END:VCARD";
+
+ return output;
+ }
+ */
+
+ private String getNameField() throws GeneratorException {
+ String inputName = name.getText();
+ if (inputName.length() < 1) {
+ throw new GeneratorException("Name must be at least 1 character.");
+ }
+ if (inputName.contains("\n")) {
+ throw new GeneratorException("Name should not contanains \\n characters.");
+ }
+ if (inputName.contains(";")) {
+ throw new GeneratorException("Name must not contains ; characters");
+ }
+ return inputName;
+ }
+
+ private String getCompanyField() throws GeneratorException {
+ String input = company.getText();
+ if (input.length() < 1) {
+ return "";
+ }
+ if (input.contains("\n")) {
+ throw new GeneratorException("Company should not contanains \\n characters.");
+ }
+ if (input.contains(";")) {
+ throw new GeneratorException("Company must not contains ; characters");
+ }
+ // the input contains some informations.
+ return input;
+ }
+
+ private String getTelField() throws GeneratorException {
+ String input = Validators.filterNumber(tel.getText());
+ if (input.length() < 1) {
+ return "";
+ }
+ Validators.validateNumber(input);
+ if (input.contains(";")) {
+ throw new GeneratorException("Tel must not contains ; characters");
+ }
+ return input;
+ }
+
+ private String getUrlField() throws GeneratorException {
+ String input = url.getText();
+ if (input.length() < 1) {
+ return "";
+ }
+ Validators.validateUrl(input);
+ if (input.contains(";")) {
+ throw new GeneratorException("URL must not contains ; characters");
+ }
+ return input;
+ }
+
+ private String getEmailField() throws GeneratorException {
+ String input = email.getText();
+ if (input.length() < 1) {
+ return "";
+ }
+ Validators.validateEmail(input);
+ if (input.contains(";")) {
+ throw new GeneratorException("Email must not contains ; characters");
+ }
+ return input;
+ }
+
+ private String getAddressField() throws GeneratorException {
+ String input = address.getText();
+ if (input.length() < 1) {
+ return "";
+ }
+ if (input.contains("\n")) {
+ throw new GeneratorException("Address must not contain \\n characters.");
+ }
+ if (input.contains(";")) {
+ throw new GeneratorException("Address must not contains ; characters");
+ }
+ return input;
+ }
+
+ private String getMemoField() throws GeneratorException {
+ String input = memo.getText();
+ if (input.length() < 1) {
+ return "";
+ }
+ if (input.contains("\n")) {
+ throw new GeneratorException("Memo must not contain \\n characters.");
+ }
+ if (input.contains(";")) {
+ throw new GeneratorException("Memo must not contains ; characters");
+ }
+ return input;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ // early termination if the table has already been constructed
+ return table;
+ }
+ table = new Grid(7, 2);
+
+ table.setText(0, 0, "Name");
+ table.setWidget(0, 1, name);
+ table.setText(1, 0, "Company");
+ table.setWidget(1, 1, company);
+ table.setText(2, 0, "Phone number");
+ table.setWidget(2, 1, tel);
+ table.setText(3, 0, "Email");
+ table.setWidget(3, 1, email);
+ table.setText(4, 0, "Address");
+ table.setWidget(4, 1, address);
+ table.setText(5, 0, "Website");
+ table.setWidget(5, 1, url);
+ table.setText(6, 0, "Memo");
+ table.setWidget(6, 1, memo);
+
+ name.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ return table;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == name) getNameField();
+ if (widget == company) getCompanyField();
+ if (widget == tel) getTelField();
+ if (widget == email) getEmailField();
+ if (widget == address) getAddressField();
+ if (widget == url) getUrlField();
+ if (widget == memo) getMemoField();
+ }
+
+ public void setFocus() {
+ name.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/EmailGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/EmailGenerator.java
new file mode 100644
index 000000000..cf160d3dd
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/EmailGenerator.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Generator for email address.
+ *
+ * @author Yohann Coppel
+ */
+public class EmailGenerator implements GeneratorSource {
+ Grid table = null;
+ TextBox email = new TextBox();
+
+ public EmailGenerator(ChangeListener listener) {
+ email.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ email.addChangeListener(listener);
+ }
+
+ public String getName() {
+ return "Email address";
+ }
+
+ public String getText() throws GeneratorException {
+ String email = getEmailField();
+ return "mailto:"+email;
+ }
+
+ private String getEmailField() throws GeneratorException {
+ String input = email.getText();
+ if (input.length() < 1) {
+ throw new GeneratorException("Email must be present.");
+ }
+ Validators.validateEmail(input);
+ return input;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ return table;
+ }
+ table = new Grid(1, 2);
+
+ table.setText(0, 0, "Address");
+ table.setWidget(0, 1, email);
+
+ return table;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == email) getEmailField();
+ }
+
+ public void setFocus() {
+ email.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Generator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Generator.java
new file mode 100644
index 000000000..94b066263
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Generator.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.ListBox;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Entry point classes define onModuleLoad()
. Setup the page, and loads
+ * all different generators.
+ *
+ * @author Yohann Coppel
+ */
+public class Generator implements EntryPoint {
+// public static final StringConstants S = GWT.create(StringConstants.class);
+ private final List generators = new ArrayList();
+ ListBox genList = new ListBox();
+ ListBox sizeList = new ListBox();
+ Image result = new Image("");//http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=http://google.com");
+ Grid topPanel = new Grid(5, 1);
+ GeneratorSource selectedGenerator = null;
+ private Button generateButton;
+ VerticalPanel rightPanel = new VerticalPanel();
+ TextBox urlResult = new TextBox();
+ HTML downloadText = new HTML("Download or embed with this URL:");
+ //Element errorElement = null;
+
+ public void onModuleLoad() {
+ loadGenerators();
+
+ HorizontalPanel mainPanel = new HorizontalPanel();
+ setupLeftPanel();
+ topPanel.getElement().setId("leftpanel");
+ Widget leftPanel = topPanel;
+
+ mainPanel.add(leftPanel);
+
+ SimplePanel div = new SimplePanel();
+ SimplePanel div2 = new SimplePanel();
+ div2.add(result);
+ div2.getElement().setId("innerresult");
+ div.add(div2);
+ div.getElement().setId("imageresult");
+
+ urlResult.getElement().setId("urlresult");
+ downloadText.getElement().setId("downloadText");
+ rightPanel.add(div);
+ rightPanel.add(downloadText);
+ rightPanel.add(urlResult);
+ mainPanel.add(rightPanel);
+ mainPanel.getElement().setId("mainpanel");
+ RootPanel.get("ui").add(mainPanel);
+ //RootPanel.get().add(output);
+ //output.setHeight("200px");
+ //output.setWidth("500px");
+ setWidget(1);
+ invalidateBarcode();
+ }
+
+ private void setWidget(int id) {
+ if (id >= 0 && id < generators.size()) {
+ topPanel.setWidget(1, 0, generators.get(id).getWidget());
+ genList.setSelectedIndex(id);
+ selectedGenerator = generators.get(id);
+ eraseErrorMessage();
+ invalidateBarcode();
+ genList.setFocus(false);
+ selectedGenerator.setFocus();
+ }
+ }
+
+ private void loadGenerators() {
+ generators.add(new CalendarEventGenerator(changeListener));
+ generators.add(new ContactInfoGenerator(changeListener));
+ generators.add(new EmailGenerator(changeListener));
+ generators.add(new GeoLocationGenerator(changeListener));
+ generators.add(new PhoneNumberGenerator(changeListener));
+ generators.add(new SmsAddressGenerator(changeListener));
+ generators.add(new TextGenerator(changeListener));
+ generators.add(new UrlGenerator(changeListener));
+ }
+
+ public void setupLeftPanel() {
+ topPanel.setHTML(2, 0,
+ "");
+
+ // fills up the list of generators
+ for(GeneratorSource generator: generators) {
+ genList.addItem(generator.getName());
+ setGridStyle(generator.getWidget());
+ }
+
+ sizeList.addItem("S", "120");
+ sizeList.addItem("M", "230");
+ sizeList.addItem("L", "350");
+ sizeList.setSelectedIndex(2);
+
+ // updates the second row of the table with the content of the selected
+ // generator
+ genList.addChangeListener(new ChangeListener() {
+ public void onChange(Widget sender) {
+ int i = genList.getSelectedIndex();
+ setWidget(i);
+ }
+ });
+
+ // grid for the generator picker
+ Grid selectionTable = new Grid(1, 2);
+ selectionTable.setText(0, 0, "Contents");
+ selectionTable.setWidget(0, 1, genList);
+ setGridStyle(selectionTable);
+
+ topPanel.setWidget(0, 0, selectionTable);
+
+ // grid for the generate button
+ Grid generateGrid = new Grid(1, 2);
+ setGridStyle(generateGrid);
+
+ generateButton = new Button("Generate →");
+ generateButton.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ generate();
+ }
+ });
+ generateGrid.setWidget(0,1,generateButton);
+ topPanel.setWidget(4, 0, generateGrid);
+
+ Grid sizeTable = new Grid(1, 2);
+ sizeTable.setText(0, 0, "Barcode size");
+ sizeTable.setWidget(0, 1, sizeList);
+ setGridStyle(sizeTable);
+ topPanel.setWidget(3, 0, sizeTable);
+ }
+
+ protected void setGridStyle(Grid grid) {
+ grid.getColumnFormatter().addStyleName(0, "firstColumn");
+ grid.getColumnFormatter().addStyleName(1, "secondColumn");
+ CellFormatter cellFormatter = grid.getCellFormatter();
+ for(int i = 0; i < grid.getRowCount(); ++i) {
+ cellFormatter.addStyleName(i, 0, "firstColumn");
+ cellFormatter.addStyleName(i, 1, "secondColumn");
+ }
+ }
+
+ protected String getUrl(int sizeX, int sizeY, String content) {
+ String result = "http://chart.apis.google.com/chart?cht=qr&chs=";
+ result += sizeX;
+ result += "x";
+ result += sizeY;
+ result += "&chl=";
+ result += URL.encode(content);
+ return result;
+ }
+
+ private void generate() {
+ try {
+ String text = selectedGenerator.getText();
+ eraseErrorMessage();
+ int size = Integer.parseInt(sizeList
+ .getValue(sizeList.getSelectedIndex()));
+ String url = getUrl(size, size, text);
+ result.setUrl(url);
+ result.setVisible(true);
+ urlResult.setText(url);
+ urlResult.setVisible(true);
+ Element linkElement = DOM.getElementById("downloadlink");
+ linkElement.setAttribute("href", url);
+ downloadText.setVisible(true);
+ } catch (GeneratorException ex) {
+ invalidateBarcode();
+ String error = ex.getMessage();
+ showErrorMessage(error);
+ }
+ }
+
+ public void invalidateBarcode() {
+ result.setVisible(false);
+ urlResult.setText("");
+ urlResult.setVisible(false);
+ Element linkElement = DOM.getElementById("downloadlink");
+ linkElement.setAttribute("href", "");
+ downloadText.setVisible(false);
+ }
+
+ public void showErrorMessage(String error) {
+ Element errorElement = DOM.getElementById("errorMessageID");
+ errorElement.setInnerHTML(error);
+ }
+
+ public void eraseErrorMessage() {
+ Element errorElement = DOM.getElementById("errorMessageID");
+ errorElement.setInnerHTML(" ");
+ }
+
+ public ChangeListener changeListener = new ChangeListener() {
+ public void onChange(Widget sender) {
+ try {
+ selectedGenerator.validate(sender);
+ eraseErrorMessage();
+ } catch (GeneratorException ex) {
+ String error = ex.getMessage();
+ showErrorMessage(error);
+ invalidateBarcode();
+ }
+ }
+ };
+
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorException.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorException.java
new file mode 100644
index 000000000..d6f127f80
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+/**
+ * An exception that can be raised by a generator when the input data contains
+ * errors. The getMessage() method should be used to get the error message to
+ * display to the user.
+ *
+ * @author Yohann Coppel
+ */
+public class GeneratorException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public GeneratorException(String message) {
+ super(message);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorSource.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorSource.java
new file mode 100644
index 000000000..00a277d11
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeneratorSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Base interface for any generator.
+ *
+ * @author Yohann Coppel
+ */
+public interface GeneratorSource {
+ /**
+ * @return a GWT Grid object, containing the GUI.
+ */
+ public Grid getWidget();
+ /**
+ * @return the name of the generator to be used in the GUI.
+ */
+ public String getName();
+ /**
+ * @return the text to be encoded into the QR code.
+ * @throws GeneratorException if the input data contains errors.
+ */
+ public String getText() throws GeneratorException;
+ /**
+ * @param widget the widget that was last modified, and that we want to
+ * validate the content.
+ * @throws GeneratorException if the widget contains errors.
+ */
+ public void validate(Widget widget) throws GeneratorException;
+ /**
+ * Called when the generator is selected in the list. Using this method,
+ * the generator should set the focus to the first widget it defines.
+ */
+ public void setFocus();
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeoLocationGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeoLocationGenerator.java
new file mode 100644
index 000000000..6d9b97f3e
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/GeoLocationGenerator.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.http.client.URL;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.ClickListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A generator for geo location. It also accepts a google maps links and
+ * extracts the coordinates and query from the URL.
+ *
+ * @author Yohann Coppel
+ */
+public class GeoLocationGenerator implements GeneratorSource {
+ private static final String LON_REGEXP = "[+-]?[0-9]+(.[0-9]+)?";
+ private static final String LAT_REGEXP = "[+-]?[0-9]+(.[0-9]+)?";
+
+ Grid table = null;
+ TextBox latitude = new TextBox();
+ TextBox longitude = new TextBox();
+ TextBox query = new TextBox();
+ TextBox mapsLink = new TextBox();
+
+ public GeoLocationGenerator(ChangeListener listener) {
+ latitude.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ longitude.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ latitude.addChangeListener(listener);
+ longitude.addChangeListener(listener);
+ query.addChangeListener(listener);
+ }
+
+ public String getName() {
+ return "Geo location";
+ }
+
+ public String getText() throws GeneratorException {
+ String que = getQueryField();
+ if (null != que && que.length() > 0) {
+ if (null == getLatitudeField()) {
+ latitude.setText("0");
+ }
+ if (null == getLongitudeField()) {
+ longitude.setText("0");
+ }
+ }
+ String lat = getLatitudeField();
+ String lon = getLongitudeField();
+
+ if (que.length() > 0) {
+ return "geo:"+lat+","+lon+"?q="+que;
+ }
+
+ return "geo:"+lat+","+lon;
+ }
+
+ private String getQueryField() {
+ String que = query.getText();
+ que = que.replace("&", "%26");
+ return que;
+ }
+
+ private String getLongitudeField() throws GeneratorException {
+ String lon = longitude.getText();
+ if (!lon.matches(LON_REGEXP)) {
+ throw new GeneratorException("Longitude is not a correct value.");
+ }
+ double val = Double.parseDouble(lon);
+ if (val < -180 || val > 180) {
+ throw new GeneratorException("Longitude must be in [-180:180]");
+ }
+ return lon;
+ }
+
+ private String getLatitudeField() throws GeneratorException {
+ String lat = latitude.getText();
+ if (!lat.matches(LAT_REGEXP)) {
+ throw new GeneratorException("Latitude is not a correct value.");
+ }
+ double val = Double.parseDouble(lat);
+ if (val < -90 || val > 90) {
+ throw new GeneratorException("Latitude must be in [-90:90]");
+ }
+ return lat;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ return table;
+ }
+ table = new Grid(5, 2);
+
+ table.setText(0, 0, "Latitude");
+ table.setWidget(0, 1, latitude);
+ table.setText(1, 0, "Longitude");
+ table.setWidget(1, 1, longitude);
+ table.setText(2, 0, "Query");
+ table.setWidget(2, 1, query);
+ table.setText(3, 0, "OR");
+ table.setText(3, 1, "enter a Google Maps link and click Fill:");
+ // looks like this:
+ // http://maps.google.com/?ie=UTF8&ll=40.741404,-74.00322&spn=0.001484,0.003101&z=18
+ Button fill = new Button("Fill ↑");
+ fill.addClickListener(new ClickListener() {
+ public void onClick(Widget sender) {
+ fillWithMaps();
+ }
+ });
+ table.setWidget(4, 0, fill);
+ table.setWidget(4, 1, mapsLink);
+
+ return table;
+ }
+
+ protected void fillWithMaps() {
+ String link = mapsLink.getText();
+ if (!link.matches("http://maps.google.com/.*")) {
+ return;
+ }
+ String q = "";
+ if (link.matches(".*&q=[^&]*&.*")) {
+ for (int i = link.indexOf("&q=") + 3;
+ i < link.length() && link.charAt(i) != '&'; ++i) {
+ q += link.charAt(i);
+ }
+ // special cases:
+ q = q.replace("+", " ");
+ q = q.replace("%26", "&");
+ }
+
+ String lat = "";
+ String lon = "";
+ if (link.matches(".*&s?ll=[^&]*&.*")) {
+ boolean beforeComa = true;
+ int start = 0;
+ if (link.indexOf("&sll=") == -1) {
+ start = link.indexOf("&ll=") + 4;
+ } else {
+ start = link.indexOf("&sll=") + 5;
+ }
+ for (int i = start; i < link.length() && link.charAt(i) != '&'; ++i) {
+ if (beforeComa) {
+ if (link.charAt(i) == ',') {
+ beforeComa = false;
+ } else {
+ lat += link.charAt(i);
+ }
+ } else {
+ lon += link.charAt(i);
+ }
+ }
+ }
+
+ query.setText(URL.decode(q));
+ latitude.setText(lat);
+ longitude.setText(lon);
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == latitude) getLatitudeField();
+ if (widget == longitude) getLongitudeField();
+ }
+
+ public void setFocus() {
+ latitude.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/PhoneNumberGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/PhoneNumberGenerator.java
new file mode 100644
index 000000000..9792fd9b7
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/PhoneNumberGenerator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A generator for a phone number.
+ *
+ * @author Yohann Coppel
+ */
+public class PhoneNumberGenerator implements GeneratorSource {
+ Grid table = null;
+ TextBox number = new TextBox();
+
+ public PhoneNumberGenerator(ChangeListener listener) {
+ number.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ number.addChangeListener(listener);
+ }
+
+ public String getName() {
+ return "Phone number";
+ }
+
+ public String getText() throws GeneratorException {
+ String tel = getTelField();
+ return "tel:" + tel;
+ }
+
+ private String getTelField() throws GeneratorException {
+ String input = number.getText();
+ if (input.length() < 1) {
+ throw new GeneratorException("Phone number must be present.");
+ }
+ input = Validators.filterNumber(input);
+ Validators.validateNumber(input);
+ return input;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ return table;
+ }
+ table = new Grid(1, 2);
+
+ table.setText(0, 0, "Phone number");
+ table.setWidget(0, 1, number);
+
+ return table;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == number) getTelField();
+ }
+
+ public void setFocus() {
+ number.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/SmsAddressGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/SmsAddressGenerator.java
new file mode 100644
index 000000000..05fe28b0a
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/SmsAddressGenerator.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextArea;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A generator for a sms address. Gives the option of filling up the content
+ * of the message as well.
+ *
+ * @author Yohann Coppel
+ */
+public class SmsAddressGenerator implements GeneratorSource {
+ Grid table = null;
+ TextBox number = new TextBox();
+ TextArea message = new TextArea();
+
+ public SmsAddressGenerator(ChangeListener listener) {
+ number.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ number.addChangeListener(listener);
+ message.addChangeListener(listener);
+ }
+
+ public String getName() {
+ return "SMS";
+ }
+
+ public String getText() throws GeneratorException {
+ String inputNumber = getTelField();
+ String inputMessage = getMessageField();
+
+ String output = inputNumber;
+ // we add the text only if there actually is something in the field.
+ if (inputMessage.length() > 0) {
+ output += ":" + inputMessage;
+ }
+
+ return "smsto:" + output;
+ }
+
+ private String getTelField() throws GeneratorException {
+ String input = number.getText();
+ if (input.length() < 1) {
+ throw new GeneratorException("Phone number must be present.");
+ }
+ input = Validators.filterNumber(input);
+ Validators.validateNumber(input);
+ return input;
+ }
+
+ private String getMessageField() throws GeneratorException {
+ String inputMessage = message.getText();
+ if (inputMessage.length() > 150) {
+ throw new GeneratorException("Sms message can not be longer than 150 characters.");
+ }
+ return inputMessage;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ return table;
+ }
+ table = new Grid(2, 2);
+
+ table.setText(0, 0, "Phone number");
+ table.setWidget(0, 1, number);
+
+ table.setText(1, 0, "Message");
+ table.setWidget(1, 1, message);
+
+ return table;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == number) getTelField();
+ if (widget == message) getMessageField();
+ }
+
+ public void setFocus() {
+ number.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StringConstants.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StringConstants.java
new file mode 100644
index 000000000..e690b9fe4
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StringConstants.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.i18n.client.Messages;
+
+// Not used yet. A first atempt to localization.
+public interface StringConstants extends Messages {
+ public String codeType();
+ public String generateButton();
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StylesDefs.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StylesDefs.java
new file mode 100644
index 000000000..5be82ae73
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/StylesDefs.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+/**
+ * Contains class names of css styles to be used in HTML.
+ *
+ * @author Yohann Coppel
+ */
+public class StylesDefs {
+ public static final String INPUT_FIELD_REQUIRED = "required";
+ public static final String ERROR_MESSAGE = "errorMessage";
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TextGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TextGenerator.java
new file mode 100644
index 000000000..e5fccf2e4
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TextGenerator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextArea;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A generator for any text content.
+ *
+ * @author Yohann Coppel
+ */
+public class TextGenerator implements GeneratorSource {
+ TextArea text = new TextArea();
+ Grid table = null;
+ String error = "";
+
+ public TextGenerator(ChangeListener listener) {
+ text.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ text.addChangeListener(listener);
+ }
+
+ public String getName() {
+ return "Text";
+ }
+
+ public String getText() throws GeneratorException {
+ String message = getTextField();
+ return message;
+ }
+
+ public String getTextField() throws GeneratorException {
+ String input = text.getText();
+ if (input.length() == 0) {
+ throw new GeneratorException("Text should be at least 1 character.");
+ }
+ return input;
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ // early termination if the table has already been constructed
+ return table;
+ }
+
+ table = new Grid(1, 2);
+ table.getColumnFormatter().addStyleName(0, "firstColumn");
+
+ table.setText(0, 0, "Text content");
+ table.setWidget(0, 1, text);
+
+ return table;
+ }
+
+ public String getErrorMessage() {
+ return error;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == text) getTextField();
+ }
+
+ public void setFocus() {
+ text.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TimeZoneList.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TimeZoneList.java
new file mode 100644
index 000000000..40920223b
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/TimeZoneList.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+/**
+ * A class containing a list of timezones, with their full names, and time
+ * offset.
+ *
+ * @author Yohann Coppel
+ */
+public class TimeZoneList {
+ public static class TimeZoneInfo {
+ String abreviation;
+ String longName;
+ String GMTRelative;
+ long gmtDiff;
+ public TimeZoneInfo(String abreviation, String longName, String relative, long gmtDiff) {
+ super();
+ GMTRelative = relative;
+ this.abreviation = abreviation;
+ this.gmtDiff = gmtDiff;
+ this.longName = longName;
+ }
+ }
+
+ private final static long ONE_HOUR = 60*60*1000;
+ private final static long THIRTY_MIN = 30*60*1000;
+
+ public static final TimeZoneInfo[] TIMEZONES = {
+ new TimeZoneInfo("GMT", "Greenwich Mean Time", "GMT", 0 * ONE_HOUR + 0 * THIRTY_MIN), // 0
+ new TimeZoneInfo("UTC", "Universal Coordinated Time", "GMT", 1 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("ECT", "European Central Time", "GMT+1:00", 1 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("EET", "Eastern European Time", "GMT+2:00", 2 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("ART", "(Arabic) Egypt Standard Time", "GMT+2:00", 2 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("EAT", "Eastern African Time", "GMT+3:00", 3 * ONE_HOUR + 0 * THIRTY_MIN), // 5
+ new TimeZoneInfo("MET", "Middle East Time", "GMT+3:30", 3 * ONE_HOUR + 1 * THIRTY_MIN),
+ new TimeZoneInfo("NET", "Near East Time", "GMT+4:00", 4 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("PLT", "Pakistan Lahore Time", "GMT+5:00", 5 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("IST", "India Standard Time", "GMT+5:30", 5 * ONE_HOUR + 1 * THIRTY_MIN),
+ new TimeZoneInfo("BST", "Bangladesh Standard Time", "GMT+6:00", 6 * ONE_HOUR + 0 * THIRTY_MIN), // 10
+ new TimeZoneInfo("VST", "Vietnam Standard Time", "GMT+7:00", 7 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("CTT", "China Taiwan Time", "GMT+8:00", 8 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("JST", "Japan Standard Time", "GMT+9:00", 9 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("ACT", "Australia Central Time", "GMT+9:30", 9 * ONE_HOUR + 1 * THIRTY_MIN),
+ new TimeZoneInfo("AET", "Australia Eastern Time", "GMT+10:00", 10 * ONE_HOUR + 0 * THIRTY_MIN), // 15
+ new TimeZoneInfo("SST", "Solomon Standard Time", "GMT+11:00", 11 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("NST", "New Zealand Standard Time", "GMT+12:00", 12 * ONE_HOUR + 0 * THIRTY_MIN),
+ new TimeZoneInfo("MIT", "Midway Islands Time", "GMT-11:00", -11 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("HST", "Hawaii Standard Time", "GMT-10:00", -10 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("AST", "Alaska Standard Time", "GMT-9:00", -9 * ONE_HOUR - 0 * THIRTY_MIN), // 20
+ new TimeZoneInfo("PST", "Pacific Standard Time", "GMT-8:00", -8 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("PNT", "Phoenix Standard Time", "GMT-7:00", -7 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("MST", "Mountain Standard Time", "GMT-7:00", -7 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("CST", "Central Standard Time", "GMT-6:00", -6 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("EST", "Eastern Standard Time", "GMT-5:00", -5 * ONE_HOUR - 0 * THIRTY_MIN), // 25
+ new TimeZoneInfo("IET", "Indiana Eastern Standard Time", "GMT-5:00", -5 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("PRT", "Puerto Rico and US Virgin Islands Time", "GMT-4:00", -4 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("CNT", "Canada Newfoundland Time", "GMT-3:30", -3 * ONE_HOUR - 1 * THIRTY_MIN),
+ new TimeZoneInfo("AGT", "Argentina Standard Time", "GMT-3:00", -3 * ONE_HOUR - 0 * THIRTY_MIN),
+ new TimeZoneInfo("BET", "Brazil Eastern Time", "GMT-3:00", -3 * ONE_HOUR - 0 * THIRTY_MIN), // 30
+ new TimeZoneInfo("CAT", "Central African Time", "GMT-1:00", -1 * ONE_HOUR - 0 * THIRTY_MIN),
+ };
+
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/UrlGenerator.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/UrlGenerator.java
new file mode 100644
index 000000000..bb5d23c98
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/UrlGenerator.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+import com.google.gwt.user.client.ui.ChangeListener;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.TextBox;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * A generator for URL addresses.
+ *
+ * @author Yohann Coppel
+ */
+public class UrlGenerator implements GeneratorSource {
+ Grid table = null;
+ TextBox url = new TextBox();
+
+ public UrlGenerator(ChangeListener listener) {
+ url.addStyleName(StylesDefs.INPUT_FIELD_REQUIRED);
+ url.addChangeListener(listener);
+ }
+
+ public Grid getWidget() {
+ if (table != null) {
+ // early termination if the table has already been constructed
+ return table;
+ }
+
+ table = new Grid(1, 2);
+ table.getColumnFormatter().addStyleName(0, "firstColumn");
+
+ url.setText("http://");
+
+ table.setText(0, 0, "URL");
+ table.setWidget(0, 1, url);
+
+ return table;
+ }
+
+ public String getName() {
+ return "URL";
+ }
+
+ public String getText() throws GeneratorException {
+ String input = getUrlField();
+ return input;
+ }
+
+ private String getUrlField() throws GeneratorException {
+ String input = url.getText();
+ Validators.validateUrl(input);
+ return input;
+ }
+
+ public void validate(Widget widget) throws GeneratorException {
+ if (widget == url) getUrlField();
+ }
+
+ public void setFocus() {
+ url.setFocus(true);
+ }
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Validators.java b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Validators.java
new file mode 100644
index 000000000..51646bfb9
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/client/Validators.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.web.generator.client;
+
+/**
+ * Helpers methods to check for phone numbers, email addresses, and URL. Other
+ * general purpose check methods should go here as well.
+ *
+ * @author Yohann Coppel
+ */
+public final class Validators {
+ public static String filterNumber(String number) {
+ return number.replaceAll("[ +\\.,\\-\\(\\)]", "");
+ }
+
+ public static void validateNumber(String number) throws GeneratorException {
+ if (!number.matches("[0-9]+")) {
+ throw new GeneratorException("Phone number must be digits only.");
+ }
+ }
+
+ public static void validateUrl(String url) throws GeneratorException {
+ //FIXME: url specification is a bit more complex than just that.
+ if (!((url.startsWith("http://") && url.length() > 7)
+ || (url.startsWith("https://") && url.length() > 8))) {
+ throw new GeneratorException("URL: http:// or https://," +
+ "plus at least 1 character.");
+ }
+ }
+
+ public static void validateEmail(String email) throws GeneratorException {
+ //FIXME: we can have a better check for email here.
+ if (!email.matches("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}$")) {
+ throw new GeneratorException("Email is not valid.");
+ }
+ }
+
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/DatePickerDemo.css b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/DatePickerDemo.css
new file mode 100644
index 000000000..ec89bf3b4
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/DatePickerDemo.css
@@ -0,0 +1,180 @@
+/**
+Date picker styles, most of these will eventually move into a default resource bundle.
+*/
+.gwt-DatePicker {
+ width: 150px;
+ border: 1px solid #f1f1f1;
+ border-bottom: 2px solid #f1f1f1;
+ color: 6b6b6b;
+ cursor: default;
+
+}
+
+.gwt-DatePicker td {
+ font-family: Arial;
+ font-size: 65%;
+ padding: 2px;
+ text-align: center;
+ padding: 2px;
+ outline: none;
+ -moz-outline: none;
+ cursor: default;
+}
+
+.gwt-DatePicker td,.month-selector td :focus {
+ outline: none
+}
+
+.gwt-DateBox input {
+ width: 8em;
+}
+
+/**
+Calendar styles
+*/
+.calendar-view {
+ background-color: white;
+ width: 100%;
+}
+
+.calendar-view .filler-cell {
+ color: #bfbfbf;
+ padding: 1px;
+ cursor: hand;
+}
+
+.calendar-view .date-cell {
+ padding: 1px;
+ cursor:hand;
+}
+
+.calendar-view .selected-cell {
+ background: #aaccee;
+}
+
+.calendar-view .day-title {
+ background: #dbe6de;
+}
+
+.gwt-DatePicker .calendar-view .highlighted-cell {
+ border: 1px solid #eeeeee;
+ padding: 0px;
+}
+
+.calendar-view .today-cell {
+ border: 1px solid black;
+ padding: 0px;
+}
+
+.gwt-DatePicker .calendar-view .weekend {
+ background: #f1f1f1;
+}
+
+.calendar-view .disabled-cell {
+ color: graytext;
+}
+
+/*
+Month selector styles
+*/
+.month-selector {
+ background: #dbe6de;
+ width: 100%;
+ text-align: center;
+}
+
+.month-selector td {
+ font-weight: bold;
+ font-size: 70%;
+}
+
+.month-selector .month-label {
+ text-align: center;
+ background: #dbe6de;
+}
+
+.month-selector .month-backward {
+ padding-right: 2em;
+}
+
+.month-selector .month-forward {
+ padding-left: 2em;
+}
+
+/****************************
+helper styles
+******************************/
+.log-panel {
+ position: relative;
+ float: right;
+ border: 1px solid black;
+ margin-left: 30px;
+}
+
+.gwt-TabPanel {
+ margin-top: 4px;
+}
+
+.gwt-TabPanelBottom {
+ background-color: #E8EEF7;
+}
+
+.gwt-TabBar {
+ padding-top: 2px;
+ border-bottom: 4px solid #87B3FF;
+ background-color: #fff;
+}
+
+.gwt-TabBar .gwt-TabBarItem {
+ padding: 4px;
+ cursor: pointer;
+ cursor: hand;
+ background-color: #e8eef7;
+ border-bottom: 2px solid white;
+ margin-right: 2px;
+}
+
+.gwt-TabBar .gwt-TabBarItem-selected {
+ padding: 4px;
+ font-weight: bold;
+ cursor: default;
+ background-color: #87b3ff;
+ border-bottom: 2px solid #87b3ff;
+ margin-right: 2px;
+}
+
+/* Styles demo*/
+.gwt-DatePicker .red-date {
+ background-color: red;
+}
+
+.gwt-DatePicker .blue-background {
+ background-color: blue;
+}
+
+.gwt-DatePicker .red-text {
+ color: red;
+}
+
+.gwt-DatePicker .green-border {
+ border: 5px solid green;
+}
+
+.gwt-DatePicker .big-text {
+ font-size: x-large;
+}
+
+.gwt-DatePicker .underlined-and-bold-text {
+ font-weight: bold;
+ text-decoration: underline;
+}
+
+.gwt-DatePicker .yellow-background {
+ background-color: yellow;
+}
+
+/* date range styles */
+.filler {
+ width: 30px;
+ text-align: center;
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.css b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.css
new file mode 100644
index 000000000..8d650d465
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.css
@@ -0,0 +1,123 @@
+/** CSS by Wain. */
+body {
+ background-color: #e9eef3;
+ margin: 0;
+ padding: 0;
+ font-family: Arial, sans-serif;
+ font-size: 11px;
+}
+
+#header {
+ background-color: #dde3e9;
+ border-bottom: 1px solid #f2f7fc;
+}
+
+#header h1 {
+ font-size: 15px;
+ font-weight: bold;
+ margin: 0;
+ padding: 12px;
+ border-bottom: 1px solid #c8d0d9;
+}
+
+#header span {
+ font-weight: normal;
+ color: #5e6b75;
+}
+
+#footer {
+ width: 100%;
+ text-align:center;
+ padding-top: 12px;
+}
+
+#footer a {
+ text-decoration:none;
+ color: #5e6b75;
+}
+
+/****************************************/
+#mainpanel {
+ width: 800px;
+ margin: 0 auto;
+ padding: 12px;
+ font-size: 11px;
+}
+
+#imageresult {
+ background-color: #dde3e9;
+ height: 350px;
+ width: 350px;
+ vertical-align: middle;
+ text-align:center;
+ padding: 10px;
+
+ position: relative;
+ display: table;
+ #position: relative;
+}
+
+#innerresult {
+ display: table-cell;
+ vertical-align: middle;
+ width: 100%;
+ #position: absolute;
+ #top: 50%;
+}
+
+#innerresult img {
+ #position: relative;
+ #top: -50%;
+}
+
+#downloadText {
+ font-size: 11px;
+}
+
+#urlresult {
+ width: 350px;
+}
+
+.firstColumn {
+ width: 100px;
+ text-align: right;
+ vertical-align: top;
+ padding: 4px 0 4px 0;
+ color: #5e6b75;
+ font-weight: bold;
+ font-size: 11px;
+}
+
+.secondColumn {
+ width: 270px;
+ font-size: 11px;
+ color: #5e6b75;
+ font-weight: bold;
+}
+
+/*.inputfield,*/
+.gwt-TextBox,
+.gwt-TextArea {
+ border: 1px solid #c8d0d9;
+ padding: 4px;
+ width: 256px;
+ font-family: Arial, sans-serif;
+ font-size: 13px;
+ color: #000;
+ font-weight: normal;
+}
+
+/*.inputfield:focus,*/
+.gwt-TextBox:focus {
+ background-color: #fffbcc;
+}
+
+.required {
+ background: #ffffff url(required-field-bg.png) repeat-y;
+}
+
+.errorMessage {
+ padding-left: 110px;
+ font-size: 11px;
+ color: red;
+}
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.html b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.html
new file mode 100644
index 000000000..5466e6405
--- /dev/null
+++ b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/Generator.html
@@ -0,0 +1,20 @@
+
+
+
+
+ QR Code Generator
+
+
+
+
+
+
+
+
+
+
+
diff --git a/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/required-field-bg.png b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/required-field-bg.png
new file mode 100644
index 000000000..49e272577
Binary files /dev/null and b/zxing.appspot.com/generator/src/com/google/zxing/web/generator/public/required-field-bg.png differ
diff --git a/zxing.appspot.com/redirect.py b/zxing.appspot.com/redirect.py
new file mode 100644
index 000000000..ed1cccb61
--- /dev/null
+++ b/zxing.appspot.com/redirect.py
@@ -0,0 +1,17 @@
+from google.appengine.ext import webapp
+from google.appengine.ext.webapp.util import run_wsgi_app
+
+class BlogHandler(webapp.RequestHandler):
+ def get(self, tail = ''):
+ self.redirect('/generator/'+tail, permanent = True)
+
+application = webapp.WSGIApplication(
+ [
+ (r'^/generator$', BlogHandler)
+ ])
+
+def main():
+ run_wsgi_app(application)
+
+if __name__ == "__main__":
+ main()