diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..86c8a7d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.swp
+*.ipk
+*.*~
+dropbox-auth.js
diff --git a/app/LICENSE-2.0.txt b/app/LICENSE-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/app/LICENSE-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/app/appinfo.json b/app/appinfo.json
new file mode 100644
index 0000000..97e0d55
--- /dev/null
+++ b/app/appinfo.json
@@ -0,0 +1,22 @@
+{
+ "id": "com.monkeystew.todotxtenyo.beta",
+ "version": "0.1.0",
+ "vendor": "Monkeystew",
+ "type": "web",
+ "main": "index.html",
+ "title": "Todo.txt Enyo beta",
+ "icon": "icon.png",
+ "uiRevision": 2,
+ "universalSearch": {
+ "search": {
+ "displayName":"Todo.txt Enyo",
+ "url":"com.monkeystew.todotxtenyo.beta",
+ "launchParam":"search"
+ },
+ "action": {
+ "displayName":"New todo.txt task",
+ "url":"com.monkeystew.todotxtenyo.beta",
+ "launchParam":"addTodoText"
+ }
+ }
+}
diff --git a/app/depends.js b/app/depends.js
new file mode 100644
index 0000000..7cbadb1
--- /dev/null
+++ b/app/depends.js
@@ -0,0 +1,11 @@
+enyo.depends(
+ "source/TodoTxt.js",
+ "source/TodoList.js",
+ "source/TodoEdit.js",
+ "source/TodoPrefs.js",
+ "source/dropbox-auth.js",
+ "source/Dropbox.js",
+ "source/oauth.js",
+ "source/sha1.js",
+ "source/styles.css"
+);
diff --git a/app/framework_config.json b/app/framework_config.json
new file mode 100644
index 0000000..9624b26
--- /dev/null
+++ b/app/framework_config.json
@@ -0,0 +1,3 @@
+{
+ "logLevel": 99
+}
diff --git a/app/icon.png b/app/icon.png
new file mode 100644
index 0000000..abc3357
Binary files /dev/null and b/app/icon.png differ
diff --git a/app/images/menu-icon-new.png b/app/images/menu-icon-new.png
new file mode 100644
index 0000000..46919d9
Binary files /dev/null and b/app/images/menu-icon-new.png differ
diff --git a/app/images/menu-icon-prefs.png b/app/images/menu-icon-prefs.png
new file mode 100644
index 0000000..f703d7e
Binary files /dev/null and b/app/images/menu-icon-prefs.png differ
diff --git a/app/images/menu-icon-sync.png b/app/images/menu-icon-sync.png
new file mode 100644
index 0000000..31c508c
Binary files /dev/null and b/app/images/menu-icon-sync.png differ
diff --git a/app/index.html b/app/index.html
new file mode 100644
index 0000000..bb93b50
--- /dev/null
+++ b/app/index.html
@@ -0,0 +1,25 @@
+
+
+
+ Todo.txt Enyo
+
+
+
+
+
+
+
0.1.0
+
+ - Initial open source and beta release.
+
+
+
+
diff --git a/app/mock/todoTxt_getConn.json b/app/mock/todoTxt_getConn.json
new file mode 100644
index 0000000..b1c3a51
--- /dev/null
+++ b/app/mock/todoTxt_getConn.json
@@ -0,0 +1,4 @@
+{
+"returnValue": true,
+"isInternetConnectionAvailable": false
+}
diff --git a/app/mock/todoTxt_makeDir.json b/app/mock/todoTxt_makeDir.json
new file mode 100644
index 0000000..c44f3a7
--- /dev/null
+++ b/app/mock/todoTxt_makeDir.json
@@ -0,0 +1 @@
+{ "returnValue": true }
diff --git a/app/mock/todoTxt_readFile.json b/app/mock/todoTxt_readFile.json
new file mode 100644
index 0000000..835a97b
--- /dev/null
+++ b/app/mock/todoTxt_readFile.json
@@ -0,0 +1,5 @@
+{ "returnValue": true,
+ "path": "/dummy",
+ "content": "x TEST ENTRY 2\n(C) Alpha TEST ENTRY 1\n(A) Beta TEST ENTRY 3\nTEST ENTRY 4\n(B) Some TEST ENTRY 5\n+TestProject1 @Bug this is my next bug for testing\n+someotherp testing some other one\n"
+
+}
diff --git a/app/mock/todoTxt_writeFile.json b/app/mock/todoTxt_writeFile.json
new file mode 100644
index 0000000..b463932
--- /dev/null
+++ b/app/mock/todoTxt_writeFile.json
@@ -0,0 +1,5 @@
+{
+"returnValue": true,
+"path": "no dummy",
+"bytes": "no bytes either"
+}
diff --git a/app/source/Dropbox.js b/app/source/Dropbox.js
new file mode 100644
index 0000000..48158bd
--- /dev/null
+++ b/app/source/Dropbox.js
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2012 Morgan McMillian
+ *
+ * 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.
+ *
+ */
+enyo.kind({
+ name: "Dropbox",
+ kind: enyo.Control,
+ published: {
+ token: "",
+ tokenSecret: "",
+ requestToken: "",
+ requestSecret: "",
+ authorized: false
+ },
+ events: {
+ "onAuthSuccess": "",
+ "onAuthFailure": "",
+ "onGetFileSuccess": "",
+ "onGetFileFailure": "",
+ "onPutFileSuccess": "",
+ "onPutFileFailure": "",
+ "onMetadataSuccess": "",
+ "onMetadataFailure": ""
+ },
+ components: [
+ {name: "authPopup", kind: "ModalDialog", components: [
+ {content: "Please click DONE to complete the process."},
+ {kind:"Button", caption:"DONE", onclick:"requestAccessToken"}
+ ]},
+ {name: "launch", kind: "PalmService",
+ service: "palm://com.palm.applicationManager",
+ method: "launch", onSuccess: "launchSuccess",
+ onFailure: "launchFailed"
+ },
+ {name: "webSrv", kind: "WebService" }
+ ],
+
+ create: function() {
+ this.inherited(arguments);
+
+ this.contentURL = "https://api-content.dropbox.com/1";
+ this.apiURL = "https://api.dropbox.com/1";
+ this.mainURL = "https://www.dropbox.com/1";
+
+ /* valid values are either "dropbox" or "sandbox"
+ *
+ * See the following URL for the additional details.
+ * https://www.dropbox.com/developers/reference/api
+ */
+ this.root = "dropbox";
+
+ /* The following two properties are contained in the
+ * dropbox-auth.js file. Dropbox will NOT work unless you
+ * update the file to contain the correct values.
+ */
+ this.consumerKey = consumerKey;
+ this.consumerSecret = consumerSecret;
+ },
+
+ /* url - URL needed for a web service call
+ * params - (optional) Object containing paramters for the request
+ * body - (required for files_put) request body to be submitted
+ * method - method required for the request (GET, POST, PUT)
+ * onSuccess - callback function when web service call is successful
+ * onFailure - callback function when web service call has failed
+ */
+ dropboxCall: function(url, params, body, method, onSuccess, onFailure) {
+
+ var token = (this.authorized) ? this.token : this.requestToken;
+ var secret = (this.authorized) ? this.tokenSecret : this.requestSecret;
+
+ var accessor = {
+ consumerKey: this.consumerKey,
+ consumerSecret: this.consumerSecret,
+ token: token,
+ tokenSecret: secret
+ };
+
+ var message = {
+ action: url,
+ method: method,
+ parameters: params
+ };
+
+ OAuth.completeRequest(message, accessor);
+ OAuth.SignatureMethod.sign(message, accessor);
+ var post = OAuth.formEncode(message.parameters);
+
+ if (url.match(/files_put/)) {
+ requrl = url + "?" + post;
+ this.$.webSrv.setContentType("text/plain");
+ } else {
+ body = post;
+ requrl = url;
+ }
+
+ this.$.webSrv.setUrl(requrl);
+ this.$.webSrv.setMethod(method);
+ this.$.webSrv.onSuccess = onSuccess;
+ this.$.webSrv.onFailure = onFailure;
+ this.$.webSrv.call(body);
+ },
+
+ /* ******************** OAuth Functions ******************** */
+
+ validateAccess: function() {
+ if (this.token.length > 0 && this.tokenSecret.length > 0) {
+ var url = this.apiURL + "/account/info";
+ this.dropboxCall(url, {}, "", "POST",
+ "doAuthSuccess", "doAuthFailure");
+ } else {
+ this.$.authPopup.openAtCenter();
+ this.startTokenRequest();
+ }
+ },
+
+ startTokenRequest: function() {
+ var url = this.apiURL + "/oauth/request_token";
+ this.dropboxCall(url, {}, "", "POST",
+ "processRequestToken", "doAuthFailure");
+ },
+
+ processRequestToken: function(inSender, inResponse, inRequest) {
+ if (inResponse) {
+ var url = this.mainURL + "/oauth/authorize?" + inResponse;
+
+ var tokens = inResponse.split("&");
+ this.requestSecret = tokens[0].split("=")[1];
+ this.requestToken = tokens[1].split("=")[1];
+
+ console.log("...launching browser window...");
+ this.$.launch.call({
+ "id": "com.palm.app.browser",
+ "params": {"target": url}
+ });
+ }
+ },
+
+ requestAccessToken: function() {
+ var url = this.apiURL + "/oauth/access_token";
+ this.dropboxCall(url, {}, "", "POST",
+ "processAccessToken", "doAuthFailure");
+ },
+
+ processAccessToken: function(inSender, inResponse, inRequest) {
+ var tokens = inResponse.split("&");
+ var accessSecret = tokens[0].split("=")[1];
+ var accessToken = tokens[1].split("=")[1];
+
+ this.token = accessToken;
+ this.tokenSecret = accessSecret;
+ console.log("..authorized..");
+ //console.log(this.token);
+ //console.log(this.tokenSecret);
+ this.authorized = true;
+ this.doAuthSuccess();
+ this.$.authPopup.close();
+ },
+
+
+ /* ******************** Dropbox REST API ******************** */
+
+
+ /* file - Relative path of file to be uploaded.
+ * data - File contents to be uploaded.
+ * params - (optional) Object containing additional parameters.
+ *
+ * Returns the metadata for the uploaded file.
+ *
+ * See the following URL for the additional details
+ * https://www.dropbox.com/developers/reference/api#files_put
+ */
+ putFile: function(file, data, params) {
+ var url = this.contentURL+"/files_put/"+this.root+file;
+ this.dropboxCall(url, params, data, "PUT",
+ "doPutFileSuccess", "doPutFileFailure");
+ },
+
+ /* file - Relative path of file to be retrieved
+ * rev - (optional) File revision to be retrieved. Defaults to
+ * the most recent revision.
+ *
+ * Returns the file contents at the requested revision. The
+ * HTTP response contains the content metadata in JSON format
+ * within an x-dropbox-metadata header.
+ *
+ * See the following URL for the additional details.
+ * https://www.dropbox.com/developers/reference/api#files_put
+ */
+ getFile: function(file, rev) {
+ var url = this.contentURL+"/files/"+this.root+file;
+ var params = { rev: rev };
+ this.dropboxCall(url, params, "", "GET",
+ "doGetFileSuccess", "doGetFileFailure");
+ },
+
+ /* path - path to a file or folder
+ * params - (optional) Object containing additional parameters.
+ *
+ * Returns metadata for requested file or folder
+ *
+ * See the following URL for the additional details
+ * https://www.dropbox.com/developers/reference/api#metadata
+ */
+ getMetadata: function(path, params) {
+ var url = this.apiURL+"/metadata/"+this.root+path;
+ this.dropboxCall(url, params, "", "GET",
+ "doMetadataSuccess", "doMetadataFailure");
+ }
+
+});
diff --git a/app/source/TodoEdit.js b/app/source/TodoEdit.js
new file mode 100644
index 0000000..dc7b24a
--- /dev/null
+++ b/app/source/TodoEdit.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012 Morgan McMillian
+ *
+ * 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.
+ *
+ */
+enyo.kind({
+ name: "TodoEdit",
+ kind: enyo.VFlexBox,
+ events: {
+ "onClose": "",
+ "onSave": ""
+ },
+ components: [
+
+ {flex: 1, kind: "Scroller", components: [
+ {kind: "RichText", name: "tododetail"},
+ ]},
+ {name: "editToolbar", kind: "Toolbar", pack: "justify", className: "enyo-toolbar-light",
+ components: [
+ {flex: 1, kind: "Button", caption: "Cancel",
+ onclick: "doClose", align: "left"},
+ {flex: 1, kind: "Button", caption: "Save",
+ onclick: "doSave", align: "right"}
+ ]
+ }
+
+ ]
+
+});
diff --git a/app/source/TodoList.js b/app/source/TodoList.js
new file mode 100644
index 0000000..b922aff
--- /dev/null
+++ b/app/source/TodoList.js
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2012 Morgan McMillian
+ *
+ * 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.
+ *
+ */
+enyo.kind({
+ name: "TodoList",
+ kind: enyo.VFlexBox,
+ published: {
+ replaceItem: false,
+ completeItem: false,
+ sortOrder: "pri",
+ cacheChanges: "NO",
+ searchFilter: null,
+ sortedList: undefined
+ },
+ events: {
+ "onEdit": "",
+ "onPrefs": "",
+ "onReload": ""
+ },
+ components: [
+
+ {name: "todoPopup", kind: "ModalDialog", components: [
+ {kind: "Button", caption: "Complete", onclick: "completeTodoItem"},
+ {kind: "Button", caption: "Prioritize", onclick: "showPriList"},
+ {kind: "Button", caption: "Update", onclick: "updateTodoItem"},
+ {kind: "Button", caption: "Delete", onclick: "deleteTodoItem"},
+ {kind: "Button", caption: "Dismiss", onclick: "closePopup"}
+ ]},
+ {name: "completedPopup", kind: "ModalDialog", components: [
+ {kind: "Button", caption: "Undo Complete", onclick: "undoCompleteTodoItem"},
+ {kind: "Button", caption: "Delete", onclick: "deleteTodoItem"},
+ {kind: "Button", caption: "Dismiss", onclick: "closePopup"}
+ ]},
+ {name: "priorityPopup", kind: "ModalDialog", components: [
+ {kind: "HtmlContent", content: "Select priority"},
+ {kind: "RadioGroup", name: "priGroup", onChange: "setPriority", components: [
+ {caption: "-", value: "-"},
+ {caption: "A", value: "A"},
+ {caption: "B", value: "B"},
+ {caption: "C", value: "C"},
+ {caption: "D", value: "D"},
+ {caption: "E", value: "E"}
+ ]},
+ {kind: "Button", caption: "Dismiss", onclick: "closePopup"}
+ ]},
+ {kind: "SearchInput", name: "searchbox", onchange: "searchList", onCancel: "clearSearch"},
+ {flex: 1, kind: "Scroller", name: "scroller", components: [
+ {kind: "VirtualRepeater", name: "todoList",
+ onSetupRow: "getTodoList", onclick: "todoTap",
+ components: [
+ {kind: "SwipeableItem", onConfirm: "deleteTodo",
+ layoutKind: enyo.HFlexLayout, tapHighlight: true, name: "todoRow",
+ components: [
+ {name: "lineNum", className: "left-col"},
+ {name: "priority", className: "mid-col"},
+ {flex: 1, name: "todoItem"}
+ ]
+ }
+ ]
+ }
+ ]},
+ {name: "listToolbar", kind: "Toolbar", pack: "justify", className: "enyo-toolbar-light",
+ components: [
+ {flex: 1},
+ //{kind: "Button", caption: "Filter"},
+ //TODO: consider moving to the top
+ {kind: "ListSelector", value: "pri",
+ onChange: "changeSort", items: [
+ {caption: "Priority", value: "pri"},
+ {caption: "ID Ascending", value: "asc"},
+ {caption: "ID Descending", value: "dsc"},
+ {caption: "Text (A-Z)", value: "az"}
+ ]},
+ {icon: "images/menu-icon-new.png",
+ onclick: "doEdit", align: "right"},
+ {icon: "images/menu-icon-sync.png",
+ onclick: "doReload", align: "right"},
+ {icon: "images/menu-icon-prefs.png",
+ onclick: "doPrefs", align: "right"}
+ ]
+ },
+
+ ],
+
+ getTodoList: function(inSender, inIndex) {
+
+ if (this.sortedList == undefined) {
+ this.sortedList = this.owner.todoList.slice(0);
+ this.changeSort(null, this.sortOrder, null);
+ }
+
+ var r = this.sortedList[inIndex];
+
+ if (r) {
+ var filtered = false;
+ var s = r.detail.replace(/^x\s/,"");
+ var s = s.replace(/^\([A-E]\)\s/,"");
+ var filter = new RegExp(this.searchFilter, "i");
+ if (this.search && s.match(filter)) {
+ filtered = true;
+ } else if (!this.search) {
+ filtered = true;
+ }
+ if (this.owner.preferences["lineNumbers"]) {
+ this.$.lineNum.setContent(r.num);
+ }
+ this.$.priority.setContent(r.pri);
+ var pri = r.detail.replace(/^\(([A-E])\)\s.*$/,"$1");
+ if (r.pri == "(A) ") this.$.priority.addClass("pri-a");
+ if (r.pri == "(B) ") this.$.priority.addClass("pri-b");
+ if (r.pri == "(C) ") this.$.priority.addClass("pri-c");
+ this.$.todoItem.setContent(s);
+ if (r.detail.match(/^x\s/)) {
+ this.$.todoItem.addStyles("text-decoration:line-through;");
+ this.$.todoItem.addClass("completed-item");
+ }
+ if (inIndex == this.selectedIndex) {
+ this.$.todoRow.addClass("selected-item");
+ }
+ if (!filtered) {
+ this.$.todoRow.hide();
+ }
+ return true;
+ }
+ },
+
+ todoTap: function(inSender, inEvent) {
+ this.selectedIndex = inEvent.rowIndex;
+ if (this.selectedIndex != undefined) {
+ this.selectedId = this.sortedList[this.selectedIndex].num-1;
+ }
+ var r = this.sortedList[inEvent.rowIndex];
+
+ if (r) {
+ var completed = r.detail.match(/^x\s/);
+ var quick = this.owner.preferences["quickComplete"];
+ if (completed) {
+ this.$.completedPopup.openAtCenter();
+ } else if (quick) {
+ this.completeTodoItem();
+ } else {
+ this.$.todoPopup.openAtCenter();
+ }
+ }
+ this.$.todoList.render();
+ },
+
+ completeTodoItem: function() {
+ this.completeItem = true;
+ var dfmt = new enyo.g11n.DateFmt({date:"yyyy-MM-dd"});
+ this.cacheChanges = "START";
+ this.clearPriority();
+ this.owner.$.editView.$.tododetail.setValue(
+ "x " + dfmt.format(new Date()) + " " +
+ this.owner.todoList[this.selectedId].detail
+ );
+ this.replaceItem = true;
+ this.cacheChanges = "COMMIT";
+ this.owner.addTodo();
+ this.completeItem = false;
+ this.$.todoPopup.close();
+ },
+
+ undoCompleteTodoItem: function() {
+ this.completeItem = true;
+ this.owner.$.editView.$.tododetail.setValue(
+ this.owner.todoList[this.selectedId].detail.replace(/^x\s[0-9]{4}-[0-9]{2}-[0-9]{2}\s/,"")
+ );
+ this.replaceItem = true;
+ this.owner.addTodo();
+ this.completeItem = false;
+ this.closePopup();
+ },
+
+ updateTodoItem: function() {
+ this.owner.$.editView.$.tododetail.setValue(this.owner.todoList[this.selectedId].detail);
+ this.replaceItem = true;
+ this.owner.showEditView();
+ this.$.todoPopup.close();
+ },
+
+ addTodo: function() {
+ var task = new Object();
+ task.num = this.owner.todoList.length + 1;
+ task.pri = null;
+ task.detail = this.owner.$.editView.$.tododetail.getValue();
+ task.detail = task.detail.replace(/(<\/?[A-Za-z][A-Za-z0-9]*>)+/g," ");
+ if (this.owner.preferences["dateTasks"] && !this.completeItem) {
+ var dfmt = new enyo.g11n.DateFmt({date:"yyyy-MM-dd"});
+ task.detail = dfmt.format(new Date()) + " " + task.detail;
+ }
+ if (this.owner.preferences["storage"] != "none" && this.cacheChanges != "YES" && this.cacheChanges != "COMMIT") {
+ console.log("saving backup");
+ this.owner.saveFile(
+ this.owner.preferences["filepath"]+".bak", this.owner.todoList);
+ if (this.cacheChanges == "START") {
+ this.cacheChanges = "YES"
+ }
+ }
+ if (this.replaceItem) {
+ this.owner.todoList[this.selectedId].detail = task.detail;
+ this.owner.todoList[this.selectedId].pri = task.detail.match(/^\([A-E]\)\s/);
+ this.replaceItem = false;
+ } else {
+ this.owner.todoList.push(task);
+ }
+ this.listRefresh();
+ if (this.owner.preferences["storage"] != "none" && this.cacheChanges != "START" && this.cacheChanges != "YES") {
+ console.log("saving list");
+ this.owner.saveFile(
+ this.owner.preferences["filepath"], this.owner.todoList);
+ if (this.cacheChanges == "COMMIT") {
+ this.cacheChanges = "NO"
+ }
+ }
+ this.owner.closeView();
+ },
+
+ deleteTodoItem: function() {
+ this.deleteTodo(null, this.selectedId);
+ this.closePopup();
+ },
+
+ deleteTodo: function(inSender, inIndex) {
+ if (this.owner.preferences["storage"] != "none") {
+ this.owner.saveFile(
+ this.owner.preferences["filepath"]+".bak", this.owner.todoList);
+ }
+ this.owner.todoList.splice(inIndex, 1);
+ this.listRefresh();
+ if (this.owner.preferences["storage"] != "none") {
+ this.owner.saveFile(
+ this.owner.preferences["filepath"], this.owner.todoList);
+ }
+ },
+
+ closePopup: function() {
+ this.$.todoPopup.close();
+ this.$.completedPopup.close();
+ this.$.priorityPopup.close();
+ this.selectedIndex = null;
+ this.selectedId = null;
+ this.$.todoList.render();
+ },
+
+ showPriList: function() {
+ var r = this.sortedList[this.selectedIndex];
+
+ this.$.todoPopup.close();
+ this.$.priorityPopup.openAtCenter();
+ if (r.detail.match(/^\(([A-E])\)\s/)) {
+ var pri = r.detail.replace(/^\(([A-E])\)\s.*$/,"$1");
+ this.$.priGroup.setValue(pri);
+ } else {
+ this.$.priGroup.setValue(pri);
+ }
+ },
+
+ setPriority: function(inSender) {
+ var val = inSender.getValue();
+ this.clearPriority();
+ if (val.match(/[A-E]/)) {
+ this.owner.$.editView.$.tododetail.setValue(
+ "(" + val + ") " + this.owner.todoList[this.selectedId].detail);
+ this.replaceItem = true;
+ this.addTodo();
+ }
+ this.$.priorityPopup.close();
+ },
+
+ clearPriority: function() {
+ this.owner.$.editView.$.tododetail.setValue(
+ this.owner.todoList[this.selectedId].detail.replace(/^\([A-E]\)\s/,"")
+ );
+ this.replaceItem = true;
+ this.owner.addTodo();
+ },
+
+ searchList: function(inSender) {
+ this.searchFilter = inSender.getValue();
+ this.searchFilter = this.searchFilter.replace(/\+/,"\\+");
+ //console.log(this.searchFilter);
+ this.search = true;
+ this.$.scroller.scrollIntoView(0,0);
+ this.$.todoList.render();
+ },
+
+ clearSearch: function() {
+ this.searchFilter = null;
+ this.search = false;
+ this.$.todoList.render();
+ },
+
+ changeSort: function(inSender, inValue, inOldValue) {
+ this.sortOrder = inValue;
+ if (inValue == "pri") {
+ this.sortedList.sort(function (a,b) {
+ if (!a["pri"] && a["detail"].match(/^x\s/)) {
+ checka = "-";
+ } else if (!a["pri"]) {
+ checka = "+";
+ } else {
+ checka = a["pri"];
+ }
+ if (!b["pri"] && b["detail"].match(/^x\s/)) {
+ checkb = "-";
+ } else if (!b["pri"]) {
+ checkb = "+";
+ } else {
+ checkb = b["pri"];
+ }
+ if (checka < checkb) return -1;
+ if (checka > checkb) return 1;
+ return 0;
+ });
+ this.$.todoList.render();
+ } else if (inValue == "asc") {
+ this.sortedList.sort(function (a,b) {
+ if (a["num"] < b["num"]) return -1;
+ if (a["num"] > b["num"]) return 1;
+ return 0;
+ });
+ this.$.todoList.render();
+ } else if (inValue == "dsc") {
+ this.sortedList.sort(function (a,b) {
+ if (a["num"] > b["num"]) return -1;
+ if (a["num"] < b["num"]) return 1;
+ return 0;
+ });
+ this.$.todoList.render();
+ } else if (inValue == "az") {
+ this.sortedList.sort(function (a,b) {
+ var checka = a["detail"].replace(/^\([A-E]\)\s/,"");
+ var checkb = b["detail"].replace(/^\([A-E]\)\s/,"");
+ if (checka < checkb) return -1;
+ if (checka > checkb) return 1;
+ return 0;
+ });
+ this.$.todoList.render();
+ } else {
+ console.log("wait what?");
+ }
+ },
+
+ listRefresh: function() {
+ this.sortedList = undefined;
+ this.$.todoList.render();
+ }
+
+});
diff --git a/app/source/TodoPrefs.js b/app/source/TodoPrefs.js
new file mode 100644
index 0000000..8149bde
--- /dev/null
+++ b/app/source/TodoPrefs.js
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2012 Morgan McMillian
+ *
+ * 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.
+ *
+ */
+enyo.kind({
+ name: "TodoPrefs",
+ kind: enyo.VFlexBox,
+ events: {
+ "onClose": "",
+ "onAbout": "",
+ "onPrefReset": ""
+ },
+ components: [
+
+ {flex: 1, kind: "Scroller", components: [
+ {kind: "VFlexBox", className: "box-center", components: [
+ {kind: "RowGroup", caption: "todo.txt", components: [
+ {kind: "Item", layoutKind: "HFlexLayout",
+ components: [
+ {content: "Show line numbers"},
+ {kind: "Spacer"},
+ {kind: "CheckBox", name: "lineNumbers",
+ preferenceProperty: "lineNumbers",
+ onChange: "setPreference"}
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ components: [
+ {content: "Date new tasks"},
+ {kind: "Spacer"},
+ {kind: "CheckBox", name: "dateTasks",
+ preferenceProperty: "dateTasks",
+ onChange: "setPreference"}
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ components: [
+ {content: "Quick complete"},
+ {kind: "Spacer"},
+ {kind: "CheckBox", name: "quickComplete",
+ preferenceProperty: "quickComplete",
+ onChange: "setPreference"}
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ components: [
+ {content: "Work offline"},
+ {kind: "Spacer"},
+ {kind: "CheckBox", name: "offline",
+ preferenceProperty: "offline",
+ onChange: "setPreference"}
+ ]}
+ ]},
+ {kind: "RowGroup", caption: "storage", components: [
+ {kind: "Item", layoutKind: "HFlexLayout",
+ components: [
+ {flex: 1, kind: "ListSelector", name: "storage",
+ preferenceProperty: "storage",
+ onChange: "setPreference",
+ items: [
+ {caption: "No Storage", value: "none"},
+ {caption: "Internal Storage", value: "file"},
+ {caption: "Dropbox", value: "dropbox"}
+ ]}
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ name: "filepathselect", showing: true,
+ components: [
+ {flex: 1, kind: "Input", name: "filepath",
+ preferenceProperty: "filepath",
+ hint: "file path",
+ disabled: true
+ }
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ name: "dboxpathselect", showing: true,
+ components: [
+ {flex: 1, kind: "Input", name: "dboxpath",
+ preferenceProperty: "dboxpath",
+ hint: "Dropbox path",
+ onchange: "setPreference",
+ disabled: false
+ }
+ ]},
+ {kind: "Item", layoutKind: "HFlexLayout",
+ name: "dboxlogout", showing: false,
+ components: [
+ {flex: 1, kind: "Button",
+ caption: "Log out of Dropbox",
+ className: "enyo-button-negative",
+ onclick: "clearDropbox"
+ }
+ ]}
+ ]},
+ {kind: "Button", caption: "About", onclick: "doAbout",
+ className: "enyo-button-dark"
+ },
+ {kind: "Button", caption: "Reset Prefs", onclick: "doPrefReset",
+ className: "enyo-button-dark"
+ }
+ ]}
+ ]},
+ {name: "prefToolbar", kind: "Toolbar", pack: "justify", className: "enyo-toolbar-light",
+ components: [
+ {kind: "Spacer"},
+ {flex: 1, kind: "Button", caption: "Done",
+ onclick: "doClose", align: "center"},
+ {kind: "Spacer"}
+ ]
+ },
+ {name: "todoFilePicker", kind: "FilePicker", fileType: ["document"],
+ onPickFile: "fileSelected", allowMultiSelect: false}
+
+ ],
+
+ setPreference: function(inSender, inEvent, inValue) {
+ //var value = (inSender.kind === "CheckBox") ? inSender.getChecked() : inValue;
+ if (inSender.kind === "CheckBox") {
+ value = inSender.getChecked();
+ } else if (inSender.kind === "Input") {
+ value = inValue;
+ } else {
+ value = inEvent;
+ }
+
+ this.owner.preferences[inSender.preferenceProperty] = value;
+
+ if (inSender.preferenceProperty == "storage") {
+ if (value == "file") {
+ //this.$.todoFilePicker.pickFile();
+ var mypath = "/media/internal/todo/todo.txt"
+ this.owner.preferences["filepath"] = mypath;
+ this.$.filepath.setValue(mypath);
+ this.$.filepath.render();
+ this.$.dboxlogout.hide();
+ this.owner.preferences["offline"] = true;
+ this.owner.$.preferenceView.$.offline.setChecked(true);
+ this.owner.refreshTodo();
+ } else if (value == "dropbox") {
+ this.owner.$.dropbox.validateAccess();
+ } else {
+ this.owner.preferences["filepath"] = "";
+ this.$.filepath.setValue("");
+ this.$.filepath.render();
+ this.$.dboxlogout.hide();
+ this.owner.preferences["offline"] = true;
+ this.owner.$.preferenceView.$.offline.setChecked(true);
+ }
+ } else if (inSender.preferenceProperty == "dboxpath") {
+ //console.log(this.owner.preferences["dboxpath"]);
+ this.owner.dropboxRefresh = true;
+ this.owner.refreshTodo();
+ }
+
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.owner.preferences));
+ this.owner.$.listView.$.todoList.render();
+ },
+
+ fileSelected: function(inSender, inResponse) {
+ //console.log(inResponse[0].fullPath);
+ value = inResponse[0].fullPath;
+ if (value) {
+ this.owner.preferences["filepath"] = value;
+ this.$.filepath.setValue(value);
+ this.$.filepath.render();
+ this.owner.refreshTodo();
+ }
+ },
+
+ clearDropbox: function() {
+ this.owner.preferences["dboxuid"] = "";
+ this.owner.preferences["dboxtoken"] = "";
+ this.owner.preferences["dboxsecret"] = "";
+ this.owner.preferences["dboxname"] = "";
+ this.owner.preferences["dboxrev"] = "";
+ this.owner.preferences["storage"] = "file";
+ this.owner.preferences["offline"] = true;
+ this.owner.$.preferenceView.$.offline.setChecked(true);
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.owner.preferences));
+ this.$.storage.setValue("file");
+ this.$.dboxlogout.hide();
+ }
+
+});
diff --git a/app/source/TodoTxt.js b/app/source/TodoTxt.js
new file mode 100644
index 0000000..90257c1
--- /dev/null
+++ b/app/source/TodoTxt.js
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2012 Morgan McMillian
+ *
+ * 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.
+ *
+ */
+enyo.kind({
+ name: "TodoTxt",
+ kind: enyo.VFlexBox,
+ published: {
+ launchParams: null
+ },
+ components: [
+
+ {kind: "AppMenu", components: [
+ {caption: "Preferences", onclick: "showPrefView"},
+ {caption: "About", onclick: "showAbout"}
+ ]},
+ {kind: "Popup", name: "about", layoutKind: "VFlexLayout",
+ contentHeight: "100%", height: "80%", width: "80%",
+ components: [
+ {name: "aboutTitle", content: ""},
+ {content: "by Morgan McMillian"},
+ {kind: "Divider", caption: "Version History"},
+ {flex: 1, kind: "Scroller", components: [
+ {kind: "HtmlContent", className: "ver-history",
+ srcId: "history"}
+ ]},
+ {kind: "Button", caption: "Done", onclick: "closeAbout" }
+ ]},
+ {kind: "PageHeader", pack: "justify", components: [
+ {kind: "HtmlContent", content: "Todo.txt Enyo [beta]"},
+ {flex: 1},
+ {name: "viewTitle", kind: "HtmlContent",
+ className: "todo-view-title", content: ""}
+ ]},
+ {flex: 1, kind: "Pane", onSelectView: "viewSelected", components: [
+ {name: "listView", kind: "TodoList", onEdit: "showEditView",
+ onPrefs: "showPrefView", onReload: "refreshTodo"
+ },
+ {name: "editView", kind: "TodoEdit",
+ onClose: "closeView", onSave: "addTodo"
+ },
+ {name: "preferenceView", kind: "TodoPrefs",
+ onClose: "closeView", onAbout: "showAbout",
+ onPrefReset: "resetPreferences"
+ }
+ ]},
+ {name: "dropbox", kind: "Dropbox",
+ onAuthSuccess: "enableDropbox",
+ onAuthFailure: "disableDropbox",
+ onGetFileSuccess: "loadDropbox",
+ onGetFileFailure: "fail",
+ onPutFileSuccess: "dboxPutFileSuccess",
+ onPutFileFailure: "fail"
+ },
+ {name: "readFile", kind: "PalmService",
+ service: "palm://com.monkeystew.todotxtenyo.beta.service/",
+ method: "readfile",
+ onSuccess: "parseFile", onFailure: "doNothing"
+ },
+ {name: "writeFile", kind: "PalmService",
+ service: "palm://com.monkeystew.todotxtenyo.beta.service/",
+ method: "writefile", onSuccess: "saveSuccess"
+ },
+ {name: "makeDir", kind: "PalmService",
+ service: "palm://com.monkeystew.todotxtenyo.beta.service/",
+ method: "makedir", onSuccess: "dirCreated",
+ onFailure: "doNothing"
+ },
+ {name: "getConn", kind: "PalmService",
+ service: "palm://com.palm.connectionmanager/",
+ method: "getstatus",
+ onSuccess: "handleConnectionChange",
+ subscribe: true
+ }
+
+ ],
+
+ ready: function() {
+ this.$.getConn.call();
+
+ this.preferences = localStorage.getItem("TodoPreferences");
+ if (this.preferences == undefined) {
+ this.resetPreferences();
+ } else {
+ this.preferences = JSON.parse(this.preferences);
+ }
+
+ if (this.preferences["storage"] == "dropbox") {
+ if (this.preferences["dboxtoken"]) {
+ this.$.dropbox.setToken(
+ this.preferences["dboxtoken"]
+ );
+ this.$.dropbox.setTokenSecret(
+ this.preferences["dboxsecret"]
+ );
+ this.$.dropbox.setAuthorized(true);
+ this.$.preferenceView.$.dboxlogout.show();
+ this.$.preferenceView.$.dboxpathselect.show();
+ this.$.preferenceView.$.filepathselect.hide();
+ }
+ }
+
+ //TODO: need to derive a better method for introducing new
+ //preferences into the application
+
+ if (this.preferences["dboxpath"] == undefined) {
+ this.preferences["dboxpath"] = "/todo";
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.preferences));
+ }
+
+ if (this.preferences["offline"] == true) {
+ this.$.viewTitle.setContent("[offline]");
+ }
+
+ this.$.makeDir.call({ path: "/media/internal/todo" });
+
+ this.todoList = [];
+ this.refreshTodo();
+
+ },
+
+ launchParamsChanged: function() {
+ if (this.launchParams) {
+ if (this.launchParams.addTodoText) {
+ this.$.editView.$.tododetail.setValue(
+ this.launchParams.addTodoText
+ );
+ this.showEditView();
+ } else if (this.launchParams.search) {
+ this.$.listView.$.searchbox.setValue(
+ this.launchParams.search
+ );
+ this.$.listView.$.searchbox.fire("onchange");
+ }
+ }
+ },
+
+ viewSelected: function(inSender, inView) {
+ if (inView == this.$.listView) {
+ if (this.preferences["offline"] == true) {
+ this.$.viewTitle.setContent("[offline]");
+ } else {
+ this.$.viewTitle.setContent("");
+ }
+ } else if (inView == this.$.preferenceView) {
+ this.$.viewTitle.setContent("preferences");
+ }
+ },
+
+ refreshTodo: function() {
+ if (this.preferences["storage"] == "file") {
+ this.getLocalFile();
+ } else if (this.preferences["storage"] == "dropbox") {
+ if (this.preferences["offline"] == false) {
+ var dboxpath = this.preferences["dboxpath"];
+ this.$.dropbox.getFile(dboxpath+"/todo.txt");
+ } else {
+ console.log("working offline, loading local copy instead");
+ this.getLocalFile();
+ }
+ }
+ },
+
+ addTodo: function() {
+ this.$.listView.addTodo();
+ },
+
+ showEditView: function() {
+ this.$.pane.selectViewByName("editView");
+ if (this.$.listView.getReplaceItem()) {
+ this.$.viewTitle.setContent("update task");
+ } else {
+ this.$.viewTitle.setContent("add task");
+ }
+ this.$.editView.$.tododetail.forceFocus();
+ },
+
+ showPrefView: function() {
+ for ( item in this.preferences ) {
+ var component = eval("this.$.preferenceView.$."+item);
+ if (component != undefined) {
+ if (component.kind === "CheckBox") {
+ component.setChecked(this.preferences[component.preferenceProperty]);
+ } else {
+ component.setValue(this.preferences[component.preferenceProperty]);
+ }
+ }
+ }
+ this.$.pane.selectViewByName("preferenceView");
+ },
+
+ showAbout: function() {
+ this.$.about.openAtCenter();
+ this.$.aboutTitle.setContent(
+ "Todo.txt Enyo [beta] v" + enyo.fetchAppInfo().version);
+ this.$.about.render();
+ },
+
+ closeAbout: function() {
+ this.$.about.close();
+ },
+
+ closeView: function() {
+ this.$.editView.$.tododetail.setValue("");
+ this.$.pane.selectViewByName("listView");
+ },
+
+ parseFile: function(path, file) {
+ var todofile = file.content.split("\n");
+ this.todoList = [];
+ for (line in todofile) {
+ if (todofile[line]) {
+ if (typeof line == "string") {
+ line = parseInt(line, 10);
+ }
+ var task = new Object();
+ task.num = line + 1;
+ task.pri = todofile[line].match(/^\([A-E]\)\s/);
+ task.detail = todofile[line];
+ this.todoList.push(task);
+ }
+ }
+ this.$.listView.setSortedList(undefined);
+ this.$.listView.$.todoList.render();
+ console.log("row count: " + this.todoList.length);
+ },
+
+ saveFile: function(path, list) {
+ data = "";
+ for (item in list) {
+ data = data + list[item].detail + "\n";
+ }
+ this.$.writeFile.call({
+ path: path, content: data });
+
+ if (this.preferences["storage"] == "dropbox" &&
+ this.preferences["offline"] == false &&
+ this.dropboxRefresh == false) {
+ var filename = path.match(/todo\.txt.*/);
+ filename = this.preferences["dboxpath"]+"/"+filename;
+ //var params = { overwrite: true };
+ this.$.dropbox.putFile(filename, data);
+ } else if (this.dropboxRefresh == true) {
+ this.dropboxRefresh = false;
+ }
+ },
+
+ saveSuccess: function(inSender, inEvent) {
+ if (inEvent.error) {
+ console.log("error: "+inEvent.error.message);
+ } else {
+ enyo.windows.addBannerMessage(inEvent.path + " saved", "{}");
+ console.log(inEvent.path + " saved...");
+ console.log(inEvent.bytes + " bytes...");
+ }
+ },
+
+ dboxPutFileSuccess: function(inSender, inResponse) {
+ console.log(inResponse.path + " uploaded to Dropbox");
+ console.log("at revision " + inResponse.revision);
+ console.log(inResponse.size + " bytes...");
+ },
+
+ doNothing: function(inSender) {
+ console.log("errrr");
+ console.log(":: " + inSender);
+ },
+
+ dirCreated: function(inSender, inEvent) {
+ if (inEvent.error) {
+ console.log("error: " + inEvent.error.message);
+ }
+ },
+
+ getLocalFile: function() {
+ this.$.readFile.call({ path: this.preferences["filepath"] });
+ },
+
+ loadDropbox: function(inSender, inResponse, inRequest) {
+ var file = new Object();
+ file.content = inResponse;
+ this.parseFile(null, file);
+ this.dropboxRefresh = true;
+ this.saveFile(this.preferences["filepath"], this.todoList);
+ },
+
+ resetPreferences: function() {
+ this.preferences = new Object();
+ this.preferences["storage"] = "file";
+ this.preferences["offline"] = true;
+ this.preferences["filepath"] = "/media/internal/todo/todo.txt";
+ this.preferences["dboxpath"] = "/todo";
+ this.$.preferenceView.$.offline.setChecked(true);
+ this.$.preferenceView.$.dboxlogout.hide();
+ this.$.preferenceView.$.dboxpathselect.hide();
+ this.$.preferenceView.$.filepathselect.show();
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.preferences));
+ },
+
+ handleConnectionChange: function(inSender, inResponse) {
+ var r = inResponse.isInternetConnectionAvailable;
+
+ if (r) {
+ console.log("went online...");
+ } else {
+ console.log("went offline...");
+ if (this.preferences["offline"] == false) {
+ enyo.windows.addBannerMessage("offline mode", "{}");
+ this.preferences["offline"] = true;
+ this.$.preferenceView.$.offline.setChecked(true);
+ }
+ }
+ },
+
+ fail: function(inSender, inResponse, inRequest) {
+ console.log("error");
+ console.log(JSON.stringify(inResponse));
+ enyo.windows.addBannerMessage(inResponse.error, "{}");
+ },
+
+ enableDropbox: function() {
+ console.log("authentication successful, enabling dropbox");
+ var token = this.$.dropbox.getToken();
+ var secret = this.$.dropbox.getTokenSecret();
+ this.preferences["dboxtoken"] = token;
+ this.preferences["dboxsecret"] = secret;
+ this.preferences["offline"] = false;
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.preferences));
+ this.$.preferenceView.$.dboxlogout.show();
+ this.$.preferenceView.$.dboxpathselect.show();
+ this.$.preferenceView.$.filepathselect.hide();
+ this.dropboxRefresh = true;
+ this.refreshTodo();
+ },
+
+ disableDropbox: function() {
+ this.$.dropbox.setToken("");
+ this.$.dropbox.setTokenSecret("");
+ this.$.dropbox.setAuthorized(false);
+ this.preferences["storage"] = "file";
+ this.preferences["dboxtoken"] = "";
+ this.preferences["dboxsecret"] = "";
+ localStorage.setItem("TodoPreferences", JSON.stringify(this.preferences));
+ }
+
+});
diff --git a/app/source/dropbox-auth.js.sample b/app/source/dropbox-auth.js.sample
new file mode 100644
index 0000000..c0aed71
--- /dev/null
+++ b/app/source/dropbox-auth.js.sample
@@ -0,0 +1,9 @@
+
+/* Dropbox will NOT work unless you replace the following two
+* properties with the appropriate values for your registered
+* application and rename the file to dropbox-auth.js or you
+* will be able to package and run this correctly.
+*/
+
+var consumerKey = "";
+var consumerSecret = "";
diff --git a/app/source/oauth.js b/app/source/oauth.js
new file mode 100644
index 0000000..295569a
--- /dev/null
+++ b/app/source/oauth.js
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2008 Netflix, Inc.
+ *
+ * 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.
+ */
+
+/* Here's some JavaScript software for implementing OAuth.
+
+ This isn't as useful as you might hope. OAuth is based around
+ allowing tools and websites to talk to each other. However,
+ JavaScript running in web browsers is hampered by security
+ restrictions that prevent code running on one website from
+ accessing data stored or served on another.
+
+ Before you start hacking, make sure you understand the limitations
+ posed by cross-domain XMLHttpRequest.
+
+ On the bright side, some platforms use JavaScript as their
+ language, but enable the programmer to access other web sites.
+ Examples include Google Gadgets, and Microsoft Vista Sidebar.
+ For those platforms, this library should come in handy.
+*/
+
+// The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by
+// http://pajhome.org.uk/crypt/md5/sha1.js
+
+/* An OAuth message is represented as an object like this:
+ {method: "GET", action: "http://server.com/path", parameters: ...}
+
+ The parameters may be either a map {name: value, name2: value2}
+ or an Array of name-value pairs [[name, value], [name2, value2]].
+ The latter representation is more powerful: it supports parameters
+ in a specific sequence, or several parameters with the same name;
+ for example [["a", 1], ["b", 2], ["a", 3]].
+
+ Parameter names and values are NOT percent-encoded in an object.
+ They must be encoded before transmission and decoded after reception.
+ For example, this message object:
+ {method: "GET", action: "http://server/path", parameters: {p: "x y"}}
+ ... can be transmitted as an HTTP request that begins:
+ GET /path?p=x%20y HTTP/1.0
+ (This isn't a valid OAuth request, since it lacks a signature etc.)
+ Note that the object "x y" is transmitted as x%20y. To encode
+ parameters, you can call OAuth.addToURL, OAuth.formEncode or
+ OAuth.getAuthorization.
+
+ This message object model harmonizes with the browser object model for
+ input elements of an form, whose value property isn't percent encoded.
+ The browser encodes each value before transmitting it. For example,
+ see consumer.setInputs in example/consumer.js.
+ */
+
+/* This script needs to know what time it is. By default, it uses the local
+ clock (new Date), which is apt to be inaccurate in browsers. To do
+ better, you can load this script from a URL whose query string contains
+ an oauth_timestamp parameter, whose value is a current Unix timestamp.
+ For example, when generating the enclosing document using PHP:
+
+