initial import of beta source tree
This commit is contained in:
parent
436d07046b
commit
b34e3fa7aa
31 changed files with 2357 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.swp
|
||||||
|
*.ipk
|
||||||
|
*.*~
|
||||||
|
dropbox-auth.js
|
202
app/LICENSE-2.0.txt
Normal file
202
app/LICENSE-2.0.txt
Normal file
|
@ -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.
|
22
app/appinfo.json
Normal file
22
app/appinfo.json
Normal file
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
app/depends.js
Normal file
11
app/depends.js
Normal file
|
@ -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"
|
||||||
|
);
|
3
app/framework_config.json
Normal file
3
app/framework_config.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"logLevel": 99
|
||||||
|
}
|
BIN
app/icon.png
Normal file
BIN
app/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
app/images/menu-icon-new.png
Normal file
BIN
app/images/menu-icon-new.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
app/images/menu-icon-prefs.png
Normal file
BIN
app/images/menu-icon-prefs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.1 KiB |
BIN
app/images/menu-icon-sync.png
Normal file
BIN
app/images/menu-icon-sync.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
25
app/index.html
Normal file
25
app/index.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Todo.txt Enyo</title>
|
||||||
|
<meta name="viewport" content="height=device-height,width=device-width">
|
||||||
|
<script src="/opt/PalmSDK/Current/share/framework/enyo/1.0/framework/enyo.js" type="text/javascript" launch="nobridge"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var appInstance = enyo.create({kind: "TodoTxt"});
|
||||||
|
|
||||||
|
if (window.PalmSystem && enyo.windowParams) {
|
||||||
|
appInstance.setLaunchParams(enyo.windowParams);
|
||||||
|
}
|
||||||
|
appInstance.renderInto(document.body);
|
||||||
|
</script>
|
||||||
|
<div id="history" style="display: none">
|
||||||
|
<div class="version">0.1.0</div>
|
||||||
|
<ul>
|
||||||
|
<li>Initial open source and beta release.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
4
app/mock/todoTxt_getConn.json
Normal file
4
app/mock/todoTxt_getConn.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"returnValue": true,
|
||||||
|
"isInternetConnectionAvailable": false
|
||||||
|
}
|
1
app/mock/todoTxt_makeDir.json
Normal file
1
app/mock/todoTxt_makeDir.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{ "returnValue": true }
|
5
app/mock/todoTxt_readFile.json
Normal file
5
app/mock/todoTxt_readFile.json
Normal file
|
@ -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"
|
||||||
|
|
||||||
|
}
|
5
app/mock/todoTxt_writeFile.json
Normal file
5
app/mock/todoTxt_writeFile.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"returnValue": true,
|
||||||
|
"path": "no dummy",
|
||||||
|
"bytes": "no bytes either"
|
||||||
|
}
|
223
app/source/Dropbox.js
Normal file
223
app/source/Dropbox.js
Normal file
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
40
app/source/TodoEdit.js
Normal file
40
app/source/TodoEdit.js
Normal file
|
@ -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"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
});
|
363
app/source/TodoList.js
Normal file
363
app/source/TodoList.js
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
193
app/source/TodoPrefs.js
Normal file
193
app/source/TodoPrefs.js
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
358
app/source/TodoTxt.js
Normal file
358
app/source/TodoTxt.js
Normal file
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
9
app/source/dropbox-auth.js.sample
Normal file
9
app/source/dropbox-auth.js.sample
Normal file
|
@ -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 = "";
|
551
app/source/oauth.js
Normal file
551
app/source/oauth.js
Normal file
|
@ -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:
|
||||||
|
|
||||||
|
<script src="oauth.js?oauth_timestamp=<?=time()?>" ...
|
||||||
|
|
||||||
|
Another option is to call OAuth.correctTimestamp with a Unix timestamp.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var OAuth; if (OAuth == null) OAuth = {};
|
||||||
|
|
||||||
|
OAuth.setProperties = function setProperties(into, from) {
|
||||||
|
if (into != null && from != null) {
|
||||||
|
for (var key in from) {
|
||||||
|
into[key] = from[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return into;
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth.setProperties(OAuth, // utility functions
|
||||||
|
{
|
||||||
|
percentEncode: function percentEncode(s) {
|
||||||
|
if (s == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (s instanceof Array) {
|
||||||
|
var e = "";
|
||||||
|
for (var i = 0; i < s.length; ++s) {
|
||||||
|
if (e != "") e += '&';
|
||||||
|
e += OAuth.percentEncode(s[i]);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
s = encodeURIComponent(s);
|
||||||
|
// Now replace the values which encodeURIComponent doesn't do
|
||||||
|
// encodeURIComponent ignores: - _ . ! ~ * ' ( )
|
||||||
|
// OAuth dictates the only ones you can ignore are: - _ . ~
|
||||||
|
// Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent
|
||||||
|
s = s.replace(/\!/g, "%21");
|
||||||
|
s = s.replace(/\*/g, "%2A");
|
||||||
|
s = s.replace(/\'/g, "%27");
|
||||||
|
s = s.replace(/\(/g, "%28");
|
||||||
|
s = s.replace(/\)/g, "%29");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
decodePercent: function decodePercent(s) {
|
||||||
|
if (s != null) {
|
||||||
|
// Handle application/x-www-form-urlencoded, which is defined by
|
||||||
|
// http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
|
||||||
|
s = s.replace(/\+/g, " ");
|
||||||
|
}
|
||||||
|
return decodeURIComponent(s);
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Convert the given parameters to an Array of name-value pairs. */
|
||||||
|
getParameterList: function getParameterList(parameters) {
|
||||||
|
if (parameters == null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (typeof parameters != "object") {
|
||||||
|
return OAuth.decodeForm(parameters + "");
|
||||||
|
}
|
||||||
|
if (parameters instanceof Array) {
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
var list = [];
|
||||||
|
for (var p in parameters) {
|
||||||
|
list.push([p, parameters[p]]);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Convert the given parameters to a map from name to value. */
|
||||||
|
getParameterMap: function getParameterMap(parameters) {
|
||||||
|
if (parameters == null) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (typeof parameters != "object") {
|
||||||
|
return OAuth.getParameterMap(OAuth.decodeForm(parameters + ""));
|
||||||
|
}
|
||||||
|
if (parameters instanceof Array) {
|
||||||
|
var map = {};
|
||||||
|
for (var p = 0; p < parameters.length; ++p) {
|
||||||
|
var key = parameters[p][0];
|
||||||
|
if (map[key] === undefined) { // first value wins
|
||||||
|
map[key] = parameters[p][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
getParameter: function getParameter(parameters, name) {
|
||||||
|
if (parameters instanceof Array) {
|
||||||
|
for (var p = 0; p < parameters.length; ++p) {
|
||||||
|
if (parameters[p][0] == name) {
|
||||||
|
return parameters[p][1]; // first value wins
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return OAuth.getParameterMap(parameters)[name];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
formEncode: function formEncode(parameters) {
|
||||||
|
var form = "";
|
||||||
|
var list = OAuth.getParameterList(parameters);
|
||||||
|
for (var p = 0; p < list.length; ++p) {
|
||||||
|
var value = list[p][1];
|
||||||
|
if (value == null) value = "";
|
||||||
|
if (form != "") form += '&';
|
||||||
|
form += OAuth.percentEncode(list[p][0])
|
||||||
|
+'='+ OAuth.percentEncode(value);
|
||||||
|
}
|
||||||
|
return form;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
decodeForm: function decodeForm(form) {
|
||||||
|
var list = [];
|
||||||
|
var nvps = form.split('&');
|
||||||
|
for (var n = 0; n < nvps.length; ++n) {
|
||||||
|
var nvp = nvps[n];
|
||||||
|
if (nvp == "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var equals = nvp.indexOf('=');
|
||||||
|
var name;
|
||||||
|
var value;
|
||||||
|
if (equals < 0) {
|
||||||
|
name = OAuth.decodePercent(nvp);
|
||||||
|
value = null;
|
||||||
|
} else {
|
||||||
|
name = OAuth.decodePercent(nvp.substring(0, equals));
|
||||||
|
value = OAuth.decodePercent(nvp.substring(equals + 1));
|
||||||
|
}
|
||||||
|
list.push([name, value]);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
setParameter: function setParameter(message, name, value) {
|
||||||
|
var parameters = message.parameters;
|
||||||
|
if (parameters instanceof Array) {
|
||||||
|
for (var p = 0; p < parameters.length; ++p) {
|
||||||
|
if (parameters[p][0] == name) {
|
||||||
|
if (value === undefined) {
|
||||||
|
parameters.splice(p, 1);
|
||||||
|
} else {
|
||||||
|
parameters[p][1] = value;
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value !== undefined) {
|
||||||
|
parameters.push([name, value]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parameters = OAuth.getParameterMap(parameters);
|
||||||
|
parameters[name] = value;
|
||||||
|
message.parameters = parameters;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,
|
||||||
|
setParameters: function setParameters(message, parameters) {
|
||||||
|
var list = OAuth.getParameterList(parameters);
|
||||||
|
for (var i = 0; i < list.length; ++i) {
|
||||||
|
OAuth.setParameter(message, list[i][0], list[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Fill in parameters to help construct a request message.
|
||||||
|
This function doesn't fill in every parameter.
|
||||||
|
The accessor object should be like:
|
||||||
|
{consumerKey:'foo', consumerSecret:'bar', accessorSecret:'nurn', token:'krelm', tokenSecret:'blah'}
|
||||||
|
The accessorSecret property is optional.
|
||||||
|
*/
|
||||||
|
completeRequest: function completeRequest(message, accessor) {
|
||||||
|
if (message.method == null) {
|
||||||
|
message.method = "GET";
|
||||||
|
}
|
||||||
|
var map = OAuth.getParameterMap(message.parameters);
|
||||||
|
if (map.oauth_consumer_key == null) {
|
||||||
|
OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || "");
|
||||||
|
}
|
||||||
|
if (map.oauth_token == null && accessor.token != null) {
|
||||||
|
OAuth.setParameter(message, "oauth_token", accessor.token);
|
||||||
|
}
|
||||||
|
if (map.oauth_version == null) {
|
||||||
|
OAuth.setParameter(message, "oauth_version", "1.0");
|
||||||
|
}
|
||||||
|
if (map.oauth_timestamp == null) {
|
||||||
|
OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
|
||||||
|
}
|
||||||
|
if (map.oauth_nonce == null) {
|
||||||
|
OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
|
||||||
|
}
|
||||||
|
OAuth.SignatureMethod.sign(message, accessor);
|
||||||
|
}
|
||||||
|
,
|
||||||
|
setTimestampAndNonce: function setTimestampAndNonce(message) {
|
||||||
|
OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp());
|
||||||
|
OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6));
|
||||||
|
}
|
||||||
|
,
|
||||||
|
addToURL: function addToURL(url, parameters) {
|
||||||
|
newURL = url;
|
||||||
|
if (parameters != null) {
|
||||||
|
var toAdd = OAuth.formEncode(parameters);
|
||||||
|
if (toAdd.length > 0) {
|
||||||
|
var q = url.indexOf('?');
|
||||||
|
if (q < 0) newURL += '?';
|
||||||
|
else newURL += '&';
|
||||||
|
newURL += toAdd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newURL;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Construct the value of the Authorization header for an HTTP request. */
|
||||||
|
getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) {
|
||||||
|
var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"';
|
||||||
|
var list = OAuth.getParameterList(parameters);
|
||||||
|
for (var p = 0; p < list.length; ++p) {
|
||||||
|
var parameter = list[p];
|
||||||
|
var name = parameter[0];
|
||||||
|
if (name.indexOf("oauth_") == 0) {
|
||||||
|
header += ',' + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Correct the time using a parameter from the URL from which the last script was loaded. */
|
||||||
|
correctTimestampFromSrc: function correctTimestampFromSrc(parameterName) {
|
||||||
|
parameterName = parameterName || "oauth_timestamp";
|
||||||
|
var scripts = document.getElementsByTagName('script');
|
||||||
|
if (scripts == null || !scripts.length) return;
|
||||||
|
var src = scripts[scripts.length-1].src;
|
||||||
|
if (!src) return;
|
||||||
|
var q = src.indexOf("?");
|
||||||
|
if (q < 0) return;
|
||||||
|
parameters = OAuth.getParameterMap(OAuth.decodeForm(src.substring(q+1)));
|
||||||
|
var t = parameters[parameterName];
|
||||||
|
if (t == null) return;
|
||||||
|
OAuth.correctTimestamp(t);
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Generate timestamps starting with the given value. */
|
||||||
|
correctTimestamp: function correctTimestamp(timestamp) {
|
||||||
|
OAuth.timeCorrectionMsec = (timestamp * 1000) - (new Date()).getTime();
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** The difference between the correct time and my clock. */
|
||||||
|
timeCorrectionMsec: 0
|
||||||
|
,
|
||||||
|
timestamp: function timestamp() {
|
||||||
|
var t = (new Date()).getTime() + OAuth.timeCorrectionMsec;
|
||||||
|
return Math.floor(t / 1000);
|
||||||
|
}
|
||||||
|
,
|
||||||
|
nonce: function nonce(length) {
|
||||||
|
var chars = OAuth.nonce.CHARS;
|
||||||
|
var result = "";
|
||||||
|
for (var i = 0; i < length; ++i) {
|
||||||
|
var rnum = Math.floor(Math.random() * chars.length);
|
||||||
|
result += chars.substring(rnum, rnum+1);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
/** Define a constructor function,
|
||||||
|
without causing trouble to anyone who was using it as a namespace.
|
||||||
|
That is, if parent[name] already existed and had properties,
|
||||||
|
copy those properties into the new constructor.
|
||||||
|
*/
|
||||||
|
OAuth.declareClass = function declareClass(parent, name, newConstructor) {
|
||||||
|
var previous = parent[name];
|
||||||
|
parent[name] = newConstructor;
|
||||||
|
if (newConstructor != null && previous != null) {
|
||||||
|
for (var key in previous) {
|
||||||
|
if (key != "prototype") {
|
||||||
|
newConstructor[key] = previous[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An abstract algorithm for signing messages. */
|
||||||
|
OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod(){});
|
||||||
|
|
||||||
|
OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members
|
||||||
|
{
|
||||||
|
/** Add a signature to the message. */
|
||||||
|
sign: function sign(message) {
|
||||||
|
var baseString = OAuth.SignatureMethod.getBaseString(message);
|
||||||
|
var signature = this.getSignature(baseString);
|
||||||
|
OAuth.setParameter(message, "oauth_signature", signature);
|
||||||
|
return signature; // just in case someone's interested
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Set the key string for signing. */
|
||||||
|
initialize: function initialize(name, accessor) {
|
||||||
|
var consumerSecret;
|
||||||
|
if (accessor.accessorSecret != null
|
||||||
|
&& name.length > 9
|
||||||
|
&& name.substring(name.length-9) == "-Accessor")
|
||||||
|
{
|
||||||
|
consumerSecret = accessor.accessorSecret;
|
||||||
|
} else {
|
||||||
|
consumerSecret = accessor.consumerSecret;
|
||||||
|
}
|
||||||
|
this.key = OAuth.percentEncode(consumerSecret)
|
||||||
|
+"&"+ OAuth.percentEncode(accessor.tokenSecret);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* SignatureMethod expects an accessor object to be like this:
|
||||||
|
{tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."}
|
||||||
|
The accessorSecret property is optional.
|
||||||
|
*/
|
||||||
|
// Class members:
|
||||||
|
OAuth.setProperties(OAuth.SignatureMethod, // class members
|
||||||
|
{
|
||||||
|
sign: function sign(message, accessor) {
|
||||||
|
var name = OAuth.getParameterMap(message.parameters).oauth_signature_method;
|
||||||
|
if (name == null || name == "") {
|
||||||
|
name = "HMAC-SHA1";
|
||||||
|
OAuth.setParameter(message, "oauth_signature_method", name);
|
||||||
|
}
|
||||||
|
OAuth.SignatureMethod.newMethod(name, accessor).sign(message);
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Instantiate a SignatureMethod for the given method name. */
|
||||||
|
newMethod: function newMethod(name, accessor) {
|
||||||
|
var impl = OAuth.SignatureMethod.REGISTERED[name];
|
||||||
|
if (impl != null) {
|
||||||
|
var method = new impl();
|
||||||
|
method.initialize(name, accessor);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
var err = new Error("signature_method_rejected");
|
||||||
|
var acceptable = "";
|
||||||
|
for (var r in OAuth.SignatureMethod.REGISTERED) {
|
||||||
|
if (acceptable != "") acceptable += '&';
|
||||||
|
acceptable += OAuth.percentEncode(r);
|
||||||
|
}
|
||||||
|
err.oauth_acceptable_signature_methods = acceptable;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** A map from signature method name to constructor. */
|
||||||
|
REGISTERED : {}
|
||||||
|
,
|
||||||
|
/** Subsequently, the given constructor will be used for the named methods.
|
||||||
|
The constructor will be called with no parameters.
|
||||||
|
The resulting object should usually implement getSignature(baseString).
|
||||||
|
You can easily define such a constructor by calling makeSubclass, below.
|
||||||
|
*/
|
||||||
|
registerMethodClass: function registerMethodClass(names, classConstructor) {
|
||||||
|
for (var n = 0; n < names.length; ++n) {
|
||||||
|
OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
,
|
||||||
|
/** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */
|
||||||
|
makeSubclass: function makeSubclass(getSignatureFunction) {
|
||||||
|
var superClass = OAuth.SignatureMethod;
|
||||||
|
var subClass = function() {
|
||||||
|
superClass.call(this);
|
||||||
|
};
|
||||||
|
subClass.prototype = new superClass();
|
||||||
|
// Delete instance variables from prototype:
|
||||||
|
// delete subclass.prototype... There aren't any.
|
||||||
|
subClass.prototype.getSignature = getSignatureFunction;
|
||||||
|
subClass.prototype.constructor = subClass;
|
||||||
|
return subClass;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
getBaseString: function getBaseString(message) {
|
||||||
|
var URL = message.action;
|
||||||
|
var q = URL.indexOf('?');
|
||||||
|
var parameters;
|
||||||
|
if (q < 0) {
|
||||||
|
parameters = message.parameters;
|
||||||
|
} else {
|
||||||
|
// Combine the URL query string with the other parameters:
|
||||||
|
parameters = OAuth.decodeForm(URL.substring(q + 1));
|
||||||
|
var toAdd = OAuth.getParameterList(message.parameters);
|
||||||
|
for (var a = 0; a < toAdd.length; ++a) {
|
||||||
|
parameters.push(toAdd[a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OAuth.percentEncode(message.method.toUpperCase())
|
||||||
|
+'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL))
|
||||||
|
+'&'+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters));
|
||||||
|
}
|
||||||
|
,
|
||||||
|
normalizeUrl: function normalizeUrl(url) {
|
||||||
|
var uri = OAuth.SignatureMethod.parseUri(url);
|
||||||
|
var scheme = uri.protocol.toLowerCase();
|
||||||
|
var authority = uri.authority.toLowerCase();
|
||||||
|
var dropPort = (scheme == "http" && uri.port == 80)
|
||||||
|
|| (scheme == "https" && uri.port == 443);
|
||||||
|
if (dropPort) {
|
||||||
|
// find the last : in the authority
|
||||||
|
var index = authority.lastIndexOf(":");
|
||||||
|
if (index >= 0) {
|
||||||
|
authority = authority.substring(0, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var path = uri.path;
|
||||||
|
if (!path) {
|
||||||
|
path = "/"; // conforms to RFC 2616 section 3.2.2
|
||||||
|
}
|
||||||
|
// we know that there is no query and no fragment here.
|
||||||
|
return scheme + "://" + authority + path;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
parseUri: function parseUri (str) {
|
||||||
|
/* This function was adapted from parseUri 1.2.1
|
||||||
|
http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js
|
||||||
|
*/
|
||||||
|
var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
|
||||||
|
parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }};
|
||||||
|
var m = o.parser.strict.exec(str);
|
||||||
|
var uri = {};
|
||||||
|
var i = 14;
|
||||||
|
while (i--) uri[o.key[i]] = m[i] || "";
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
,
|
||||||
|
normalizeParameters: function normalizeParameters(parameters) {
|
||||||
|
if (parameters == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
var list = OAuth.getParameterList(parameters);
|
||||||
|
var sortable = [];
|
||||||
|
for (var p = 0; p < list.length; ++p) {
|
||||||
|
var nvp = list[p];
|
||||||
|
if (nvp[0] != "oauth_signature") {
|
||||||
|
sortable.push([ OAuth.percentEncode(nvp[0])
|
||||||
|
+ " " // because it comes before any character that can appear in a percentEncoded string.
|
||||||
|
+ OAuth.percentEncode(nvp[1])
|
||||||
|
, nvp]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sortable.sort(function(a,b) {
|
||||||
|
if (a[0] < b[0]) return -1;
|
||||||
|
if (a[0] > b[0]) return 1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
var sorted = [];
|
||||||
|
for (var s = 0; s < sortable.length; ++s) {
|
||||||
|
sorted.push(sortable[s][1]);
|
||||||
|
}
|
||||||
|
return OAuth.formEncode(sorted);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"],
|
||||||
|
OAuth.SignatureMethod.makeSubclass(
|
||||||
|
function getSignature(baseString) {
|
||||||
|
return this.key;
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"],
|
||||||
|
OAuth.SignatureMethod.makeSubclass(
|
||||||
|
function getSignature(baseString) {
|
||||||
|
b64pad = '=';
|
||||||
|
var signature = b64_hmac_sha1(this.key, baseString);
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
try {
|
||||||
|
OAuth.correctTimestampFromSrc();
|
||||||
|
} catch(e) {
|
||||||
|
}
|
202
app/source/sha1.js
Normal file
202
app/source/sha1.js
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
|
||||||
|
* in FIPS PUB 180-1
|
||||||
|
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
|
||||||
|
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||||
|
* Distributed under the BSD License
|
||||||
|
* See http://pajhome.org.uk/crypt/md5 for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Configurable variables. You may need to tweak these to be compatible with
|
||||||
|
* the server-side, but the defaults work in most cases.
|
||||||
|
*/
|
||||||
|
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||||
|
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||||
|
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are the functions you'll usually want to call
|
||||||
|
* They take string arguments and return either hex or base-64 encoded strings
|
||||||
|
*/
|
||||||
|
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
|
||||||
|
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
|
||||||
|
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
|
||||||
|
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
|
||||||
|
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
|
||||||
|
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform a simple self-test to see if the VM is working
|
||||||
|
*/
|
||||||
|
function sha1_vm_test()
|
||||||
|
{
|
||||||
|
return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the SHA-1 of an array of big-endian words, and a bit length
|
||||||
|
*/
|
||||||
|
function core_sha1(x, len)
|
||||||
|
{
|
||||||
|
/* append padding */
|
||||||
|
x[len >> 5] |= 0x80 << (24 - len % 32);
|
||||||
|
x[((len + 64 >> 9) << 4) + 15] = len;
|
||||||
|
|
||||||
|
var w = Array(80);
|
||||||
|
var a = 1732584193;
|
||||||
|
var b = -271733879;
|
||||||
|
var c = -1732584194;
|
||||||
|
var d = 271733878;
|
||||||
|
var e = -1009589776;
|
||||||
|
|
||||||
|
for(var i = 0; i < x.length; i += 16)
|
||||||
|
{
|
||||||
|
var olda = a;
|
||||||
|
var oldb = b;
|
||||||
|
var oldc = c;
|
||||||
|
var oldd = d;
|
||||||
|
var olde = e;
|
||||||
|
|
||||||
|
for(var j = 0; j < 80; j++)
|
||||||
|
{
|
||||||
|
if(j < 16) w[j] = x[i + j];
|
||||||
|
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
|
||||||
|
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
|
||||||
|
safe_add(safe_add(e, w[j]), sha1_kt(j)));
|
||||||
|
e = d;
|
||||||
|
d = c;
|
||||||
|
c = rol(b, 30);
|
||||||
|
b = a;
|
||||||
|
a = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = safe_add(a, olda);
|
||||||
|
b = safe_add(b, oldb);
|
||||||
|
c = safe_add(c, oldc);
|
||||||
|
d = safe_add(d, oldd);
|
||||||
|
e = safe_add(e, olde);
|
||||||
|
}
|
||||||
|
return Array(a, b, c, d, e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform the appropriate triplet combination function for the current
|
||||||
|
* iteration
|
||||||
|
*/
|
||||||
|
function sha1_ft(t, b, c, d)
|
||||||
|
{
|
||||||
|
if(t < 20) return (b & c) | ((~b) & d);
|
||||||
|
if(t < 40) return b ^ c ^ d;
|
||||||
|
if(t < 60) return (b & c) | (b & d) | (c & d);
|
||||||
|
return b ^ c ^ d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the appropriate additive constant for the current iteration
|
||||||
|
*/
|
||||||
|
function sha1_kt(t)
|
||||||
|
{
|
||||||
|
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
|
||||||
|
(t < 60) ? -1894007588 : -899497514;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate the HMAC-SHA1 of a key and some data
|
||||||
|
*/
|
||||||
|
function core_hmac_sha1(key, data)
|
||||||
|
{
|
||||||
|
var bkey = str2binb(key);
|
||||||
|
if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
|
||||||
|
|
||||||
|
var ipad = Array(16), opad = Array(16);
|
||||||
|
for(var i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
ipad[i] = bkey[i] ^ 0x36363636;
|
||||||
|
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
|
||||||
|
return core_sha1(opad.concat(hash), 512 + 160);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||||
|
* to work around bugs in some JS interpreters.
|
||||||
|
*/
|
||||||
|
function safe_add(x, y)
|
||||||
|
{
|
||||||
|
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||||
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||||
|
return (msw << 16) | (lsw & 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bitwise rotate a 32-bit number to the left.
|
||||||
|
*/
|
||||||
|
function rol(num, cnt)
|
||||||
|
{
|
||||||
|
return (num << cnt) | (num >>> (32 - cnt));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an 8-bit or 16-bit string to an array of big-endian words
|
||||||
|
* In 8-bit function, characters >255 have their hi-byte silently ignored.
|
||||||
|
*/
|
||||||
|
function str2binb(str)
|
||||||
|
{
|
||||||
|
var bin = Array();
|
||||||
|
var mask = (1 << chrsz) - 1;
|
||||||
|
for(var i = 0; i < str.length * chrsz; i += chrsz)
|
||||||
|
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an array of big-endian words to a string
|
||||||
|
*/
|
||||||
|
function binb2str(bin)
|
||||||
|
{
|
||||||
|
var str = "";
|
||||||
|
var mask = (1 << chrsz) - 1;
|
||||||
|
for(var i = 0; i < bin.length * 32; i += chrsz)
|
||||||
|
str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an array of big-endian words to a hex string.
|
||||||
|
*/
|
||||||
|
function binb2hex(binarray)
|
||||||
|
{
|
||||||
|
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||||
|
var str = "";
|
||||||
|
for(var i = 0; i < binarray.length * 4; i++)
|
||||||
|
{
|
||||||
|
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
|
||||||
|
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert an array of big-endian words to a base-64 string
|
||||||
|
*/
|
||||||
|
function binb2b64(binarray)
|
||||||
|
{
|
||||||
|
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
var str = "";
|
||||||
|
for(var i = 0; i < binarray.length * 4; i += 3)
|
||||||
|
{
|
||||||
|
var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
|
||||||
|
| (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
|
||||||
|
| ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
|
||||||
|
for(var j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
|
||||||
|
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
35
app/source/styles.css
Normal file
35
app/source/styles.css
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/*Your CSS here*/
|
||||||
|
/* TODO: retheme this whole bloddy mess */
|
||||||
|
.todo-header {
|
||||||
|
background-color: #448820;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.todo-view-title {
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
.box-center {
|
||||||
|
width: 320px;
|
||||||
|
margin: 23px auto 0;
|
||||||
|
}
|
||||||
|
.left-col {
|
||||||
|
margin-right: 7px;
|
||||||
|
color: #777777;
|
||||||
|
}
|
||||||
|
.mid-col {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.pri-a { color: yellow; }
|
||||||
|
.pri-b { color: green; }
|
||||||
|
.pri-c { color: slateblue; }
|
||||||
|
.dialog-fix {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
.completed-item {
|
||||||
|
color: #aaaaaa;
|
||||||
|
}
|
||||||
|
.selected-item {
|
||||||
|
background: grey;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.ver-history { font-size: 16px; }
|
||||||
|
.version { font-weight: bold;}
|
10
pkg/packageinfo.json
Normal file
10
pkg/packageinfo.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"id": "com.monkeystew.todotxtenyo.beta",
|
||||||
|
"package_format_version": 2,
|
||||||
|
"loc_name": "Todo.txt Enyo",
|
||||||
|
"icon": "icon.png",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"vendor": "Monkeystew",
|
||||||
|
"app": "com.monkeystew.todotxtenyo.beta",
|
||||||
|
"services": ["com.monkeystew.todotxtenyo.beta.service"]
|
||||||
|
}
|
12
srv/MakeDirAssistant.js
Normal file
12
srv/MakeDirAssistant.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
var MakeDirAssistant = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
MakeDirAssistant.prototype.run = function(future) {
|
||||||
|
var fs = IMPORTS.require("fs");
|
||||||
|
|
||||||
|
var path = this.controller.args.path;
|
||||||
|
|
||||||
|
fs.mkdir(path, 0755, function(err) {
|
||||||
|
future.result = { error: err };
|
||||||
|
});
|
||||||
|
}
|
10
srv/ReadDirAssistant.js
Normal file
10
srv/ReadDirAssistant.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
var ReadDirAssistant = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadDirAssistant.prototype.run = function(future) {
|
||||||
|
var fs = IMPORTS.require("fs");
|
||||||
|
|
||||||
|
var path = this.controller.args.path;
|
||||||
|
|
||||||
|
fs.readdir(path, function(err, files) { future.result = { path: path, files: files }; });
|
||||||
|
}
|
16
srv/ReadFileAssistant.js
Normal file
16
srv/ReadFileAssistant.js
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
var ReadFileAssistant = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadFileAssistant.prototype.run = function(future) {
|
||||||
|
var fs = IMPORTS.require("fs");
|
||||||
|
|
||||||
|
var path = this.controller.args.path
|
||||||
|
|
||||||
|
fs.readFile(path, 'utf8', function(err,data) {
|
||||||
|
//future.result = { path: path, content: data };
|
||||||
|
setTimeout(function () {
|
||||||
|
console.log("delay test...");
|
||||||
|
future.result = { path: path, content: data };
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
11
srv/WriteFileAssistant.js
Normal file
11
srv/WriteFileAssistant.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
var WriteFileAssistant = function() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteFileAssistant.prototype.run = function(future) {
|
||||||
|
var fs = IMPORTS.require("fs");
|
||||||
|
|
||||||
|
var path = this.controller.args.path
|
||||||
|
var content = this.controller.args.content
|
||||||
|
|
||||||
|
fs.writeFile(path, content, 'utf8', function(err) { future.result = { path: path, bytes: content.length, error: err }; });
|
||||||
|
}
|
3
srv/framework_config.json
Normal file
3
srv/framework_config.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"logLevel": 99
|
||||||
|
}
|
32
srv/services.json
Normal file
32
srv/services.json
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"id": "com.monkeystew.todotxtenyo.beta.service",
|
||||||
|
"description": "FileIO Service",
|
||||||
|
"services": [
|
||||||
|
{
|
||||||
|
"name": "com.monkeystew.todotxtenyo.beta.service",
|
||||||
|
"description": "FileIO Service",
|
||||||
|
"commands": [
|
||||||
|
{
|
||||||
|
"name": "readdir",
|
||||||
|
"assistant": "ReadDirAssistant",
|
||||||
|
"public": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "makedir",
|
||||||
|
"assistant": "MakeDirAssistant",
|
||||||
|
"public": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "writefile",
|
||||||
|
"assistant": "WriteFileAssistant",
|
||||||
|
"public": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "readfile",
|
||||||
|
"assistant": "ReadFileAssistant",
|
||||||
|
"public": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
7
srv/sources.json
Normal file
7
srv/sources.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[
|
||||||
|
{ "library": { "name": "foundations", "version": "1.0" } },
|
||||||
|
{ "source": "ReadDirAssistant.js" },
|
||||||
|
{ "source": "MakeDirAssistant.js" },
|
||||||
|
{ "source": "WriteFileAssistant.js" },
|
||||||
|
{ "source": "ReadFileAssistant.js" }
|
||||||
|
]
|
Loading…
Reference in a new issue