├── .gitattributes
├── app
├── .buildignore
├── robots.txt
├── favicon.ico
├── images
│ └── yeoman.png
├── scripts
│ ├── controllers
│ │ ├── auth.js
│ │ └── main.js
│ ├── app.js
│ └── dropbox-client-example.js
├── views
│ └── main.html
├── index.html
├── 404.html
├── styles
│ └── main.scss
└── .htaccess
├── .jshintignore
├── .bowerrc
├── .gitignore
├── .travis.yml
├── test
├── runner.html
├── spec
│ └── controllers
│ │ └── main.js
└── .jshintrc
├── .jshintrc
├── .editorconfig
├── bower.json
├── karma-e2e.conf.js
├── package.json
├── karma.conf.js
├── README.md
└── Gruntfile.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/app/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | app/scripts/dropbox-client-example.js
--------------------------------------------------------------------------------
/app/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "app/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .tmp
4 | .sass-cache
5 | app/bower_components
6 |
--------------------------------------------------------------------------------
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thrashr888/dropbox-taskpaper-editor/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/app/images/yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thrashr888/dropbox-taskpaper-editor/HEAD/app/images/yeoman.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.8'
4 | - '0.10'
5 | before_script:
6 | - 'npm install -g bower grunt-cli'
7 | - 'bower install'
8 |
--------------------------------------------------------------------------------
/test/runner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | End2end Test Runner
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "angular": false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('dropboxTaskpaperApp'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropbox-taskpaper",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": "1.2.6",
6 | "json3": "~3.2.6",
7 | "es5-shim": "~2.1.0",
8 | "jquery": "~1.10.2",
9 | "sass-bootstrap": "~3.0.2",
10 | "angular-animate": "1.2.6",
11 | "angular-resource": "1.2.6",
12 | "angular-cookies": "1.2.6",
13 | "angular-sanitize": "1.2.6",
14 | "angular-route": "1.2.6",
15 | "ngDropbox": "christiansmith/ngDropbox",
16 | "jsTaskPaper": "https://github.com/dhilowitz/jsTaskPaper.git"
17 | },
18 | "devDependencies": {
19 | "angular-mocks": "1.2.6",
20 | "angular-scenario": "1.2.6"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/scripts/controllers/auth.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('dropboxTaskpaperApp')
4 | .controller('AuthCtrl', function (Dropbox, DropboxLocalStorageOAuthKey, $rootScope, $location) {
5 | // $timeout(function() {
6 | // dropbox.authenticate();
7 | // }, 200);
8 |
9 | if($rootScope.checkAuthenticated() && Dropbox.isAuthenticated()) {
10 | $location.path('/').replace();
11 | return;
12 | }
13 |
14 | Dropbox.authenticate().then(function (oauth) {
15 | if(oauth.uid){
16 | localStorage[DropboxLocalStorageOAuthKey] = angular.toJson(oauth);
17 | $rootScope.uid = oauth.uid;
18 | $rootScope.isAuthenticated = true;
19 | $location.path('/').replace();
20 | }
21 | });
22 |
23 | });
24 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "after": false,
23 | "afterEach": false,
24 | "angular": false,
25 | "before": false,
26 | "beforeEach": false,
27 | "browser": false,
28 | "describe": false,
29 | "expect": false,
30 | "inject": false,
31 | "it": false,
32 | "jasmine": false,
33 | "spyOn": false
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/karma-e2e.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['ng-scenario'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'test/e2e/**/*.js'
15 | ],
16 |
17 | // list of files / patterns to exclude
18 | exclude: [],
19 |
20 | // web server port
21 | port: 8080,
22 |
23 | // level of logging
24 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
25 | logLevel: config.LOG_INFO,
26 |
27 |
28 | // enable / disable watching file and executing tests whenever any file changes
29 | autoWatch: false,
30 |
31 |
32 | // Start these browsers, currently available:
33 | // - Chrome
34 | // - ChromeCanary
35 | // - Firefox
36 | // - Opera
37 | // - Safari (only Mac)
38 | // - PhantomJS
39 | // - IE (only Windows)
40 | browsers: ['Chrome'],
41 |
42 |
43 | // Continuous Integration mode
44 | // if true, it capture browsers, run tests and exit
45 | singleRun: false
46 |
47 | // Uncomment the following lines if you are using grunt's server to run the tests
48 | // proxies: {
49 | // '/': 'http://localhost:9000/'
50 | // },
51 | // URL root prevent conflicts with the site root
52 | // urlRoot: '_karma_'
53 | });
54 | };
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dropboxtaskpaper",
3 | "version": "0.0.0",
4 | "dependencies": {},
5 | "devDependencies": {
6 | "grunt": "~0.4.1",
7 | "grunt-autoprefixer": "~0.4.0",
8 | "grunt-bower-install": "~0.7.0",
9 | "grunt-concurrent": "~0.4.1",
10 | "grunt-contrib-clean": "~0.5.0",
11 | "grunt-contrib-coffee": "~0.7.0",
12 | "grunt-contrib-compass": "~0.6.0",
13 | "grunt-contrib-concat": "~0.3.0",
14 | "grunt-contrib-connect": "~0.5.0",
15 | "grunt-contrib-copy": "~0.4.1",
16 | "grunt-contrib-cssmin": "~0.7.0",
17 | "grunt-contrib-htmlmin": "~0.1.3",
18 | "grunt-contrib-imagemin": "~0.3.0",
19 | "grunt-contrib-jshint": "~0.7.1",
20 | "grunt-contrib-uglify": "~0.2.0",
21 | "grunt-contrib-watch": "~0.5.2",
22 | "grunt-google-cdn": "~0.2.0",
23 | "grunt-newer": "~0.5.4",
24 | "grunt-ngmin": "~0.0.2",
25 | "grunt-rev": "~0.1.0",
26 | "grunt-svgmin": "~0.2.0",
27 | "grunt-usemin": "~2.0.0",
28 | "jshint-stylish": "~0.1.3",
29 | "load-grunt-tasks": "~0.2.0",
30 | "time-grunt": "~0.2.1",
31 | "karma-ng-scenario": "~0.1.0",
32 | "grunt-karma": "~0.6.2",
33 | "karma-script-launcher": "~0.1.0",
34 | "karma-chrome-launcher": "~0.1.2",
35 | "karma-firefox-launcher": "~0.1.3",
36 | "karma-html2js-preprocessor": "~0.1.0",
37 | "karma-jasmine": "~0.1.5",
38 | "requirejs": "~2.1.9",
39 | "karma-requirejs": "~0.2.1",
40 | "karma-coffee-preprocessor": "~0.1.2",
41 | "karma-phantomjs-launcher": "~0.1.1",
42 | "karma": "~0.10.9",
43 | "karma-ng-html2js-preprocessor": "~0.1.0"
44 | },
45 | "engines": {
46 | "node": ">=0.8.0"
47 | },
48 | "scripts": {
49 | "test": "grunt test"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'app/bower_components/angular/angular.js',
15 | 'app/bower_components/angular-mocks/angular-mocks.js',
16 | 'app/bower_components/angular-resource/angular-resource.js',
17 | 'app/bower_components/angular-cookies/angular-cookies.js',
18 | 'app/bower_components/angular-sanitize/angular-sanitize.js',
19 | 'app/bower_components/angular-route/angular-route.js',
20 | 'app/scripts/*.js',
21 | 'app/scripts/**/*.js',
22 | 'test/mock/**/*.js',
23 | 'test/spec/**/*.js'
24 | ],
25 |
26 | // list of files / patterns to exclude
27 | exclude: [],
28 |
29 | // web server port
30 | port: 8080,
31 |
32 | // level of logging
33 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
34 | logLevel: config.LOG_INFO,
35 |
36 |
37 | // enable / disable watching file and executing tests whenever any file changes
38 | autoWatch: false,
39 |
40 |
41 | // Start these browsers, currently available:
42 | // - Chrome
43 | // - ChromeCanary
44 | // - Firefox
45 | // - Opera
46 | // - Safari (only Mac)
47 | // - PhantomJS
48 | // - IE (only Windows)
49 | browsers: ['Chrome'],
50 |
51 |
52 | // Continuous Integration mode
53 | // if true, it capture browsers, run tests and exit
54 | singleRun: false
55 | });
56 | };
57 |
--------------------------------------------------------------------------------
/app/views/main.html:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
Dropbox Taskpaper Editor
10 |
11 | The Web-based Dropbox-synced Taskpaper Document Editor
12 |
13 |
Log In
14 |
15 |
16 |
17 |
{{message}}
18 |
19 |
20 |
Taskpaper Documents
21 |
22 | - Loading list of documents...
23 | - {{file}}
24 |
25 |
26 |
27 |
28 |
{{currentFile}} ⚠ Conflict Loading document... ⟳
29 |
30 |
31 |
32 |
Editor
33 |
34 |
35 |
36 |
40 |
41 |
42 |
43 |
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Dropbox Taskpaper Editor
2 | ========================
3 |
4 | "It's certainly an impressive piece of work." — [Macdrifter](http://macdrifter.com/2014/02/the-taskpaper-rd-notebook.html)
5 |
6 | This app allows you to edit your Taskpaper documents stored in Dropbox directly from the browser. It requires "Full Dropbox" permissions in order to edit the Taskpaper files.
7 |
8 | This is a client-side app using AngularJS and Dropbox APIs. It does not require a server to run. You should be able to deploy directly to Dropbox, S3, Google Drive or other static file hosts. If so, you'll need to setup your own Dropbox App [https://www.dropbox.com/developers/apps] and add the ngDropbox callback url to the whitelist. For example: `http://localhost:9000/bower_components/ngDropbox/callback.html`. Then add your appKey in `app/scripts/app.js`. Outside of localhost, the Dropbox authentication only works with https.
9 |
10 | Demo site deployed on S3 and CloudFront: [Dropbox Taskpaper Editor](https://dgiu4ye9xtr28.cloudfront.net/)
11 |
12 | Install & Run
13 | -------------
14 |
15 | > npm install -g grunt-cli bower
16 | > npm install
17 | > bower install
18 | > grunt serve
19 |
20 | Thanks
21 | ------
22 |
23 | This app uses:
24 |
25 | - ngDropbox
26 | - JSTaskPaper
27 | - AngularJS
28 | - Yeoman
29 | - Grunt
30 | - Bootstrap
31 | - jQuery
32 |
33 | License
34 | -------
35 |
36 | Copyright (C) 2013-2014 Paul Thrasher
37 |
38 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
39 |
40 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
41 |
42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 |
--------------------------------------------------------------------------------
/app/scripts/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('dropboxTaskpaperApp', [
4 | 'ngCookies',
5 | 'ngResource',
6 | 'ngSanitize',
7 | 'ngRoute',
8 | 'ngAnimate',
9 | 'dropbox'
10 | ])
11 | .config(function ($routeProvider, $locationProvider) {
12 | $locationProvider.html5Mode(true);
13 | $routeProvider
14 | .when('/', {
15 | templateUrl: 'views/main.html',
16 | controller: 'MainCtrl'
17 | })
18 | .when('/auth', {
19 | templateUrl: 'views/main.html',
20 | controller: 'AuthCtrl'
21 | })
22 | .otherwise({
23 | redirectTo: '/'
24 | });
25 | })
26 | .value('DropboxClientId', '563mc3wfk1qd68q')
27 | .value('DropboxRedirectUri', 'https://' + window.location.host + '/bower_components/ngDropbox/callback.html')
28 | .value('DropboxLocalStorageOAuthKey', 'ngDropbox.oauth')
29 | .config(function (DropboxProvider) {
30 | DropboxProvider.config('563mc3wfk1qd68q',
31 | 'https://' + window.location.host + '/bower_components/ngDropbox/callback.html');
32 | })
33 | // .constant('DROPBOX_APP_KEY', '563mc3wfk1qd68q')
34 | // .service('dropbox', function (DROPBOX_APP_KEY, $rootScope) {
35 | // // Exposed for easy access in the browser console.
36 | // var dropbox = new Dropbox.Client({
37 | // key: DROPBOX_APP_KEY
38 | // });
39 |
40 | // dropbox.authenticate({
41 | // interactive: false
42 | // }, function(error) {
43 | // if (error) {
44 | // console.log('Authentication error', error);
45 | // } else {
46 | // $rootScope.authenticated = true;
47 | // }
48 | // });
49 |
50 | // dropbox.getAccountInfo({}, function(info){
51 | // console.log(info);
52 | // })
53 |
54 | // if (dropbox.isAuthenticated()) {
55 | // $rootScope.authenticated = true;
56 | // }
57 | // return dropbox;
58 | // })
59 | // .service('datastore', function (dropbox, $q, $rootScope) {
60 | // var promise = $q.defer();
61 |
62 | // // console.log(dropbox.getDatastoreManager());
63 | // if (dropbox.isAuthenticated()) {
64 | // // dropbox is authenticated. Display UI.
65 | // console.log(dropbox.getDatastoreManager());
66 | // dropbox.getDatastoreManager().openDefaultDatastore(function(error, datastore) {
67 | // if (error) {
68 | // console.error('Error opening default datastore', error);
69 | // }
70 |
71 | // promise.resolve(datastore);
72 | // });
73 | // } else {
74 | // promise.reject();
75 | // }
76 | // return promise.promise;
77 | // })
78 | .run(function ($rootScope, Dropbox, DropboxLocalStorageOAuthKey) {
79 | $rootScope.isAuthenticated = false;
80 |
81 | $rootScope.checkAuthenticated = function () {
82 | // console.log(localStorage);
83 | if (localStorage[DropboxLocalStorageOAuthKey]) {
84 | var oauth = angular.fromJson(localStorage[DropboxLocalStorageOAuthKey]);
85 | if(oauth) {
86 |
87 | }
88 | Dropbox.setCredentials(oauth);
89 | $rootScope.uid = oauth.uid;
90 | $rootScope.isAuthenticated = true;
91 | return true;
92 | } else {
93 | return false;
94 | }
95 | };
96 |
97 | $rootScope.checkAuthenticated();
98 |
99 | })
100 | ;
101 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Dropbox Taskpaper Editor
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
41 |
42 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/app/scripts/dropbox-client-example.js:
--------------------------------------------------------------------------------
1 | // Insert your Dropbox app key here:
2 | var DROPBOX_APP_KEY = '563mc3wfk1qd68q';
3 |
4 | // Exposed for easy access in the browser console.
5 | var client = new Dropbox.Client({
6 | key: DROPBOX_APP_KEY
7 | });
8 | var taskTable;
9 |
10 | $(function() {
11 | // Insert a new task record into the table.
12 | function insertTask(text) {
13 | taskTable.insert({
14 | taskname: text,
15 | created: new Date(),
16 | completed: false
17 | });
18 | }
19 |
20 | // updateList will be called every time the table changes.
21 | function updateList() {
22 | $('#tasks').empty();
23 |
24 | var records = taskTable.query();
25 |
26 | // Sort by creation time.
27 | records.sort(function(taskA, taskB) {
28 | if (taskA.get('created') < taskB.get('created')) return -1;
29 | if (taskA.get('created') > taskB.get('created')) return 1;
30 | return 0;
31 | });
32 |
33 | // Add an item to the list for each task.
34 | for (var i = 0; i < records.length; i++) {
35 | var record = records[i];
36 | $('#tasks').append(
37 | renderTask(record.getId(), record.get('completed'), record.get('taskname')));
38 | }
39 |
40 | addListeners();
41 | $('#newTask').focus();
42 | }
43 |
44 | // The login button will start the authentication process.
45 | $('#loginButton').click(function(e) {
46 | e.preventDefault();
47 | // This will redirect the browser to OAuth login.
48 | client.authenticate();
49 | });
50 |
51 | // Try to finish OAuth authorization.
52 | client.authenticate({
53 | interactive: false
54 | }, function(error) {
55 | if (error) {
56 | alert('Authentication error: ' + error);
57 | }
58 | });
59 |
60 | if (client.isAuthenticated()) {
61 | // Client is authenticated. Display UI.
62 | $('#loginButton').hide();
63 | $('#main').show();
64 |
65 | client.getDatastoreManager().openDefaultDatastore(function(error, datastore) {
66 | if (error) {
67 | alert('Error opening default datastore: ' + error);
68 | }
69 |
70 | taskTable = datastore.getTable('tasks');
71 |
72 | // Populate the initial task list.
73 | updateList();
74 |
75 | // Ensure that future changes update the list.
76 | datastore.recordsChanged.addListener(updateList);
77 | });
78 | }
79 |
80 | // Set the completed status of a task with the given ID.
81 | function setCompleted(id, completed) {
82 | taskTable.get(id).set('completed', completed);
83 | }
84 |
85 | // Delete the record with a given ID.
86 | function deleteRecord(id) {
87 | taskTable.get(id).deleteRecord();
88 | }
89 |
90 | // Render the HTML for a single task.
91 | function renderTask(id, completed, text) {
92 | return $('').attr('id', id).append(
93 | $('