├── .gitignore
├── Gruntfile.js
├── README.md
├── app
├── cloud-explorer.html
├── demo.html
├── images
│ └── default
│ │ ├── breadcrumb.png
│ │ ├── btn-close.png
│ │ ├── btn-icon-items.png
│ │ ├── btn-item-delete.png
│ │ ├── btn-item-rename.png
│ │ ├── btn-item-select.png
│ │ ├── btn-list-items.png
│ │ ├── btn-new-folder.png
│ │ ├── btn-parent-folder.png
│ │ ├── btn-sort.png
│ │ ├── ce-header.png
│ │ ├── dropzone.png
│ │ ├── home-dropbox.png
│ │ ├── home-ftp.png
│ │ ├── home-github.png
│ │ ├── home-open-pages.png
│ │ ├── home-webdav.png
│ │ ├── home-www.png
│ │ ├── icon-file.png
│ │ ├── icon-folder.png
│ │ ├── icon-image.png
│ │ ├── icon-sound.png
│ │ ├── icon-video.png
│ │ ├── srv-dropbox.png
│ │ ├── srv-ftp.png
│ │ ├── srv-github.png
│ │ ├── srv-open-pages.png
│ │ ├── srv-state-connected.png
│ │ ├── srv-state-disabled.png
│ │ ├── srv-state-disconnected.png
│ │ ├── srv-webdav.png
│ │ └── srv-www.png
├── index.html
├── oauth-cb.html
├── scripts
│ └── .gitignore
└── styles
│ ├── base.scss
│ ├── cloud-explorer.scss
│ ├── default
│ ├── README.md
│ └── theme.scss
│ └── platforms.scss
├── build.hxml
├── package.json
├── screenshot01.png
├── screenshot02.png
├── screenshot03.png
├── screenshot04.png
├── screenshot05.png
├── screenshot06.png
├── server
└── unifile.js
└── src
└── ce
├── api
└── CloudExplorer.hx
├── core
├── Controller.hx
├── config
│ └── Config.hx
├── ctrl
│ └── ErrorCtrl.hx
├── model
│ ├── CEBlob.hx
│ ├── CEError.hx
│ ├── DisplayMode.hx
│ ├── Location.hx
│ ├── Mode.hx
│ ├── Service.hx
│ ├── SortField.hx
│ ├── SortOrder.hx
│ ├── State.hx
│ ├── api
│ │ ├── ExportOptions.hx
│ │ ├── PickOptions.hx
│ │ ├── ReadOptions.hx
│ │ └── WriteOptions.hx
│ ├── oauth
│ │ └── OAuthResult.hx
│ └── unifile
│ │ ├── Account.hx
│ │ ├── ConnectResult.hx
│ │ ├── File.hx
│ │ ├── LoginResult.hx
│ │ ├── LogoutResult.hx
│ │ ├── Service.hx
│ │ ├── UnifileError.hx
│ │ └── UploadResult.hx
├── parser
│ ├── json
│ │ └── Json2Primitive.hx
│ ├── oauth
│ │ └── Str2OAuthResult.hx
│ └── unifile
│ │ ├── Json2Account.hx
│ │ ├── Json2ConnectResult.hx
│ │ ├── Json2File.hx
│ │ ├── Json2LoginResult.hx
│ │ ├── Json2LogoutResult.hx
│ │ ├── Json2Service.hx
│ │ ├── Json2UnifileError.hx
│ │ └── Json2UploadResult.hx
├── service
│ └── UnifileSrv.hx
└── view
│ ├── AlertPopup.hx
│ ├── Application.hx
│ ├── AuthPopup.hx
│ ├── Breadcrumb.hx
│ ├── Button.hx
│ ├── DropZone.hx
│ ├── Export.hx
│ ├── FileBrowser.hx
│ └── Home.hx
└── util
├── FileTools.hx
├── HtmlTools.hx
└── OptionTools.hx
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bin/web
3 | bin/native
4 | .tmp
5 | .sass-cache
6 | bin/web/app/bower_components
7 | cloud-explorer.swf
8 | cloud-explorer.js
9 | test.swf
10 | test.js
11 | *.js.map
12 | *.sublime-project
13 | *.sublime-workspace
14 | *.DS_Store
15 | export/
16 | all-tests.js
17 | all-tests.swf
18 | all-tests.js.map
19 | Thumbs.db
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2013-11-01 using generator-webapp 0.4.3
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 | // show elapsed time at the end
12 | require('time-grunt')(grunt);
13 | // load all grunt tasks
14 | require('load-grunt-tasks')(grunt);
15 |
16 | grunt.initConfig({
17 | // configurable paths
18 | yeoman: {
19 | app: 'app',
20 | dist: 'bin/web',
21 | native: 'bin/native',
22 | heroku: '../heroku'
23 | },
24 | watch: {
25 | compass: {
26 | files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
27 | tasks: ['compass:server', 'autoprefixer']
28 | },
29 | styles: {
30 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
31 | tasks: ['copy:styles', 'autoprefixer']
32 | },
33 | livereload: {
34 | options: {
35 | livereload: '<%= connect.options.livereload %>'
36 | },
37 | files: [
38 | '<%= yeoman.app %>/*.html',
39 | '<%= yeoman.app %>/*.swf',
40 | '.tmp/styles/{,*/}*.css',
41 | '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js',
42 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
43 | ]
44 | }
45 | },
46 | connect: {
47 | options: {
48 | port: 9000,
49 | livereload : false,
50 | /*livereload: 35729,*/
51 | // change this to '0.0.0.0' to access the server from outside
52 | hostname: 'localhost'
53 | },
54 | livereload: {
55 | options: {
56 | livereload : false,
57 | open: true,
58 | base: [
59 | '.tmp',
60 | '<%= yeoman.app %>'
61 | ]
62 | }
63 | },
64 | /*test: {
65 | options: {
66 | base: [
67 | '.tmp',
68 | 'test',
69 | '<%= yeoman.app %>'
70 | ]
71 | }
72 | },*/
73 | test : {
74 | options: {
75 | open : true,
76 | keepalive:true,
77 | base: ['test/bin']
78 | }
79 | },
80 | dist: {
81 | options: {
82 | open: true,
83 | base: '<%= yeoman.dist %>'
84 | }
85 | }
86 | },
87 | clean: {
88 | heroku: {
89 | options: { force: true },
90 | files: [{
91 | dot: true,
92 | src: [
93 | '.tmp',
94 | '<%= yeoman.heroku %>/app/*',
95 | '!<%= yeoman.heroku %>/app/.git*'
96 | ]
97 | }]
98 | },
99 | dist: {
100 | files: [{
101 | dot: true,
102 | src: [
103 | '.tmp',
104 | '<%= yeoman.dist %>/*',
105 | '!<%= yeoman.dist %>/.git*'
106 | ]
107 | }]
108 | },
109 | native : '<%= yeoman.native %>',
110 | server: '.tmp'
111 | },
112 | jshint: {
113 | options: {
114 | jshintrc: '.jshintrc'
115 | },
116 | all: [
117 | 'Gruntfile.js',
118 | '<%= yeoman.app %>/scripts/{,*/}*.js',
119 | '!<%= yeoman.app %>/scripts/vendor/*',
120 | 'test/spec/{,*/}*.js'
121 | ]
122 | },
123 | mocha: {
124 | all: {
125 | options: {
126 | run: true,
127 | urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
128 | }
129 | }
130 | },
131 | compass: {
132 | options: {
133 | sassDir: '<%= yeoman.app %>/styles',
134 | cssDir: '.tmp/styles',
135 | generatedImagesDir: '.tmp/images/generated',
136 | imagesDir: '<%= yeoman.app %>/images',
137 | javascriptsDir: '<%= yeoman.app %>/scripts',
138 | //importPath: '<%= yeoman.app %>/bower_components',
139 | httpImagesPath: '/images',
140 | httpGeneratedImagesPath: '/images/generated',
141 | relativeAssets: false,
142 | assetCacheBuster: false
143 | },
144 | dist: {
145 | options: {
146 | generatedImagesDir: '<%= yeoman.dist %>/images/generated'
147 | }
148 | },
149 | server: {
150 | options: {
151 | debugInfo: false
152 | }
153 | }
154 | },
155 | autoprefixer: {
156 | options: {
157 | browsers: ['last 1 version']
158 | },
159 | dist: {
160 | files: [{
161 | expand: true,
162 | cwd: '.tmp/styles/',
163 | src: '{,*/}*.css',
164 | dest: '.tmp/styles/'
165 | }]
166 | }
167 | },
168 | // not used since Uglify task does concat,
169 | // but still available if needed
170 | /*concat: {
171 | dist: {
172 |
173 | }
174 | },*/
175 | /*'bower-install': {
176 | app: {
177 | html: '<%= yeoman.app %>/cloud-explorer.html',
178 | ignorePath: '<%= yeoman.app %>/'
179 | }
180 | },*/
181 | // not enabled since usemin task does concat and uglify
182 | // check index.html to edit your build targets
183 | // enable this task if you prefer defining your build targets here
184 | /*uglify: {
185 | dist: {}
186 | },*/
187 | rev: {
188 | dist: {
189 | files: {
190 | src: [
191 | '<%= yeoman.dist %>/scripts/{,*/}*.js',
192 | '<%= yeoman.dist %>/styles/{,*/}*.css',
193 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}'
194 | ]
195 | }
196 | }
197 | },
198 | useminPrepare: {
199 | options: {
200 | dest: '<%= yeoman.dist %>'
201 | },
202 | html: '<%= yeoman.app %>/cloud-explorer.html'
203 | },
204 | usemin: {
205 | options: {
206 | dirs: ['<%= yeoman.dist %>']
207 | },
208 | html: ['<%= yeoman.dist %>/{,*/}*.html'],
209 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
210 | },
211 | // imagemin: {
212 | // dist: {
213 | // files: [{
214 | // expand: true,
215 | // cwd: '<%= yeoman.app %>/images',
216 | // src: '{,*/}*.{png,jpg,jpeg}',
217 | // dest: '<%= yeoman.dist %>/images'
218 | // }]
219 | // }
220 | // },
221 | svgmin: {
222 | dist: {
223 | files: [{
224 | expand: true,
225 | cwd: '<%= yeoman.app %>/images',
226 | src: '{,*/}*.svg',
227 | dest: '<%= yeoman.dist %>/images'
228 | }]
229 | }
230 | },
231 | cssmin: {
232 | // This task is pre-configured if you do not wish to use Usemin
233 | // blocks for your CSS. By default, the Usemin block from your
234 | // `index.html` will take care of minification, e.g.
235 | //
236 | //
237 | //
238 | // dist: {
239 | // files: {
240 | // '<%= yeoman.dist %>/styles/main.css': [
241 | // '.tmp/styles/{,*/}*.css',
242 | // '<%= yeoman.app %>/styles/{,*/}*.css'
243 | // ]
244 | // }
245 | // }
246 | },
247 | htmlmin: {
248 | dist: {
249 | options: {
250 | /*removeCommentsFromCDATA: true,
251 | // https://github.com/yeoman/grunt-usemin/issues/44
252 | //collapseWhitespace: true,
253 | collapseBooleanAttributes: true,
254 | removeAttributeQuotes: true,
255 | removeRedundantAttributes: true,
256 | useShortDoctype: true,
257 | removeEmptyAttributes: true,
258 | removeOptionalTags: true*/
259 | removeOptionalTags : false
260 | },
261 | files: [{
262 | expand: true,
263 | cwd: '<%= yeoman.app %>',
264 | src: '*.html',
265 | dest: '<%= yeoman.dist %>'
266 | }]
267 | }
268 | },
269 | // Put files not handled in other tasks here
270 | copy: {
271 | heroku: {
272 | files: [{
273 | expand: true,
274 | dot: true,
275 | cwd: '<%= yeoman.app %>',
276 | dest: '<%= yeoman.heroku %>/app/',
277 | src: [
278 | 'scripts/*.js',
279 | 'cloud-explorer.html',
280 | 'oauth-cb.html',
281 | 'demo.html',
282 | '*.{ico,png,txt}',
283 | '.htaccess',
284 | 'images/{,*/}*.{webp,gif}',
285 | 'fonts/{,*/}*.*',
286 | 'cloud-explorer.swf',
287 | 'images/{,*/}*.{png,jpg,jpeg}'
288 | ]
289 | },
290 | // needed as we don't use cssmin anymore
291 | {
292 | expand: true,
293 | dot: true,
294 | cwd: '.tmp',
295 | dest: '<%= yeoman.heroku %>/app/',
296 | src: [
297 | 'styles/cloud-explorer.css'
298 | ]
299 | }]
300 | },
301 | dist: {
302 | files: [{
303 | expand: true,
304 | dot: true,
305 | cwd: '<%= yeoman.app %>',
306 | dest: '<%= yeoman.dist %>',
307 | src: [
308 | 'scripts/*.js',
309 | 'cloud-explorer.html',
310 | 'oauth-cb.html',
311 | '*.{ico,png,txt}',
312 | '.htaccess',
313 | 'images/{,*/}*.{webp,gif}',
314 | 'fonts/{,*/}*.*',
315 | 'cloud-explorer.swf',
316 | 'images/{,*/}*.{png,jpg,jpeg}'
317 | ]
318 | },
319 | // needed as we don't use cssmin anymore
320 | {
321 | expand: true,
322 | dot: true,
323 | cwd: '.tmp',
324 | dest: '<%= yeoman.dist %>',
325 | src: [
326 | 'styles/cloud-explorer.css'
327 | ]
328 | }]
329 | },
330 | styles: {
331 | expand: true,
332 | dot: true,
333 | cwd: '<%= yeoman.app %>/styles',
334 | dest: '.tmp/styles/',
335 | src: '{,*/}*.css'
336 | },
337 |
338 | native : {
339 | expand: true,
340 | dot: true,
341 | cwd: '<%= yeoman.dist %>',
342 | dest: '<%= yeoman.native %>',
343 | src: [
344 | 'cloud-explorer.html',
345 | 'images/{,*/}*.png',
346 | 'styles/{,*/}*.css'
347 | ]
348 | }
349 |
350 | },
351 | concurrent: {
352 | server: [
353 | 'compass',
354 | 'copy:styles'
355 | ],
356 | test: [
357 | 'copy:styles'
358 | ],
359 | dist: [
360 | 'compass',
361 | 'copy:styles',
362 | //'imagemin',
363 | 'svgmin'//,
364 | //'htmlmin' // don't htmlmin as it breaks XHTML
365 | ]
366 | },
367 | haxe : {
368 | build : {
369 | hxml : "build.hxml"
370 | },
371 | test : {
372 | hxml : "test.hxml"
373 | }
374 | }
375 | });
376 |
377 | /*
378 | grunt.registerTask('run', 'Start Silex', function () {
379 | var server = require('./dist/server/server.js');
380 | console.log('Start Silex', server);
381 | });
382 | */
383 | // run haxe and build Cloud Explorer
384 | grunt.registerTask('npmHaxeBuild', 'Run haxe to build Cloud Explorer', function () {
385 | var buildCommand = 'node node_modules/haxe/bin/haxe-cli.js build.hxml';
386 | console.log('Run haxe and build Cloud Explorer');
387 | console.log('Executing', buildCommand);
388 | var cp = require('child_process')
389 | cp.exec(buildCommand);
390 |
391 | grunt.task.run([
392 | 'clean:dist',
393 | 'useminPrepare',
394 | 'concurrent:dist',
395 | 'autoprefixer',
396 | 'concat',
397 | 'copy:dist',
398 | 'clean:native',
399 | 'copy:native'
400 | ]);
401 | });
402 |
403 | grunt.registerTask('server', function (target) {
404 |
405 | var server = require('./server/unifile.js');
406 | console.log('Start unifile', server);
407 |
408 | if (target === 'dist') {
409 | return grunt.task.run(['build', 'connect:dist:keepalive']);
410 | }
411 |
412 | grunt.task.run([
413 | 'clean:server',
414 | 'haxe:build',
415 | 'concurrent:server',
416 | 'autoprefixer',
417 | //'connect:livereload',
418 | 'watch'
419 | ]);
420 | });
421 |
422 | /*grunt.registerTask('test', [
423 | 'clean:server',
424 | 'concurrent:test',
425 | 'autoprefixer',
426 | 'connect:test',
427 | 'mocha'
428 | ]);*/
429 |
430 | grunt.registerTask('test', [
431 | 'haxe:test',
432 | 'connect:test'
433 | ]);
434 |
435 | grunt.registerTask('build', [
436 | 'clean:dist',
437 | 'haxe:build',
438 | 'useminPrepare',
439 | 'concurrent:dist',
440 | 'autoprefixer',
441 | 'concat',
442 | //'cssmin',
443 | //'uglify',
444 | //'modernizr',
445 | 'copy:dist',
446 | 'clean:native',
447 | 'copy:native'
448 | //'copy:styles',
449 | //'rev',
450 | //'usemin'
451 | ]);
452 |
453 | grunt.registerTask('heroku', [
454 | 'clean:heroku',
455 | 'haxe:build',
456 | 'useminPrepare',
457 | 'concurrent:dist',
458 | 'autoprefixer',
459 | 'concat',
460 | 'copy:heroku'
461 | ]);
462 |
463 | grunt.registerTask('default', [
464 | //'jshint',
465 | //'test',
466 | 'build'
467 | ]);
468 | };
469 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #Cloud Explorer, file picker for the cloud
2 |
3 | Cloud Explorer enables your application or website users with picking their files from the cloud.
4 |
5 | Here is [cloud explorer official website](http://cloud-explorer.org/).
6 |
7 | Cloud Explorer is a free and open source project [powered by Silex Labs](http://www.silexlabs.org/).
8 |
9 | Authored by Thomas Fétiveau [@zab0jad](https://twitter.com/zab0jad) and Alex Hoyau [@lexoyo](https://twitter.com/lexoyo).
10 |
11 | ##About Cloud Explorer
12 |
13 | Cloud Explorer aims to provide an open source client library that exposes the same API as the [File Picker API](https://www.filepicker.com/documentation/file-ingestion/javascript-api).
14 |
15 | Cloud Explorer is the front end client of the [unifile](https://github.com/silexlabs/unifile) backend, a nodejs server which provides a unified access to cloud services. This project uses nodejs and those modules: express, dbox, express, googleapis, logger, node-oauth, oauth, path.
16 |
17 | The backend is in node.js and the front end is in Javascript. Cloud Explorer is written with [Haxe](http://www.haxe.org), enabling a modern and elegant syntax as well as a strong typed and more reliable javascript source code.
18 |
19 | The project is not mature yet and doesn't provide half of what is provided by File Picker. It's however under constant development and will provide more and more of the IPF API every week plus some extra features we've found useful for our projects but that were not offered by IPF.
20 |
21 | Cloud Explorer is also skinable with CSS and hostable in house.
22 |
23 | ###Discussions
24 |
25 | * Facebook http://www.facebook.com/silexlabs
26 | * Twitter https://twitter.com/silexlabs
27 | * Google plus https://plus.google.com/communities/107373636457908189681
28 |
29 | ##Setup
30 |
31 | ### Development or Test
32 |
33 | Prerequisite :
34 |
35 | * [node.js](http://nodejs.org/) installed
36 | * [NPM](https://npmjs.org/) installed
37 | * [Haxe compiler](http://haxe.org/download) installed
38 |
39 | Cloud Explorer default development environment uses [grunt](http://gruntjs.com/) (nodejs), [compass](http://compass-style.org/) and few other little tools that aim to make developping CE easier and faster.
40 |
41 | For early testers and contributors, here are the setup steps to follow in order to run this version of CE:
42 |
43 | * git clone this branch on your local file system,
44 | ```
45 | git clone git@github.com:silexlabs/cloud-explorer.git cloud-explorer
46 | ```
47 |
48 | * run the following command in a terminal to install the nodejs dependencies:
49 | ```
50 | npm install
51 | ```
52 |
53 | * run the following command in a terminal to start the local server:
54 | ```
55 | grunt server
56 | ```
57 |
58 | * compile the haxe js sources
59 | ```
60 | haxe build.hxml
61 | ```
62 |
63 | * open your favorite HTML5 browser on http://localhost:6805/ to have the test page displayed. From there, click the buttons corresponding to the API functions you want to test. Some of them are not yet implemented.
64 |
65 | ### Production
66 |
67 | To install and use Cloud Explorer in your projects, follow those steps :
68 |
69 | * compile the Cloud Explorer library out of this repository:
70 | ```
71 | grunt
72 | ```
73 | You will then find the full Cloud Explorer library under bin/web. Place the entire bin/web directory in a subdirectory of your web app project (you may rename web into cloud-explorer).
74 |
75 | * include the Cloud Explorer javascript file in your web page:
76 | ```
77 |
78 |
79 |
80 | My project
81 |
82 |
83 |
84 |
85 | (...)
86 |
87 |
88 |
89 | ```
90 | We assume that you've pasted the Cloud Explorer lib files in the cloud-explorer sub directory.
91 |
92 | * To use it from your project js code, first initialize a Cloud Explorer instance:
93 | ```
94 | window.document.onload = function(e){
95 |
96 | window.cloudExplorer = ce.api.CloudExplorer.get();
97 | }
98 | ```
99 |
100 | Note that you can also precise the iframe element id that will be used by Cloud Explorer. If not specified, one will be automatically generated.
101 | ```
102 | (...)
103 |
109 | (...)
110 |
116 | ```
117 |
118 | * You will then be able to call it like you would call File Picker:
119 | ```
120 | cloudExplorer.pick(function(b){
121 |
122 | console.log("my Blob: " + JSON.stringify(b));
123 |
124 | }, function(e){ console.log("error " + JSON.stringify(e)); });
125 | ```
126 |
127 | #### Configuration
128 |
129 | To pass configuration variables to Cloud Explorer, specify the iframe it will use as described earlier:
130 | ```
131 |
138 | ```
139 | This will allow you to add the below supported configuration properties:
140 |
141 | * data-ce-unifile-url: the url of the unifile root endpoint your instance of Cloud Explorer will use.
142 | * data-ce-path: path to the folder containing the cloud-explorer files on your server.
143 |
144 | ## Current implementation state and roadmap
145 |
146 | ### Currently supported cloud services
147 |
148 | * hosting server (www), transfer files from and to your own unifile server
149 | * [Dropbox](http://www.dropbox.com)
150 |
151 | More to come soon...
152 |
153 | ### Currently supported features
154 |
155 | The currently implemented part of the IPF API in Cloud Explorer consists of:
156 |
157 | * [CEBlob](https://www.filepicker.com/documentation/file-ingestion/javascript-api/blob)
158 |
159 | Supported fields: url, filename, mimetype, size
160 |
161 | Other fields will return null.
162 |
163 | * [Pick Files](https://www.filepicker.com/documentation/file-ingestion/javascript-api/pick)
164 | ```
165 | cloudExplorer.pick(function(b){
166 |
167 | currentBlob = b;
168 |
169 | textarea.val(textarea.val() + "\ncurrentBlob: " + JSON.stringify(currentBlob));
170 |
171 | }, function(e){ console.log("error " + JSON.stringify(e)); });
172 | ```
173 |
174 | No option supported yet. Will just pick a file from your favorite cloud service and give back a CEBlob instance.
175 |
176 |
177 | * [Export](https://www.filepicker.com/documentation/file-export/javascript-api/export)
178 | ```
179 | cloudExplorer.exportFile(currentBlob, { mimetype: "text/html" }, function(b){
180 |
181 | currentBlob = b;
182 |
183 | textarea.val(textarea.val() + "\ncurrentBlob is now: " + JSON.stringify(currentBlob));
184 |
185 | }, function(e){ console.log("error " + JSON.stringify(e)); });
186 | ```
187 |
188 | Supported options are: mimetype, extension.
189 |
190 | This function doesn't work exactly like the IFP yet as it will need a call to write() after the call to export() to actually write the file. For now, it will just generate a CEBlob instance corresponding to the new file you want to create/store.
191 |
192 | * [Write back to a file](https://www.filepicker.com/documentation/file-ingestion/javascript-api/write)
193 | ```
194 | cloudExplorer.write(currentBlob, "write() test succeeded", function(ceb){
195 |
196 | currentBlob = ceb;
197 |
198 | textarea.val(textarea.val() + "\nwrite operation successful!");
199 |
200 | }, function(e){ console.log("error " + JSON.stringify(e)); });
201 | ```
202 |
203 | No option supported yet.
204 | * [Read Files](https://www.filepicker.com/documentation/file-ingestion/javascript-api/read)
205 |
206 | ```
207 | cloudExplorer.read(currentBlob, function(d){
208 |
209 | textarea.val(textarea.val() + "\nread data: " + d);
210 |
211 | }, function(e){ console.log("error " + JSON.stringify(e)); });
212 | ```
213 |
214 | No option supported yet.
215 |
216 | ### Roadmap
217 |
218 | Current version is 1.0. It is a complete refactoring of the previous 0.1 version that was dependant on JQuery and AngularJS. Version 1.0 has no client side dependency and is implemented with Haxe javascript, allowing future ports of the basecode to native mobile/desktop, Flash/AIR, ...
219 |
220 | The goals of version 1.1 are simple: implement the full File Picker API (web version).
221 |
222 | Then we will probably make SDKs for Android and iOS apps.
223 |
224 | ## Contributions
225 |
226 | We love contributions and consider all kind of pull requests:
227 |
228 | * new themes or improvments of existing default theme
229 | * new components or functionnalities
230 | * additions to the documentation
231 | * bug reports, fixes
232 | * any idea or suggestion
233 |
234 | You can also contribute to the [unifile library](https://github.com/silexlabs/unifile#developer-guide) in order to add more cloud services to Cloud Explorer
235 |
236 | > See the [instructions to contribute to unifile](https://github.com/silexlabs/unifile#developer-guide)
237 |
238 | ## Contributors
239 |
240 | We are all members of [Silex Labs foundation](http://www.silexlabs.org).
241 |
242 | 
243 |
244 | Feel free to contact us through twitter or linkedin
245 |
246 |  Thomas Fétiveau [@zab0jad](https://twitter.com/zab0jad) [Web & mobile Developer](https://www.linkedin.com/in/thomasfetiveau)
247 |
248 |  Alex Hoyau [@lexoyo](https://twitter.com/lexoyo) [Front end architect](https://www.linkedin.com/in/webappdev/)
249 |
250 |  Pol Goasdoué [@superwup](https://twitter.com/superwup) [freelance UI designer](http://superwup.me)
251 |
252 |
253 |
254 | ##Licensing
255 |
256 | Cloud Explorer is licensed under the MIT license.
257 |
258 | ##Screenshots
259 |
260 | 
261 | 
262 | 
263 | 
264 | 
265 | 
266 |
267 |
268 |
--------------------------------------------------------------------------------
/app/cloud-explorer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Cloud Explorer
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
41 |
42 |
43 |
44 |
45 |
46 |
51 |
52 |
53 |
54 |
Click on a service in the list below to connect it.
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
Logout all
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | /
74 |
75 |
76 |
77 |
78 | Name
79 | Last update
80 | Type
81 |
82 |
101 |
102 |
103 |
104 |
105 |
Drop your file(s) here
106 |
or
107 |
Select your file(s) to upload
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/app/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Cloud Explorer
5 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
88 |
89 |
90 |
Welcome to the Cloud Explorer demo
91 |
Thank you for testing Cloud Explorer!
92 |
Please click the following button to start the demo: Start
93 |
94 |
95 |
96 |
Open a file from the cloud
97 |
Pick a file, image or text content, from a cloud storage: Open
98 |
Great ! Here is your file's content:
99 |
Click here to continue: Continue
100 |
101 |
102 |
103 |
Create some content and save it in the cloud
104 |
Write some text in the below input:
105 |
106 |
And save it in a new file : Save As
107 |
File successfully saved with content:
108 |
Click here to continue: Continue
109 |
110 |
111 |
112 |
Edit your file content and save the changes
113 |
Now, edit your previously created file's content in the below input:
114 |
115 |
And save those changes to the cloud : Save
116 |
File successfully saved with new content:
117 |
Click here to continue: Continue
118 |
119 |
120 |
121 |
Thank you for having tested Cloud Explorer
122 |
For suggestion, bug report, contribution or anything about the project, please use the Github issue tracker .
123 |
To restart the demo, click here: Restart
124 |
125 |
126 |
242 |
243 |
244 |
--------------------------------------------------------------------------------
/app/images/default/breadcrumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/breadcrumb.png
--------------------------------------------------------------------------------
/app/images/default/btn-close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-close.png
--------------------------------------------------------------------------------
/app/images/default/btn-icon-items.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-icon-items.png
--------------------------------------------------------------------------------
/app/images/default/btn-item-delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-item-delete.png
--------------------------------------------------------------------------------
/app/images/default/btn-item-rename.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-item-rename.png
--------------------------------------------------------------------------------
/app/images/default/btn-item-select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-item-select.png
--------------------------------------------------------------------------------
/app/images/default/btn-list-items.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-list-items.png
--------------------------------------------------------------------------------
/app/images/default/btn-new-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-new-folder.png
--------------------------------------------------------------------------------
/app/images/default/btn-parent-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-parent-folder.png
--------------------------------------------------------------------------------
/app/images/default/btn-sort.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/btn-sort.png
--------------------------------------------------------------------------------
/app/images/default/ce-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/ce-header.png
--------------------------------------------------------------------------------
/app/images/default/dropzone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/dropzone.png
--------------------------------------------------------------------------------
/app/images/default/home-dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-dropbox.png
--------------------------------------------------------------------------------
/app/images/default/home-ftp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-ftp.png
--------------------------------------------------------------------------------
/app/images/default/home-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-github.png
--------------------------------------------------------------------------------
/app/images/default/home-open-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-open-pages.png
--------------------------------------------------------------------------------
/app/images/default/home-webdav.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-webdav.png
--------------------------------------------------------------------------------
/app/images/default/home-www.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/home-www.png
--------------------------------------------------------------------------------
/app/images/default/icon-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/icon-file.png
--------------------------------------------------------------------------------
/app/images/default/icon-folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/icon-folder.png
--------------------------------------------------------------------------------
/app/images/default/icon-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/icon-image.png
--------------------------------------------------------------------------------
/app/images/default/icon-sound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/icon-sound.png
--------------------------------------------------------------------------------
/app/images/default/icon-video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/icon-video.png
--------------------------------------------------------------------------------
/app/images/default/srv-dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-dropbox.png
--------------------------------------------------------------------------------
/app/images/default/srv-ftp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-ftp.png
--------------------------------------------------------------------------------
/app/images/default/srv-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-github.png
--------------------------------------------------------------------------------
/app/images/default/srv-open-pages.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-open-pages.png
--------------------------------------------------------------------------------
/app/images/default/srv-state-connected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-state-connected.png
--------------------------------------------------------------------------------
/app/images/default/srv-state-disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-state-disabled.png
--------------------------------------------------------------------------------
/app/images/default/srv-state-disconnected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-state-disconnected.png
--------------------------------------------------------------------------------
/app/images/default/srv-webdav.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-webdav.png
--------------------------------------------------------------------------------
/app/images/default/srv-www.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/app/images/default/srv-www.png
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Cloud Explorer
5 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | Pick
22 | Pick Image
23 | Pick Folder
24 | Pick Multiple
25 | Pick And Store
26 | Export
27 | Read
28 | Stat
29 | Write
30 | Store
31 | Convert
32 | Remove
33 | Is logged into www ?
34 | authorize www
35 |
36 |
37 |
38 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/app/oauth-cb.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Cloud Explorer - authentication
5 |
6 |
7 |
14 |
15 |
--------------------------------------------------------------------------------
/app/scripts/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
--------------------------------------------------------------------------------
/app/styles/base.scss:
--------------------------------------------------------------------------------
1 | html, body {
2 |
3 | font-family: sans-serif;
4 | margin: 0;
5 | padding: 0;
6 | color: #000;
7 | height: 100%; width: 100%;
8 | }
9 |
10 | @import "platforms.scss";
11 |
12 | .placeholder {
13 |
14 | display: none;
15 | }
16 |
17 | .explorer {
18 |
19 | width: 100%; height: 100%;
20 | }
21 |
22 | .explorer {
23 |
24 | .loader,
25 | .closeBtn,
26 | .logoutBtn,
27 | .breadcrumb,
28 | .export,
29 | .home,
30 | .fileBrowser,
31 | .alertPopup,
32 | .authPopup,
33 | .footer,
34 | .parentFolderBtn,
35 | .newFolderBtn,
36 | .listItemsBtn,
37 | .iconItemsBtn,
38 | .deleteBtn,
39 | .fileBrowser .files ul .folder.new
40 | {
41 | display: none;
42 | }
43 |
44 |
45 | /**
46 |
47 | Application states
48 |
49 | **/
50 |
51 | &.loading {
52 |
53 | .loader {
54 |
55 | display: block;
56 | }
57 | .fileBrowser {
58 |
59 | .dropzone {
60 |
61 | display: none;
62 | }
63 | }
64 | }
65 |
66 | &.starting {
67 |
68 | .home {
69 |
70 | display: block;
71 | }
72 | .closeBtn {
73 |
74 | display: inline-block;
75 | }
76 | }
77 |
78 | &.browsing {
79 |
80 | .fileBrowser {
81 |
82 | display: block;
83 | }
84 | .parentFolderBtn,
85 | .newFolderBtn,
86 | .listItemsBtn,
87 | .iconItemsBtn,
88 | .closeBtn {
89 |
90 | display: inline-block;
91 | }
92 | .breadcrumb {
93 |
94 | display: block;
95 | }
96 | .export {
97 |
98 | button {
99 |
100 | display: none;
101 | }
102 | }
103 | }
104 |
105 | &.browsing.single-file-sel-mode {
106 |
107 |
108 | }
109 |
110 | &.browsing.single-file-exp-mode {
111 |
112 | .footer {
113 |
114 | display: block;
115 | }
116 | .export {
117 |
118 | display: inline-block;
119 |
120 | .saveBtn {
121 |
122 | display: inline-block;
123 | }
124 | .overwriteBtn {
125 |
126 | display: none;
127 | }
128 | }
129 |
130 | &.export-overwriting {
131 |
132 | .export {
133 |
134 | .saveBtn {
135 |
136 | display: none;
137 | }
138 | .overwriteBtn {
139 |
140 | display: inline-block;
141 | }
142 | }
143 | }
144 | }
145 |
146 | &.browsing.making-new-folder {
147 |
148 | .fileBrowser .files ul .folder.new {
149 |
150 | display: block;
151 | }
152 | }
153 |
154 | &.authorizing {
155 |
156 | .authPopup {
157 |
158 | display: block;
159 | }
160 | }
161 |
162 | &.alerting {
163 |
164 | .alertPopup {
165 |
166 | display: block;
167 | }
168 | }
169 |
170 | &.loggedin {
171 |
172 | .logoutBtn {
173 |
174 | display: block;
175 | }
176 | }
177 |
178 |
179 | /**
180 |
181 | Components states
182 |
183 | **/
184 |
185 | .fileBrowser {
186 |
187 | .files {
188 |
189 | .list {
190 |
191 | ul {
192 |
193 | li {
194 |
195 | input[type="text"] {
196 |
197 | display: none;
198 | }
199 |
200 | &.renaming {
201 |
202 | .fileName {
203 |
204 | display: none;
205 | }
206 | input[type="text"] {
207 |
208 | display: inline-block;
209 | }
210 | }
211 | }
212 | li.new {
213 |
214 | input[type="text"] {
215 |
216 | display: inline-block;
217 | }
218 | }
219 | }
220 | }
221 | }
222 | }
223 |
224 | &.selecting {
225 |
226 | .footer {
227 |
228 | display: block;
229 | }
230 | button.deleteBtn {
231 |
232 | display: inline-block;
233 | }
234 | }
235 | }
--------------------------------------------------------------------------------
/app/styles/cloud-explorer.scss:
--------------------------------------------------------------------------------
1 | @import "base.scss";
2 |
3 | @import "default/theme.scss";
--------------------------------------------------------------------------------
/app/styles/default/README.md:
--------------------------------------------------------------------------------
1 | default theme for cloud explorer
--------------------------------------------------------------------------------
/app/styles/default/theme.scss:
--------------------------------------------------------------------------------
1 | ul { margin: 0; padding: 0; list-style: none; }
2 |
3 | *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; }
4 |
5 | body, input[type="text"] {
6 | font-family: 'Roboto', arial, sans-serif; font-size: 14px; color: #727271;
7 | }
8 |
9 | button:focus { outline: 0; } /* avoid default blue outline on chrome */
10 |
11 | .explorer-bg {
12 |
13 | background-color: black; opacity: .5;
14 | position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 0;
15 | }
16 |
17 | .explorer {
18 |
19 | position: relative; top: 50%; margin: auto; margin-top: -330px;
20 | height: 660px; width: 80%; min-width: 640px;
21 | background-color: white; opacity: 1; border-radius: 5px;
22 | overflow: hidden;
23 |
24 | .loader, .alertPopup {
25 |
26 | position: absolute; top: 0; left: 0; z-index: 99;
27 | width: 100%; height: 100%; vertical-align: middle;
28 |
29 | div.bg {
30 | position: absolute; top: 0; left: 0; z-index: -1;
31 | width: 100%; height: 100%;
32 | background-color: black; opacity: .5;
33 | }
34 | div.txt {
35 | position: absolute;
36 | top: 50%;
37 | left: 50%; width: 450px; margin-left: -225px;
38 | padding: 20px;
39 | background-color: white;
40 | opacity: 1; border-radius: 10px;
41 | text-align: center;
42 | font: {
43 | size: 20px;
44 | }
45 | }
46 | }
47 | .loader {
48 | div.txt {
49 | height: 80px; margin-top: -40px;
50 | }
51 | }
52 | .alertPopup {
53 |
54 | div.txt {
55 |
56 | height: 160px; margin-top: -80px;
57 |
58 | .choice {
59 | padding: 6px; margin: 2px;
60 | cursor: pointer;
61 | user-select: none;
62 | border-radius: 5px;
63 | }
64 | .choice:nth-of-type(1) {
65 | border: 2px solid red;
66 | }
67 | .choice:nth-of-type(2) {
68 | border: 2px solid green;
69 | }
70 | .choice:nth-of-type(1):hover {
71 | background: red; color: #FFF;
72 | }
73 | .choice:nth-of-type(2):hover {
74 | background: green; color: #FFF;
75 | }
76 | }
77 | }
78 | .alertPopup.warning {
79 |
80 | div.txt {
81 |
82 | background-color: orange;
83 | }
84 | }
85 | .alertPopup.error {
86 |
87 | div.txt {
88 |
89 | background-color: red;
90 | }
91 | }
92 |
93 | .home {
94 |
95 | text-align: center;
96 |
97 | span {
98 | display: inline-block;
99 | font-size: 20px; margin-top: 180px;
100 | }
101 | ul {
102 | display: block; width: 400px;
103 | margin: auto; margin-top: 15px;
104 | li {
105 | display: inline-block;
106 | width: 110px; height: 110px;
107 | vertical-align: top;
108 | margin: 10px;
109 | cursor: pointer;
110 | user-select: none;
111 | padding-top: 90px;
112 | background: {
113 | repeat: no-repeat;
114 | size: auto 110px;
115 | position: 0px -8px;
116 | }
117 | border: 1px solid 34b7ea;
118 | font: {
119 | size: 16;
120 | }
121 | }
122 | li:hover {
123 | background-position: -110px -8px;
124 | border: 1px solid 288cb3;
125 | }
126 | li:active {
127 | background-position: -220px -8px;
128 | border: 1px solid 195a73;
129 | }
130 | li.dropbox {
131 | background-image: url('../images/default/home-dropbox.png');
132 | }
133 | li.www {
134 | background-image: url('../images/default/home-www.png');
135 | }
136 | li.ftp {
137 | background-image: url('../images/default/home-ftp.png');
138 | }
139 | li.webdav {
140 | background-image: url('../images/default/home-webdav.png');
141 | }
142 | li.github {
143 | background-image: url('../images/default/home-github.png');
144 | }
145 | li.open-pages{
146 | background-image: url('../images/default/home-open-pages.png');
147 | }
148 | }
149 | }
150 |
151 | .header {
152 |
153 | position: relative; z-index: 2;
154 | height: 104px;
155 | background: {
156 | color: #282828;
157 | image: url('../images/default/ce-header.png');
158 | repeat: no-repeat;
159 | size: auto 70%;
160 | position: 20px 50%;
161 | }
162 | .controls {
163 |
164 | position: absolute; top: 26px; right: 10px;
165 | width: 310px; height: 52px;
166 |
167 | button {
168 |
169 | float:left;
170 | width: 52px; height: 52px;
171 | padding: 0; border: 0; margin: 0;
172 | background-repeat: no-repeat;
173 | background-position: 0px 0px;
174 | background-color: transparent;
175 | cursor: pointer;
176 | }
177 | button:hover {
178 |
179 | background-position: -52px 0px;
180 | }
181 | button:active {
182 |
183 | background-position: -104px 0px;
184 | }
185 | button:disabled {
186 |
187 | background-position: -208px 0px;
188 | cursor: not-allowed;
189 | }
190 | button.parentFolderBtn {
191 |
192 | background-image: url('../images/default/btn-parent-folder.png');
193 | margin-right: 10px;
194 | }
195 | button.newFolderBtn {
196 |
197 | background-image: url('../images/default/btn-new-folder.png');
198 | margin-right: 20px;
199 | }
200 | button.listItemsBtn {
201 |
202 | background-image: url('../images/default/btn-list-items.png');
203 | margin-right: 10px;
204 | }
205 | button.iconItemsBtn {
206 |
207 | background-image: url('../images/default/btn-icon-items.png');
208 | margin-right: 10px;
209 | }
210 | button.closeBtn {
211 |
212 | background-image: url('../images/default/btn-close.png');
213 | }
214 | }
215 | }
216 |
217 | .authPopup {
218 |
219 | position: absolute; z-index: 110;
220 | top: 0; left: 0;
221 | width: 100%; height: 100%;
222 | background-color: rgba(0,0,0,.4);
223 |
224 | div {
225 | position: absolute;
226 | top: 50%; height: 80px; margin-top: -40px;
227 | left: 50%; width: 450px; margin-left: -225px;
228 | padding: 20px;
229 | background-color: #FFFFFF;
230 | border-radius: 10px;
231 | color: #727271;
232 | text-align: center;
233 | font: {
234 | weight: bold;
235 | size: 20px;
236 | }
237 | }
238 | }
239 |
240 | .breadcrumb {
241 |
242 | padding: 6px; padding-left: 50px;
243 | font-size: 14px;
244 | background: url("../images/default/breadcrumb.png") no-repeat 10px 50% / auto 65%;
245 |
246 | .pathIt {
247 | font-style: italic;
248 | cursor: pointer;
249 | color: #727271;
250 | }
251 | .pathIt:hover {
252 | text-decoration: underline;
253 | }
254 | }
255 |
256 | .fileBrowser {
257 |
258 | position: relative; width: 100%; height: 100%;
259 | margin-top: -104px;
260 | padding-top: 104px; padding-bottom: 0px;
261 |
262 | .services, .files {
263 |
264 | float: left;
265 | height: 100%;
266 | /* overflow: hidden; */
267 | }
268 | .services {
269 |
270 | position: relative; width: 53px; z-index: 10;
271 | border-right: 1px solid #282828;
272 |
273 | .logoutBtn {
274 | height: 52px; margin: 0px; padding: 0px;
275 | cursor: pointer;
276 | }
277 |
278 | > ul {
279 | height: 100%; width: 100%; padding-bottom: 52px;
280 |
281 | > li {
282 | display: block; float: left;
283 | height: 52px; width: 52px;
284 | border-bottom: 1px solid #666666;
285 | cursor: pointer;
286 | background-repeat: no-repeat, no-repeat;
287 | background-position: 32px 9px, 0px 0px;
288 | background-size: 11px 11px, auto 52px;
289 | }
290 | > li:hover {
291 | background-position: 32px 9px, -52px 0px;
292 |
293 | ul.contextMenu {
294 | /* display: block; WIP */
295 | }
296 | }
297 | > li:active {
298 | background-position: 32px 9px, -104px 0px;
299 | }
300 | > li.dropbox {
301 | background-image: url("../images/default/srv-state-disconnected.png"), url("../images/default/srv-dropbox.png");
302 | }
303 | > li.dropbox.connected {
304 | background-image: url("../images/default/srv-state-connected.png"), url("../images/default/srv-dropbox.png");
305 | }
306 | > li.www {
307 | background-image: url("../images/default/srv-state-disconnected.png"), url('../images/default/srv-www.png');
308 | }
309 | > li.www.connected {
310 | background-image: url("../images/default/srv-state-connected.png"), url('../images/default/srv-www.png');
311 | }
312 | > li.ftp {
313 | background-image: url("../images/default/srv-state-disconnected.png"), url('../images/default/srv-ftp.png');
314 | }
315 | > li.ftp.connected {
316 | background-image: url("../images/default/srv-state-connected.png"), url('../images/default/srv-ftp.png');
317 | }
318 | > li.github {
319 | background-image: url("../images/default/srv-state-disconnected.png"), url('../images/default/srv-github.png');
320 | }
321 | > li.github.connected {
322 | background-image: url("../images/default/srv-state-connected.png"), url('../images/default/srv-github.png');
323 | }
324 | > li.webdav {
325 | background-image: url("../images/default/srv-state-disconnected.png"), url('../images/default/srv-webdav.png');
326 | }
327 | > li.webdav.connected {
328 | background-image: url("../images/default/srv-state-connected.png"), url('../images/default/srv-webdav.png');
329 | }
330 | > li.open-pages {
331 | background-image: url("../images/default/srv-state-disconnected.png"), url('../images/default/srv-open-pages.png');
332 | }
333 | > li.open-pages.connected {
334 | background-image: url("../images/default/srv-state-connected.png"), url('../images/default/srv-open-pages.png');
335 | }
336 |
337 | ul.contextMenu {
338 |
339 | position: absolute; z-index: 20;
340 | width: 170px; left: 50px; margin-top: 4px;
341 | display: none;
342 | padding: 0px;
343 | background: {
344 | color: #fcf6d6;
345 | }
346 | border: {
347 | left: 1px solid black;
348 | top: 1px solid black;
349 | right: 1px solid black;
350 | }
351 |
352 | li {
353 | height: 27px; line-height: 26px;
354 | margin: 0px; padding: 0px; padding-left: 6px;
355 | border-bottom: 1px solid black;
356 | }
357 | li:hover {
358 | text-decoration: underline;
359 | }
360 | li.logout {
361 | display: none;
362 | }
363 | }
364 | ul.contextMenu::before {
365 |
366 | content: "";
367 | position: absolute;
368 | width: 0; height: 0;
369 | margin-left: -12px; margin-top: 8px;
370 | border: {
371 | width: 6px;
372 | style: solid;
373 | color: transparent black transparent transparent;
374 | }
375 | }
376 | li.connected {
377 | li.login {
378 | display: none;
379 | }
380 | li.logout {
381 | display: block;
382 | }
383 | }
384 | }
385 | }
386 | .files {
387 |
388 | position: absolute; width: 100%; top: 104px; left: 0;
389 | padding-left: 52px;
390 |
391 | .list {
392 |
393 | padding-left: 10px; padding-right: 10px;
394 | display: block; height: 100%; padding-bottom: 224px;
395 |
396 | .titles {
397 |
398 | color: #34B8EB; font-size: 14px; padding-top: 6px; padding-bottom: 6px;
399 | border-top: 1px solid #34B8EB; border-bottom: 1px solid #34B8EB;
400 |
401 | span {
402 | display: inline-block;
403 | cursor: pointer; user-select: none;
404 | }
405 | span.sortArrow {
406 | width: 6px; height: 6px; margin-right: 6px;
407 | background: {
408 | image: url("../images/default/btn-sort.png");
409 | size: auto 6px;
410 | position: 0px 0px;
411 | repeat: no-repeat;
412 | }
413 | }
414 | span:hover {
415 | text-decoration: underline;
416 | .sortArrow {
417 | background-position: -6px 0px;
418 | }
419 | }
420 | span:active {
421 | .sortArrow {
422 | background-position: -12px 0px;
423 | }
424 | }
425 | .lastUpdate {
426 | width: 150px;
427 | }
428 | .fileName {
429 | margin-left: 20px; padding-left: 35px;
430 | width: 210px;
431 | }
432 | .fileType{
433 | width: 60px;
434 | }
435 | .lastUpdate, .fileType {
436 | float: right; text-align: center;
437 | }
438 | }
439 | div.items {
440 | height: 100%; padding-bottom: 30px;
441 | ul {
442 | height: 100%; overflow-y: auto;
443 | user-select: none;
444 |
445 | li .fileName {
446 | cursor: pointer;
447 | }
448 | li .fileType {
449 | width: 60px;
450 | }
451 | li .lastUpdate {
452 | width: 150px;
453 | }
454 | li .fileType, li .lastUpdate {
455 | float: right; text-align: center;
456 | }
457 | li span {
458 | display: inline-block; overflow: hidden;
459 | white-space: nowrap;
460 | }
461 | li {
462 | clear: both; height: 35px; line-height: 35px;
463 | border-bottom: 1px solid rgb(229, 229, 229);
464 | -ms-text-overflow: ellipsis; -o-text-overflow: ellipsis; text-overflow: ellipsis;
465 |
466 | span.icon {
467 | width: 34px; height: 34px;
468 | background: {
469 | repeat: no-repeat;
470 | size: auto 34px;
471 | position: 0px 0px;
472 | }
473 | }
474 | input, button, span {
475 | vertical-align: middle;
476 | }
477 | span.fileName, input[type="text"] {
478 | cursor: pointer;
479 | }
480 | input[type="text"] {
481 | border: 0; margin: 0;
482 | }
483 | button {
484 | display: none;
485 | width: 34px; height: 34px;
486 | border: 0; padding: 0; margin-left: 10px;
487 | cursor: pointer;
488 | background: {
489 | color: transparent;
490 | size: auto 34px;
491 | position: 0px 0px;
492 | repeat: no-repeat;
493 | }
494 | }
495 | button:hover {
496 | background-position: -34px 0px;
497 | }
498 | button:active {
499 | background-position: -68px 0px;
500 | }
501 | button.select {
502 | background-image: url("../images/default/btn-item-select.png");
503 | }
504 | button.rename {
505 | background-image: url("../images/default/btn-item-rename.png");
506 | }
507 | button.delete {
508 | background-image: url("../images/default/btn-item-delete.png");
509 | }
510 | }
511 | li.nosel {
512 | input[type="checkbox"] {
513 | display: none;
514 | }
515 | .fileName {
516 | margin-left: 25px;
517 | }
518 | }
519 | li:hover {
520 | span.icon {
521 | background-position: -34px 0px;
522 | }
523 | button.rename, button.delete {
524 | display: inline-block;
525 | }
526 | }
527 | li:active {
528 | span.icon {
529 | background-position: -68px 0px;
530 | }
531 | }
532 | li.folder {
533 | span.icon {
534 | background-image: url('../images/default/icon-folder.png');
535 | }
536 | &.new {
537 | input[type="text"] {
538 | margin-left: 20px;
539 | }
540 | }
541 | }
542 | li.file {
543 | span.icon {
544 | background-image: url('../images/default/icon-file.png');
545 | }
546 | }
547 | li.file.image {
548 | span.icon {
549 | background-image: url('../images/default/icon-image.png');
550 | }
551 | }
552 | li.file.sound {
553 | span.icon {
554 | background-image: url('../images/default/icon-sound.png');
555 | }
556 | }
557 | li.file.video {
558 | span.icon {
559 | background-image: url('../images/default/icon-video.png');
560 | }
561 | }
562 | li.filteredOut {
563 | /* display: none; */
564 | color: grey;
565 | font-style: italic;
566 | cursor: not-allowed;
567 | span.fileName {
568 | cursor: not-allowed;
569 | }
570 | }
571 | li.filteredOut:hover {
572 | span.icon {
573 | background-position: 0px 0px;
574 | }
575 | button.rename, button.delete {
576 | display: none;
577 | }
578 | }
579 | }
580 | }
581 | }
582 | }
583 | &.selectFolders {
584 | .files {
585 | .list {
586 | ul {
587 | li:hover {
588 | button.select {
589 | display: inline-block;
590 | }
591 | }
592 | }
593 | }
594 | }
595 | }
596 | }
597 |
598 | .dropzone {
599 |
600 | position: relative; height: 92px; z-index: 10; margin-top: -224px;
601 | border-top: 1px dashed #34B8EB;
602 | background: url("../images/default/dropzone.png") no-repeat center / auto 50%;
603 | text-align: center;
604 | color: #34B7EA;
605 |
606 | h2 {
607 | margin-top: 4px; margin-bottom: 11px;
608 | position: relative;
609 | font: {
610 | weight: 300;
611 | size: 14pt;
612 | }
613 | }
614 | div, div button {
615 | position: absolute; top: 0; left: 0;
616 | height: 100%; width: 100%; padding: 0; margin: 0; border: 0;
617 | }
618 | div button {
619 | background: transparent;
620 | color: #34B7EA;
621 | line-height : 150px;
622 | font: {
623 | weight: 300;
624 | size: 11pt;
625 | }
626 | cursor: pointer;
627 | }
628 | div button:hover {
629 | text-decoration: underline;
630 | }
631 | div input {
632 | opacity: 0;
633 | }
634 | }
635 |
636 | .footer {
637 |
638 | position: relative; height: 52px;
639 | margin-top: -52px;
640 | background: #34B8EB;
641 | border-top: 1px solid #282828;
642 | text-align: center;
643 |
644 | button.deleteBtn, div.export .saveBtn, div.export .overwriteBtn {
645 |
646 | background: #fff; height: 40px; padding: 8px;
647 | border: 0;
648 | font: {
649 | weight: 300;
650 | size: 18px;
651 | }
652 | color: #444; text-transform: lowercase;
653 | cursor: pointer;
654 | }
655 | button.deleteBtn, div.export {
656 | margin-top: 6px;
657 | }
658 | button.deleteBtn {
659 |
660 | float: right; margin-right: 10px;
661 | }
662 | div.export {
663 |
664 | .saveBtn, .overwriteBtn {
665 | color: #FFF;
666 | }
667 | .overwriteBtn {
668 | background: orange;
669 | }
670 | .saveBtn {
671 | background: green;
672 | }
673 | }
674 | }
675 |
676 |
677 | /*
678 | STYLING BY STATE
679 | */
680 |
681 | &.starting {
682 |
683 | .header .controls {
684 |
685 | width: 52px;
686 | }
687 | }
688 |
689 | &.selecting, &.browsing.single-file-exp-mode { /* footer <+ ?> */
690 |
691 | .fileBrowser {
692 |
693 | padding-bottom: 52px; /* for footer */
694 | }
695 | }
696 | &.selecting.browsing.single-file-sel-mode { /* dropzone + footer */
697 |
698 | .fileBrowser {
699 |
700 | .files {
701 |
702 | .list {
703 |
704 | padding-bottom: 276px; /* for footer + dropzone + titles + header */
705 | }
706 | }
707 | .dropzone {
708 |
709 | margin-top: -276px;
710 | }
711 | }
712 | }
713 | &.browsing.single-file-exp-mode { /* footer (no dropzone) */
714 |
715 | .fileBrowser {
716 |
717 | .files {
718 |
719 | .list {
720 |
721 | padding-bottom: 184px;
722 | }
723 | }
724 | .dropzone {
725 |
726 | display: none;
727 | }
728 | }
729 | }
730 | &.items-list {
731 |
732 | .header {
733 |
734 | .listItemsBtn, .listItemsBtn:hover {
735 | background-position: -156px 0px;
736 | }
737 | }
738 | }
739 | &.items-icons {
740 |
741 | .header {
742 |
743 | .iconItemsBtn, .iconItemsBtn:hover {
744 | background-position: -156px 0px;
745 | }
746 | }
747 | }
748 | &.browsing.making-new-folder {
749 |
750 | .header {
751 |
752 | .newFolderBtn, .newFolderBtn:hover {
753 | background-position: -156px 0px;
754 | }
755 | }
756 | }
757 | &.sortedby-name.asc {
758 | .files {
759 | .list {
760 | .titles {
761 | .fileName {
762 | span.sortArrow {
763 | background-position: -18px 0px;
764 | }
765 | }
766 | }
767 | }
768 | }
769 | }
770 | &.sortedby-name.desc {
771 | .files {
772 | .list {
773 | .titles {
774 | .fileName {
775 | span.sortArrow {
776 | background-position: -18px 0px;
777 | transform: rotate(0.5turn)
778 | }
779 | }
780 | }
781 | }
782 | }
783 | }
784 | &.sortedby-type.asc {
785 | .files {
786 | .list {
787 | .titles {
788 | .fileType {
789 | span.sortArrow {
790 | background-position: -18px 0px;
791 | }
792 | }
793 | }
794 | }
795 | }
796 | }
797 | &.sortedby-type.desc {
798 | .files {
799 | .list {
800 | .titles {
801 | .fileType {
802 | span.sortArrow {
803 | background-position: -18px 0px;
804 | transform: rotate(0.5turn)
805 | }
806 | }
807 | }
808 | }
809 | }
810 | }
811 | &.sortedby-lastUpdate.asc {
812 | .files {
813 | .list {
814 | .titles {
815 | .lastUpdate {
816 | span.sortArrow {
817 | background-position: -18px 0px;
818 | }
819 | }
820 | }
821 | }
822 | }
823 | }
824 | &.sortedby-lastUpdate.desc {
825 | .files {
826 | .list {
827 | .titles {
828 | .lastUpdate {
829 | span.sortArrow {
830 | background-position: -18px 0px;
831 | transform: rotate(0.5turn)
832 | }
833 | }
834 | }
835 | }
836 | }
837 | }
838 | &.srv-dropbox {
839 | .fileBrowser {
840 | .services {
841 | li.dropbox {
842 | background-position: 32px 9px, -156px 0px;
843 | }
844 | }
845 | }
846 | }
847 | &.srv-ftp {
848 | .fileBrowser {
849 | .services {
850 | li.ftp {
851 | background-position: 32px 9px, -156px 0px;
852 | }
853 | }
854 | }
855 | }
856 | &.srv-www {
857 | .fileBrowser {
858 | .services {
859 | li.www {
860 | background-position: 32px 9px, -156px 0px;
861 | }
862 | }
863 | }
864 | }
865 | }
866 |
--------------------------------------------------------------------------------
/app/styles/platforms.scss:
--------------------------------------------------------------------------------
1 | /**
2 |
3 | SCSS containing everything platform specific
4 |
5 | **/
6 |
7 |
--------------------------------------------------------------------------------
/build.hxml:
--------------------------------------------------------------------------------
1 | ##
2 | # Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | # @see https://github.com/silexlabs/cloud-explorer
4 | #
5 | # Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | # @see https://github.com/silexlabs/unifile
7 | #
8 | # @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | # Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | # License MIT
11 | ##
12 |
13 | # js lib compilation
14 | -js app/scripts/cloud-explorer.js
15 | -cp src
16 | # uncomment the following to disable traces
17 | #--no-traces
18 | ce.api.CloudExplorer
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cloud-explorer",
3 | "description": "",
4 | "version": "1.0.0",
5 | "author": "Thomas Fetiveau aka zabojad",
6 | "contributors": [
7 | "Alex Hoyau aka lexoyo"
8 | ],
9 | "dependencies": {
10 | "express": "3.4.x",
11 | "dbox": "0.6.x",
12 | "node-oauth": "0.1.x",
13 | "oauth": "",
14 | "haxe": "~0.1.9",
15 | "path": "",
16 | "connect-multiparty": "~2.0.0",
17 | "body-parser": "~1.15.0",
18 | "cookie-parser": "~1.4.1",
19 | "express-session": "~1.13.0",
20 | "unifile": "0.0.56"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "https://github.com/silexlabs/cloud-explorer.git"
25 | },
26 | "keywords": [
27 | "express",
28 | "cloud",
29 | "cms",
30 | "rest",
31 | "dropbox",
32 | "storage",
33 | "filepicker",
34 | "api",
35 | "haxe"
36 | ],
37 | "devDependencies": {
38 | "grunt": "~0.4.1",
39 | "grunt-cli": "~0.1.11",
40 | "grunt-contrib-copy": "~0.4.1",
41 | "grunt-contrib-concat": "~0.3.0",
42 | "grunt-contrib-uglify": "~0.2.0",
43 | "grunt-contrib-compass": "~0.5.0",
44 | "grunt-contrib-jshint": "~0.6.3",
45 | "grunt-contrib-cssmin": "~0.6.0",
46 | "grunt-contrib-connect": "~0.5.0",
47 | "grunt-contrib-clean": "~0.5.0",
48 | "grunt-contrib-htmlmin": "~0.1.3",
49 | "grunt-bower-install": "~0.5.0",
50 | "grunt-contrib-imagemin": "~0.2.0",
51 | "grunt-contrib-watch": "~0.5.2",
52 | "grunt-rev": "~0.1.0",
53 | "grunt-autoprefixer": "~0.2.0",
54 | "grunt-usemin": "~2.0.2",
55 | "grunt-mocha": "~0.4.0",
56 | "grunt-svgmin": "~0.2.0",
57 | "grunt-concurrent": "~0.3.0",
58 | "load-grunt-tasks": "~0.1.0",
59 | "time-grunt": "~0.1.1",
60 | "grunt-haxe": "~0.1.10",
61 | "mocha": "~2.4.5",
62 | "chai": "~3.5.0",
63 | "supertest": "~1.2.0"
64 | },
65 | "license": "MIT",
66 | "engines": {
67 | "node": "0.10.x"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/screenshot01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot01.png
--------------------------------------------------------------------------------
/screenshot02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot02.png
--------------------------------------------------------------------------------
/screenshot03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot03.png
--------------------------------------------------------------------------------
/screenshot04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot04.png
--------------------------------------------------------------------------------
/screenshot05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot05.png
--------------------------------------------------------------------------------
/screenshot06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/silexlabs/cloud-explorer/06947ebed3ee0de3b99f4fc325416059cf7d0f87/screenshot06.png
--------------------------------------------------------------------------------
/server/unifile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A simple unifile server to expose unifile api and nothing else
3 | * https://github.com/silexlabs/unifile/
4 | * license: GPL v2
5 | */
6 | // node modules
7 | var express = require('express');
8 | var app = express();
9 | var unifile = require('unifile');
10 | var multipart = require('connect-multiparty');
11 | var bodyParser = require('body-parser');
12 | var cookieParser = require('cookie-parser');
13 | var session = require('express-session');
14 |
15 | // config
16 | var options = unifile.defaultConfig;
17 |
18 | // define users (login/password) wich will be authorized to access the www folder (read and write)
19 | options.www.USERS = {
20 | "admin": "admin"
21 | }
22 |
23 | options.www.ROOT = __dirname + "/../bin/";
24 |
25 | // enable open pages
26 | // this is the ""self hosting mode"
27 | // auth with Mozilla persona, choose a name and brose a folder on the server where unifile is installed and which is served as http(s)://the-unifile-server.com/chosen-name/ - this is an experimental feature which still has to be fine tuned
28 | // here you can set all open pages config, see default-config.js
29 | // options.openPages.ENABLED = true;
30 |
31 | // limit the services to the tested ones
32 | options.services = ['dropbox', 'www', 'ftp'];
33 |
34 | options.staticFolders.push(
35 | // file browser
36 | {
37 | name: "/",
38 | path: __dirname + "/../app/"
39 | },
40 | {
41 | name: "/styles/",
42 | path: __dirname + "/../.tmp/styles/"
43 | });
44 |
45 | // parse data for file upload
46 | app.use(options.apiRoot, multipart());
47 |
48 | // parse data for post data
49 | app.use(options.apiRoot, bodyParser.urlencoded({
50 | extended: true
51 | }));
52 | app.use(options.apiRoot, bodyParser.json());
53 |
54 | app.use(options.apiRoot, cookieParser());
55 | app.use(options.apiRoot, session({
56 | name: options.cookieName,
57 | secret: options.sessionSecret,
58 | resave: false,
59 | saveUninitialized: false,
60 | cookie: {
61 | maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
62 | }
63 | // TIP: you may want to add a session store here
64 | // so that unifile memorizes the users connexion data
65 | // into a database or file - e.g. https://www.npmjs.com/package/connect-fs2
66 | }));
67 |
68 | // use unifile as a middleware
69 | app.use(unifile.middleware(express, app, options));
70 |
71 | // server 'loop'
72 | var port = process.env.PORT || 6805; // 6805 is the date of sexual revolution started in paris france 8-)
73 | app.listen(port, function() {
74 | console.log('Listening on ' + port);
75 | });
76 |
77 |
78 | /*
79 | // catch all errors and prevent nodejs to crash, production mode
80 | process.on('uncaughtException', function(err) {
81 | console.log ('---------------------');
82 | console.error('---------------------', 'Caught exception: ', err, '---------------------');
83 | console.log ('---------------------');
84 | });
85 | */
86 |
--------------------------------------------------------------------------------
/src/ce/api/CloudExplorer.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.api;
13 |
14 | import js.html.IFrameElement;
15 |
16 | import ce.core.config.Config;
17 | import ce.core.model.CEBlob;
18 | import ce.core.model.CEError;
19 |
20 | import ce.core.model.api.PickOptions;
21 | import ce.core.model.api.ReadOptions;
22 | import ce.core.model.api.ExportOptions;
23 | import ce.core.model.api.WriteOptions;
24 |
25 | import ce.core.Controller;
26 |
27 | @:expose("ce.api.CloudExplorer")
28 | class CloudExplorer {
29 |
30 | ///
31 | // API
32 | //
33 |
34 | /**
35 | * Returns a fresh instance of Cloud Explorer
36 | */
37 | static function get(? iframeEltId : Null) : CloudExplorer {
38 |
39 | return new CloudExplorer(iframeEltId);
40 | }
41 |
42 | /**
43 | * filepicker.pick([options], onSuccess(CEBlob){}, onError(CEError){})
44 | * @see https://developers.inkfilepicker.com/docs/web/#pick
45 | */
46 | public function pick(arg1 : Dynamic, arg2 : Dynamic, ? arg3 : Dynamic) {
47 |
48 | if (arg1 == null || arg2 == null) {
49 |
50 | throw "Missing mandatory parameters for CloudExplorer.pick(onSuccess : CEBlob -> Void, onError : CEError -> Void)";
51 | }
52 | var options : Null = arg3 != null ? arg1 : null;
53 | var onSuccess : CEBlob -> Void = options != null ? arg2 : arg1;
54 | var onError : CEError -> Void = options != null ? arg3 : arg2;
55 | trace("options: "+options+" onSuccess: "+onSuccess+" onError: "+onError);
56 | ctrl.pick(options, onSuccess, onError);
57 | }
58 |
59 | /**
60 | * @see https://developers.inkfilepicker.com/docs/web/#read
61 | */
62 | //public function read(input : CEBlob, ? options : ReadOptions, onSuccess : String -> Void, onError : CEError -> Void, onProgress : Int -> Void) {
63 | public function read(arg1 : Dynamic, arg2 : Dynamic, arg3 : Dynamic, ? arg4 : Dynamic, ? arg5 : Dynamic) {
64 |
65 | var input : CEBlob = arg1; // TODO The object to read. Can be an CEBlob, a URL, a DOM File Object, or an .
66 |
67 | var options : Null = (Reflect.isObject(arg2)) ? arg2 : null;
68 |
69 | var onSuccess : String -> Void = options == null ? arg2 : arg3;
70 | var onError : CEError -> Void = options == null ? arg3 : arg4;
71 | var onProgress : Int -> Void = options == null ? arg4 : arg5;
72 |
73 | ctrl.read(input, options, onSuccess, onError, onProgress);
74 | }
75 |
76 | /**
77 | * @see https://developers.inkfilepicker.com/docs/web/#export
78 | */
79 | public function exportFile(arg1 : Dynamic, arg2 : Dynamic, arg3 : Dynamic, ? arg4 : Dynamic) : Void {
80 |
81 | var input : CEBlob = arg1; // An InkBlob or URL pointing to data you'd like to export. If you have a DOM File object or raw data, you can use the filepicker.store call to first generate an InkBlob
82 |
83 | var options : Null = (Reflect.isObject(arg2)) ? arg2 : null;
84 |
85 | var onSuccess : CEBlob -> Void = options == null ? arg2 : arg3;
86 | var onError : CEError -> Void = options == null ? arg3 : arg4;
87 |
88 | ctrl.exportFile(input, options, onSuccess, onError);
89 | }
90 |
91 | /**
92 | * @see https://developers.inkfilepicker.com/docs/web/#write
93 | */
94 | public function write(arg1 : Dynamic, arg2 : Dynamic, arg3 : Dynamic, arg4 : Dynamic, ? arg5 : Dynamic, ? arg6 : Dynamic) : Void {
95 |
96 | var target : CEBlob = arg1;
97 | var data : Dynamic = arg2;
98 |
99 | var options : Null = (Reflect.isObject(arg3)) ? arg3 : null;
100 |
101 | var onSuccess : CEBlob -> Void = options == null ? arg3 : arg4;
102 | var onError : CEError -> Void = options == null ? arg4 : arg5;
103 | var onProgress : Null Void> = options == null ? arg5 : arg6;
104 |
105 | ctrl.write(target, data, options, onSuccess, onError, onProgress);
106 | }
107 |
108 | /**
109 | * Non-Ink API method to check if the user is currently logged into a service.
110 | */
111 | public function isLoggedIn(arg1 : Dynamic, ? arg2 : Dynamic, ? arg3 : Dynamic) : Void {
112 |
113 | var srvName : String = arg1;
114 | var onSuccess : Bool -> Void = arg2;
115 | var onError : CEError -> Void = arg3;
116 |
117 | return ctrl.isLoggedIn(srvName, onSuccess, onError);
118 | }
119 |
120 | /**
121 | * Non-Ink API method to ask the user to authorize a service.
122 | */
123 | public function requestAuthorize(arg1 : Dynamic, ? arg2 : Dynamic, ? arg3 : Dynamic) : Void {
124 |
125 | var srvName : String = arg1;
126 | var onSuccess : Void -> Void = arg2;
127 | var onError : CEError -> Void = arg3;
128 |
129 | ctrl.requestAuthorize(srvName, onSuccess, onError);
130 | }
131 |
132 |
133 | ///
134 | // INTERNALS
135 | //
136 |
137 | var ctrl : ce.core.Controller;
138 |
139 | private function new(? iframeEltId : Null) {
140 |
141 | var ceIframe : IFrameElement = iframeEltId != null ? cast js.Browser.document.getElementById(iframeEltId) : null;
142 |
143 | var config : Config = new Config(); // TODO
144 |
145 | if (ceIframe == null) {
146 |
147 | ceIframe = js.Browser.document.createIFrameElement();
148 |
149 | js.Browser.document.body.appendChild(ceIframe);
150 |
151 | } else {
152 |
153 | if (ceIframe.src != null) {
154 |
155 | for (ca in ceIframe.attributes) {
156 |
157 | if (ca.nodeName.indexOf("data-ce-") == 0) {
158 |
159 | config.readProperty(ca.nodeName.substr(8), ca.nodeValue);
160 | }
161 | }
162 | }
163 | }
164 |
165 | ctrl = new Controller(config, ceIframe);
166 | }
167 | }
--------------------------------------------------------------------------------
/src/ce/core/config/Config.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.config;
13 |
14 | /*
15 | TODO
16 | - list of enabled services ?
17 | */
18 | class Config {
19 |
20 | static inline var PROP_NAME_UNIFILE_ENDPOINT : String = "unifile-url";
21 |
22 | static inline var PROP_NAME_CE_PATH : String = "path";
23 |
24 | static inline var PROP_VALUE_DEFAULT_UNIFILE_ENDPOINT : String = "http://localhost:6805/api/1.0/";
25 |
26 | static inline var PROP_VALUE_DEFAULT_CE_PATH : String = "";
27 |
28 | public function new() { }
29 |
30 | /**
31 | * The unifile service endpoint / url
32 | * @see https://github.com/silexlabs/unifile/
33 | */
34 | public var unifileEndpoint (default, null) : String = PROP_VALUE_DEFAULT_UNIFILE_ENDPOINT;
35 |
36 | /**
37 | * The cloud-explorer/ folder path
38 | */
39 | public var path (default, null) : String = PROP_VALUE_DEFAULT_CE_PATH;
40 |
41 | ///
42 | // API
43 | //
44 |
45 | public function readProperty(name : String, value : String) : Void {
46 |
47 | switch(name) {
48 |
49 | case PROP_NAME_UNIFILE_ENDPOINT:
50 |
51 | unifileEndpoint = value;
52 |
53 | case PROP_NAME_CE_PATH:
54 |
55 | path = value;
56 |
57 | default:
58 |
59 | throw "Unexpected configuration property " + name;
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/src/ce/core/ctrl/ErrorCtrl.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.ctrl;
13 |
14 | import ce.core.view.Application;
15 |
16 | import ce.core.model.State;
17 | import ce.core.model.CEError;
18 | import ce.core.model.unifile.UnifileError;
19 |
20 | class ErrorCtrl {
21 |
22 | public function new(parent : Controller, state : State, application : Application) {
23 |
24 | this.parent = parent;
25 | this.state = state;
26 | this.application = application;
27 | }
28 |
29 | var state : State;
30 |
31 | var application : Application;
32 |
33 | var parent : Controller;
34 |
35 | ///
36 | // API
37 | //
38 |
39 | public function manageListSrvError(msg : String) : Void {
40 |
41 | switch (state.currentMode) {
42 |
43 | case SingleFileSelection(_), SingleFileExport(_) :
44 |
45 | setError(msg);
46 |
47 | case IsLoggedIn(_, onError, _) :
48 |
49 | onError(new CEError(500)); // FIXME
50 |
51 | case RequestAuthorize(_, onError, _) :
52 |
53 | onError(new CEError(500)); // FIXME
54 |
55 | state.displayState = false;
56 | }
57 | }
58 |
59 | public function manageConnectError(msg : String) : Void {
60 |
61 | switch (state.currentMode) {
62 |
63 | case SingleFileSelection(_), SingleFileExport(_) :
64 |
65 | setError(msg);
66 |
67 | case RequestAuthorize(_, onError, _) :
68 |
69 | onError(new CEError(500)); // FIXME
70 |
71 | state.displayState = false;
72 |
73 | case IsLoggedIn(_) :
74 |
75 | throw "unexpected mode " + state.currentMode;
76 | }
77 | }
78 |
79 | public function manageLoginError(msg : String) : Void {
80 |
81 | switch (state.currentMode) {
82 |
83 | case SingleFileSelection(_), SingleFileExport(_) :
84 |
85 | setError(msg);
86 |
87 | case RequestAuthorize(_, onError, _) :
88 |
89 | onError(new CEError(500)); // FIXME
90 |
91 | state.displayState = false;
92 |
93 | case IsLoggedIn(_) :
94 |
95 | throw "unexpected mode " + state.currentMode;
96 | }
97 | }
98 |
99 | /**
100 | * FIXME UnifileError should probably be converted to a generic error in service layer
101 | */
102 | public function setUnifileError(err : UnifileError) : Void {
103 |
104 | if (err.code == CEError.CODE_UNAUTHORIZED) {
105 |
106 | var srv = state.currentLocation.service;
107 |
108 | state.serviceList.get(srv).isLoggedIn = false;
109 |
110 | parent.connect(srv);
111 |
112 | } else {
113 |
114 | setError(err.message);
115 | }
116 | }
117 |
118 |
119 | public function setError(msg : String) : Void {
120 |
121 | application.setLoaderDisplayed(false); // FIXME should this be there ?
122 |
123 | application.alertPopup.setMsg(msg, 0, [{ msg: "Continue", cb: function() { application.setAlertPopupDisplayed(false); }}]);
124 |
125 | application.setAlertPopupDisplayed(true);
126 | }
127 | }
--------------------------------------------------------------------------------
/src/ce/core/model/CEBlob.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | /**
15 | * @see https://developers.inkfilepicker.com/docs/web/#inkblob
16 | */
17 | typedef CEBlob = {
18 |
19 | /**
20 | * The most critical part of the file, the url points to where the file is stored and acts as a sort of "file path".
21 | * The url is what is used when making the underlying GET and POST calls to Ink when you do a filepicker.read or
22 | * filepicker.write call.
23 | */
24 | var url : String;
25 |
26 | /**
27 | * The name of the file, if available.
28 | */
29 | var filename : Null;
30 |
31 | /**
32 | * The mimetype of the file, if available.
33 | */
34 | var mimetype : Null;
35 |
36 | /**
37 | * The size of the file in bytes, if available. We will attach this directly to the InkBlob when we have it,
38 | * otherwise you can always get the size by calling filepicker.stat
39 | */
40 | var size : Null;
41 |
42 | /**
43 | * If the file was stored in one of the file stores you specified or configured (S3, Rackspace, Azure, etc.),
44 | * this parameter will tell you where in the file store this file was put.
45 | */
46 | var key : Null;
47 |
48 | /**
49 | * If the file was stored in one of the file stores you specified or configured (S3, Rackspace, Azure, etc.),
50 | * this parameter will tell you in which container this file was put.
51 | */
52 | var container : Null;
53 |
54 | /**
55 | * This flag specifies whether the underlying file is writeable. In most cases this will be true, but if a user
56 | * uploads a photo from facebook, for instance, the original file cannot be written to. In these cases, you should
57 | * use the filepicker.exportFile call as a way to give the user the ability to save their content.
58 | */
59 | var isWriteable : Bool;
60 |
61 | /**
62 | * The path of the InkBlob indicates its position in the hierarchy of files uploaded when {folders:true} is set.
63 | * In situations where the file was not uploaded as part of or along with a folder, path will not be defined.
64 | */
65 | var path : Null;
66 | }
--------------------------------------------------------------------------------
/src/ce/core/model/CEError.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | /**
15 | * @see https://developers.inkfilepicker.com/docs/web/#errors
16 | */
17 | class CEError {
18 |
19 | /**
20 | * Bad parameters were passed to the server. This often will be the case when you turned on security but
21 | * haven't passed up a policy or signature.
22 | */
23 | static public inline var CODE_BAD_PARAMETERS : Int = 400;
24 |
25 | static public inline var CODE_UNAUTHORIZED : Int = 401;
26 |
27 | /**
28 | * The policy and/or signature don't allow you to make this request.
29 | * @see https://developers.inkfilepicker.com/docs/security/
30 | */
31 | static public inline var CODE_INVALID_REQUEST : Int = 403;
32 |
33 | public function new(code : Int) { }
34 |
35 | public var code (default, null) : Int;
36 |
37 | public function toString() : String {
38 |
39 | return Std.string(code);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/ce/core/model/DisplayMode.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | enum DisplayMode {
15 | List;
16 | Icons;
17 | }
--------------------------------------------------------------------------------
/src/ce/core/model/Location.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | class Location {
15 |
16 | public function new(s : Service, p : String) {
17 |
18 | this.service = s;
19 | this.path = p;
20 | }
21 |
22 | public var service (default, set) : Service;
23 |
24 | public var path (default, set) : String;
25 |
26 |
27 | ///
28 | // CALLBACKS
29 | //
30 |
31 | public dynamic function onChanged() { }
32 |
33 |
34 | ///
35 | // GETTERS / SETTERS
36 | //
37 |
38 | public function set_service(v : Service) : Service {
39 |
40 | if (v == service) {
41 |
42 | return v;
43 | }
44 | service = v;
45 |
46 | onChanged();
47 |
48 | return service;
49 | }
50 |
51 | public function set_path(v : String) : String {
52 |
53 | if (v == path) {
54 |
55 | return v;
56 | }
57 | path = v;
58 |
59 | onChanged();
60 |
61 | return path;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/ce/core/model/Mode.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | import ce.core.model.api.PickOptions;
15 | import ce.core.model.api.ExportOptions;
16 |
17 | enum Mode {
18 |
19 | SingleFileSelection(onSuccess : CEBlob -> Void, onError : CEError -> Void, options : Null);
20 | SingleFileExport(onSuccess : CEBlob -> Void, onError : CEError -> Void, input : CEBlob, options : Null);
21 | IsLoggedIn(onSuccess : Bool -> Void, onError : CEError -> Void, srvName : String);
22 | RequestAuthorize(onSuccess : Void -> Void, onError : CEError -> Void, srvName : String);
23 | }
--------------------------------------------------------------------------------
/src/ce/core/model/Service.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | @:enum
15 | abstract Service(String) from String to String {
16 | var Dropbox = "dropbox";
17 | var Www = "www";
18 | var Ftp = "ftp";
19 | }
--------------------------------------------------------------------------------
/src/ce/core/model/SortField.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | @:enum
15 | abstract SortField(String) to String {
16 | var Name = "name";
17 | var Type = "type";
18 | var LastUpdate = "lastUpdate";
19 | }
--------------------------------------------------------------------------------
/src/ce/core/model/SortOrder.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | @:enum
15 | abstract SortOrder(String) to String {
16 | var Asc = "asc";
17 | var Desc = "desc";
18 | }
--------------------------------------------------------------------------------
/src/ce/core/model/State.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model;
13 |
14 | import ce.core.model.unifile.Service;
15 | import ce.core.model.unifile.File;
16 | import ce.core.model.Location;
17 | import ce.core.model.Mode;
18 | import ce.core.model.DisplayMode;
19 | import ce.core.model.SortField;
20 | import ce.core.model.SortOrder;
21 |
22 | import haxe.ds.StringMap;
23 |
24 | class State {
25 |
26 | public function new() { }
27 |
28 | public var readyState (default, set) : Bool = false;
29 |
30 | public var displayState (default, set) : Bool = false;
31 |
32 | public var newFolderMode (default, set) : Bool = false;
33 |
34 | public var displayMode (default, set) : Null = null;
35 |
36 | public var serviceList (default, set) : Null> = null;
37 |
38 | public var currentLocation (default, set) : Null = null;
39 |
40 | public var currentFileList (default, set) : Null> = null;
41 |
42 | public var currentMode (default, set) : Null = null;
43 |
44 | public var currentSortField (default, set) : Null = null;
45 |
46 | public var currentSortOrder (default, set) : Null = null;
47 |
48 |
49 | ///
50 | // CALLBACKS
51 | //
52 |
53 | public dynamic function onReadyStateChanged() { }
54 |
55 | public dynamic function onDisplayStateChanged() { }
56 |
57 | public dynamic function onServiceListChanged() { }
58 |
59 | public dynamic function onCurrentLocationChanged() { }
60 |
61 | public dynamic function onCurrentFileListChanged() { }
62 |
63 | public dynamic function onCurrentModeChanged() { }
64 |
65 | public dynamic function onNewFolderModeChanged() { }
66 |
67 | public dynamic function onDisplayModeChanged() { }
68 |
69 | public dynamic function onCurrentSortFieldChanged() { }
70 |
71 | public dynamic function onCurrentSortOrderChanged() { }
72 |
73 | public dynamic function onServiceLoginStateChanged(srvName : String) { }
74 |
75 |
76 | ///
77 | // SETTERS
78 | //
79 |
80 | public function set_currentSortField(v : Null) : Null {
81 |
82 | if (v == currentSortField) {
83 |
84 | return currentSortField;
85 | }
86 | currentSortField = v;
87 | currentSortOrder = Asc; // setting new sort field also reset the order
88 |
89 | onCurrentSortFieldChanged();
90 |
91 | return currentSortField;
92 | }
93 |
94 | public function set_currentSortOrder(v : Null) : Null {
95 |
96 | if (v == currentSortOrder) {
97 |
98 | return currentSortOrder;
99 | }
100 | currentSortOrder = v;
101 |
102 | onCurrentSortOrderChanged();
103 |
104 | return currentSortOrder;
105 | }
106 |
107 | public function set_newFolderMode(v : Bool) : Bool {
108 |
109 | if (v == newFolderMode) {
110 |
111 | return newFolderMode;
112 | }
113 | newFolderMode = v;
114 |
115 | onNewFolderModeChanged();
116 |
117 | return newFolderMode;
118 | }
119 |
120 | public function set_displayMode(v : DisplayMode) : DisplayMode {
121 |
122 | if (v == displayMode) {
123 |
124 | return displayMode;
125 | }
126 | displayMode = v;
127 |
128 | onDisplayModeChanged();
129 |
130 | return displayMode;
131 | }
132 |
133 | public function set_serviceList(v : Null>) : Null> {
134 |
135 | if (v == serviceList) {
136 |
137 | return v;
138 | }
139 | serviceList = v;
140 |
141 | for (s in serviceList) {
142 |
143 | s.onLoginStateChanged = function() { onServiceLoginStateChanged(s.name); }
144 | }
145 |
146 | onServiceListChanged();
147 |
148 | return serviceList;
149 | }
150 |
151 | public function set_currentFileList(v : Null>) : Null> {
152 |
153 | if (v == currentFileList) {
154 |
155 | return v;
156 | }
157 | currentFileList = v;
158 | // reset both sort field and sort order
159 | currentSortField = Name;
160 | currentSortOrder = Asc;
161 |
162 | onCurrentFileListChanged();
163 |
164 | return currentFileList;
165 | }
166 |
167 | public function set_currentMode(v : Null) : Null {
168 |
169 | if (v == currentMode) {
170 |
171 | return v;
172 | }
173 | currentMode = v;
174 |
175 | onCurrentModeChanged();
176 |
177 | return currentMode;
178 | }
179 |
180 | public function set_readyState(v : Bool) : Bool {
181 |
182 | if (v == readyState) {
183 |
184 | return v;
185 | }
186 | readyState = v;
187 |
188 | onReadyStateChanged();
189 |
190 | return readyState;
191 | }
192 |
193 | public function set_displayState(v : Bool) : Bool {
194 |
195 | if (v == displayState) {
196 |
197 | return v;
198 | }
199 | displayState = v;
200 |
201 | onDisplayStateChanged();
202 |
203 | return displayState;
204 | }
205 |
206 | public function set_currentLocation(v : Location) : Location {
207 |
208 | if (v == currentLocation) {
209 |
210 | return v;
211 | }
212 | currentLocation = v;
213 |
214 | if (currentLocation != null) {
215 |
216 | currentLocation.onChanged = function() { onCurrentLocationChanged(); }
217 | }
218 |
219 | onCurrentLocationChanged();
220 |
221 | return currentLocation;
222 | }
223 | }
--------------------------------------------------------------------------------
/src/ce/core/model/api/ExportOptions.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.api;
13 |
14 | typedef ExportOptions = {
15 |
16 | /**
17 | * The mimetype of the file. Note that we try to guess the file extension of the file from this,
18 | * so image/png will result in a .png ending while image/* will not have an ending. If you don't
19 | * specify the mimetype, we will try to guess, otherwise will fall back to letting the user save
20 | * it as whatever extension they choose, which may cause issues (if they try to save text to
21 | * Facebook, for instance).
22 | */
23 | @:optional var mimetype : Null;
24 |
25 | /**
26 | * Specify the type of the file by extension rather than mimetype. Don't use this option with
27 | * mimetype(s) specified as well.
28 | */
29 | @:optional var extension : Null;
30 | }
--------------------------------------------------------------------------------
/src/ce/core/model/api/PickOptions.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.api;
13 |
14 | /**
15 | * An optional dictionary of key-value pairs that specify how the picker behaves.
16 | */
17 | typedef PickOptions = {
18 |
19 | /**
20 | * Specify the type of file that the user is allowed to pick. For example, if
21 | * you wanted images, specify image/* and users will only be able to select
22 | * images to upload. Similarly, you could specify application/msword for only
23 | * Word Documents.
24 | * You can also specify an array of mimetypes to allow the user to select a
25 | * file from any of the given types.
26 | */
27 | @:optional var mimetype : Null;
28 |
29 | @:optional var mimetypes : Null>;
30 |
31 | /**
32 | * Specify the type of file that the user is allowed to pick by extension.
33 | * Don't use this option with mimetype(s) specified as well
34 | * You can also specify an array of extensions to allow the user to select
35 | * a file from any of the given types.
36 | */
37 | @:optional var extension : Null;
38 |
39 | @:optional var extensions : Null>;
40 |
41 | /**
42 | * Where to load the Ink file picker UI into. Possible values are "window",
43 | * "modal", or the id of an iframe in the current document. Defaults to "modal".
44 | * Note that if the browser disables 3rd party cookies, the dialog will
45 | * automatically fall back to being served in a new window.
46 | */
47 | // var container : String;
48 |
49 | /**
50 | * Specify which services are displayed on the left panel, and in which order, by
51 | * name.
52 | * Be sure that the services you select are compatible with the mimetype(s) or
53 | * extension(s) specified.
54 | * Currently, the Ink file picker supports the following services, and we're adding
55 | * more all the time:
56 | */
57 | // service
58 | // services
59 |
60 | /**
61 | * Specifies which service to show upon opening. If not set, the user is shown their
62 | * most recently used location, or otherwise the computer upload page.
63 | */
64 | // openTo
65 |
66 | /**
67 | * Limit file uploads to be at max maxSize bytes.
68 | */
69 | // maxSize
70 |
71 | /**
72 | * Useful when developing, makes it so the onSuccess callback is fired immediately with
73 | * dummy data.
74 | */
75 | //var debug : Bool = false;
76 |
77 | /**
78 | * If you have security enabled, you'll need to have a valid Ink file picker policy and signature in order to perform the requested call. This allows you to select who can and cannot perform certain actions on your site.
79 | * @see https://developers.inkfilepicker.com/docs/security/
80 | */
81 | // policy: POLICY
82 | // signature: SIGNATURE
83 | }
--------------------------------------------------------------------------------
/src/ce/core/model/api/ReadOptions.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.api;
13 |
14 | typedef ReadOptions = {
15 |
16 | /**
17 | * Specify that you want the data to be retuned converted into base 64.
18 | * This is very useful when the contents of the file are binary rather than text, for example with images.
19 | * For your convenience, the filepicker.base64.encode and filepicker.base64.decode methods are available for your convenience.
20 | */
21 | @:optional var base64encode : Bool;
22 |
23 | /**
24 | * If you know you want the file to be read as text, the Ink files library can be more efficient if you tell it to convert
25 | * everything into text.
26 | */
27 | @:optional var asText : Bool;
28 |
29 | /**
30 | * Whether the data should be pulled from the browser's cache, if possible. Defaults to false.
31 | * Can make reads faster if you're sure the underlying file won't change.
32 | */
33 | @:optional var cache : Bool;
34 | }
--------------------------------------------------------------------------------
/src/ce/core/model/api/WriteOptions.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.api;
13 |
14 | typedef WriteOptions = {
15 |
16 | /**
17 | * Specify that you want the data to be first decoded from base64 before being written to the file.
18 | * For example, if you have base64 encoded image data, you can use this flag to first decode the data
19 | * before writing the image file.
20 | */
21 | @:optional var base64decode : Bool;
22 |
23 | }
--------------------------------------------------------------------------------
/src/ce/core/model/oauth/OAuthResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.oauth;
13 |
14 | typedef OAuthResult = {
15 |
16 | /**
17 | * true if the user chooses not to authorize the application.
18 | */
19 | @:optional var notApproved : Bool;
20 |
21 | /**
22 | * The request token that was just authorized. The request token secret isn't sent back.
23 | */
24 | @:optional var oauthToken : String;
25 |
26 | /**
27 | * The user's unique Dropbox ID.
28 | */
29 | @:optional var uid : String;
30 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/Account.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef QuotaInfo = {
15 |
16 | var available : Int;
17 | var used : Int;
18 | }
19 |
20 | typedef Account = {
21 |
22 | var displayName : String;
23 | var quotaInfo : Null;
24 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/ConnectResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef ConnectResult = {
15 |
16 | var success : Bool;
17 | var message : String;
18 | var authorizeUrl : String;
19 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/File.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef File = {
15 |
16 | var name : String;
17 | var bytes : Int;
18 | var modified : Null;
19 | var isDir : Bool;
20 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/LoginResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef LoginResult = {
15 |
16 | var success : Bool;
17 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/LogoutResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef LogoutResult = {
15 |
16 | var success : Bool;
17 | var message : String;
18 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/Service.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | class Service {
15 |
16 | public function new(n : ce.core.model.Service, dn : String, is : String, d : String, v : Bool, il : Bool, ic : Bool, ioa : Bool, ? a : Null) {
17 |
18 | this.name = n;
19 | this.displayName = dn;
20 | this.imageSmall = is;
21 | this.description = d;
22 | this.visible = v;
23 | this.isLoggedIn = il;
24 | this.isConnected = ic;
25 | this.isOAuth = ioa;
26 | this.account = a;
27 | }
28 |
29 | public var name (default, null) : ce.core.model.Service;
30 | public var displayName (default, null) : String;
31 | public var imageSmall (default, null) : String;
32 | public var description (default, null) : String;
33 | public var visible (default, null) : Bool;
34 | public var isLoggedIn (default, set) : Bool;
35 | public var isOAuth (default, null) : Bool;
36 | public var isConnected (default, default) : Bool;
37 | public var account (default, set) : Null;
38 |
39 | ///
40 | // CALLBACKS
41 | //
42 |
43 | public dynamic function onLoginStateChanged() : Void { }
44 |
45 | public dynamic function onAccountChanged() : Void { }
46 |
47 |
48 | ///
49 | // GETTERS / SETTERS
50 | //
51 |
52 | public function set_isLoggedIn(v : Bool) : Bool {
53 |
54 | if (v == isLoggedIn) {
55 |
56 | return v;
57 | }
58 | isLoggedIn = v;
59 |
60 | onLoginStateChanged();
61 |
62 | return isLoggedIn;
63 | }
64 |
65 | public function set_account(v : Null) : Null {
66 |
67 | if (v == account) {
68 |
69 | return v;
70 | }
71 | account = v;
72 |
73 | onAccountChanged();
74 |
75 | return account;
76 | }
77 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/UnifileError.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef UnifileError = {
15 |
16 | var success : Bool;
17 | var code : Int;
18 | var message : String;
19 | }
--------------------------------------------------------------------------------
/src/ce/core/model/unifile/UploadResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.model.unifile;
13 |
14 | typedef UploadResult = {
15 |
16 | var success : Bool;
17 |
18 | }
19 | /* FIXME can also be :
20 | [
21 | {
22 | "path": "//logo.png",
23 | "status": {
24 | "success": true
25 | }
26 | },
27 | {
28 | "path": "//header.png",
29 | "status": {
30 | "success": true
31 | }
32 | }
33 | ]
34 | */
--------------------------------------------------------------------------------
/src/ce/core/parser/json/Json2Primitive.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.json;
13 |
14 | /**
15 | * Common methods to parse JSON feeds
16 | */
17 | class Json2Primitive {
18 |
19 | static public function checkPath( node : Dynamic, path : String, optional : Bool = false ) : Null {
20 | //trace("checking "+path+" on "+node);
21 | var pathes : Array = path.split('.');
22 |
23 | //return doCheckPath( node, pathes, optional );
24 | var n : Null = doCheckPath( node, pathes, optional );
25 |
26 | if ( n == null && !optional ) {
27 |
28 | trace(path + " not found !");
29 | }
30 | //trace("checking "+path+" returned "+n);
31 | return n;
32 | }
33 |
34 | static public function doCheckPath( node : Dynamic, pathes : Array, optional : Bool = false ) : Null {
35 |
36 | var p = pathes.shift();
37 |
38 | if ( !Reflect.hasField( node, p ) || Reflect.field(node, p) == null ) {
39 |
40 | if (!optional) {
41 |
42 | trace(p+' not found !');
43 | // TODO throw ?
44 | }
45 | return null;
46 | }
47 | if ( pathes.length > 0 ) {
48 |
49 | return doCheckPath( Reflect.field(node, p), pathes, optional );
50 | }
51 |
52 | return Reflect.field( node, p );
53 | }
54 |
55 | static public function node2String( node : Dynamic, path : String, nullable : Bool = false ) : Null {
56 |
57 | var n : Null = checkPath( node, path, nullable );
58 |
59 | if( n == null ) {
60 |
61 | if (!nullable) {
62 |
63 | // TODO throw ?
64 | }
65 | return null;
66 | }
67 | return Std.string( n );
68 | }
69 |
70 | static public function node2Float( node : Dynamic, path : String, nullable : Bool = false ) : Null {
71 |
72 | return Std.parseFloat( node2String( node, path, nullable ) );
73 | }
74 |
75 | static public function node2Int( node : Dynamic, path : String, nullable : Bool = false ) : Null {
76 |
77 | return Std.parseInt( node2String( node, path, nullable ) );
78 | }
79 |
80 | static public function node2Bool( node : Dynamic, path : String, nullable : Bool = false ) : Bool {
81 |
82 | var v : Null = node2String( node, path, nullable );
83 |
84 | return ( v != null ? (v == "true" || v == "1") : false );
85 | }
86 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/oauth/Str2OAuthResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.oauth;
13 |
14 | import ce.core.model.oauth.OAuthResult;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Str2OAuthResult {
21 |
22 | static inline var PARAM_NOT_APPROVED : String = "not_approved";
23 | static inline var PARAM_OAUTH_TOKEN : String = "oauth_token";
24 | static inline var PARAM_UID : String = "uid";
25 |
26 | static public function parse(dataStr : String) : OAuthResult {
27 |
28 | if (dataStr.indexOf('?') == 0) {
29 | dataStr = dataStr.substr(1);
30 | }
31 | var dataArr : Array = dataStr.split("&");
32 |
33 | var res : OAuthResult = {};
34 |
35 | for (pStr in dataArr) {
36 |
37 | var kv : Array = pStr.split("=");
38 |
39 | res = parseValue(res, kv[0], kv[1]);
40 | }
41 | return res;
42 | }
43 |
44 | static private function parseValue(obj : OAuthResult, key : String, value : String) : OAuthResult {
45 |
46 | switch (key) {
47 |
48 | case PARAM_NOT_APPROVED:
49 |
50 | obj.notApproved = value.toLowerCase() == "true" || value == "1" ? true : false;
51 |
52 | case PARAM_OAUTH_TOKEN:
53 |
54 | obj.oauthToken = value;
55 |
56 | case PARAM_UID:
57 |
58 | obj.uid = value;
59 |
60 | default:
61 |
62 | throw "unexpected parameter " + key;
63 | }
64 | return obj;
65 | }
66 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2Account.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.Account;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2Account {
21 |
22 | static public function parseAccount(? dataStr : String, ? obj : Dynamic) : Null {
23 |
24 | if (obj == null) {
25 |
26 | if (dataStr == null) return null;
27 |
28 | obj = Json.parse( dataStr );
29 | }
30 |
31 | return {
32 | displayName: Json2Primitive.node2String(obj, "display_name", false),
33 | quotaInfo: Reflect.hasField(obj, "quota_info") ? parseQuotaInfo(Reflect.field(obj, "quota_info")) : null
34 | };
35 | }
36 |
37 | static public function parseQuotaInfo(obj : Dynamic) : QuotaInfo {
38 |
39 | return {
40 | available: Json2Primitive.node2Int(obj, "available", false),
41 | used: Json2Primitive.node2Int(obj, "used", false)
42 | };
43 | }
44 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2ConnectResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.ConnectResult;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2ConnectResult {
21 |
22 | static public function parse(dataStr : String) : ConnectResult {
23 |
24 | var obj : Dynamic = Json.parse( dataStr );
25 |
26 | return {
27 | success: Json2Primitive.node2Bool(obj, "success", false),
28 | message: Json2Primitive.node2String(obj, "message", false),
29 | authorizeUrl: Json2Primitive.node2String(obj, "authorize_url", false),
30 | };
31 | }
32 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2File.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.File;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2File {
21 |
22 | static public function parseFileCollection(dataStr : String) : Array {
23 |
24 | var col : Array = Json.parse( dataStr );
25 |
26 | var fileCol : Array = new Array();
27 |
28 | for (f in col) {
29 |
30 | fileCol.push(parseFile(f));
31 | }
32 |
33 | return fileCol;
34 | }
35 |
36 | static public function parseFile(obj : Dynamic) : File {
37 |
38 | var dStr : String = Json2Primitive.node2String(obj, "modified", false);
39 |
40 | return {
41 | name: Json2Primitive.node2String(obj, "name", false),
42 | bytes: Json2Primitive.node2Int(obj, "bytes", false),
43 | modified: dStr != null ? Date.fromTime( untyped __js__("new Date(dStr).getTime()") ) : null, // FIXME would be better to get a timestamp from unifie
44 | isDir: Json2Primitive.node2Bool(obj, "is_dir", false)
45 | };
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2LoginResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.LoginResult;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2LoginResult {
21 |
22 | static public function parse(dataStr : String) : LoginResult {
23 |
24 | var obj : Dynamic = Json.parse( dataStr );
25 |
26 | return {
27 | success: Json2Primitive.node2Bool(obj, "success", false)
28 | };
29 | }
30 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2LogoutResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.LogoutResult;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2LogoutResult {
21 |
22 | static public function parse(dataStr : String) : LogoutResult {
23 |
24 | var obj : Dynamic = Json.parse( dataStr );
25 |
26 | return {
27 | success: Json2Primitive.node2Bool(obj, "success", false),
28 | message: Json2Primitive.node2String(obj, "message", false)
29 | };
30 | }
31 | }
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2Service.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.Service;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2Service {
21 |
22 | static public function parseServiceCollection(dataStr : String) : Array {
23 |
24 | var col : Array = Json.parse( dataStr );
25 |
26 | var serviceCol : Array = new Array();
27 |
28 | for (s in col) {
29 |
30 | serviceCol.push(parseService(s));
31 | }
32 |
33 | return serviceCol;
34 | }
35 |
36 | static public function parseService(obj : Dynamic) : Service {
37 |
38 | return new Service(
39 | Json2Primitive.node2String(obj, "name", false),
40 | Json2Primitive.node2String(obj, "display_name", false),
41 | Json2Primitive.node2String(obj, "image_small", false),
42 | Json2Primitive.node2String(obj, "description", false),
43 | Json2Primitive.node2Bool(obj, "visible", false),
44 | Json2Primitive.node2Bool(obj, "isLoggedIn", false),
45 | Json2Primitive.node2Bool(obj, "isConnected", false),
46 | Json2Primitive.node2Bool(obj, "isOAuth", false),
47 | Reflect.hasField(obj, "user") ? Json2Account.parseAccount(null, Reflect.field(obj, "user")) : null
48 | );
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2UnifileError.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.UnifileError;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2UnifileError {
21 |
22 | static public function parseUnifileError(dataStr : String) : UnifileError {
23 |
24 | var obj : Dynamic = Json.parse( dataStr );
25 |
26 | return {
27 | success: Json2Primitive.node2Bool(obj, "success", false),
28 | code: Json2Primitive.node2Int(obj, "code", false),
29 | message: Json2Primitive.node2String(obj, "message", false)
30 | };
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/ce/core/parser/unifile/Json2UploadResult.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.parser.unifile;
13 |
14 | import ce.core.model.unifile.UploadResult;
15 |
16 | import ce.core.parser.json.Json2Primitive;
17 |
18 | import haxe.Json;
19 |
20 | class Json2UploadResult {
21 |
22 | static public function parse(dataStr : String) : UploadResult {
23 |
24 | var obj : Dynamic = Json.parse( dataStr );
25 |
26 | return {
27 | success: Json2Primitive.node2Bool(obj, "success", false)
28 | };
29 | }
30 | }
--------------------------------------------------------------------------------
/src/ce/core/service/UnifileSrv.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.service;
13 |
14 | import ce.core.config.Config;
15 |
16 | import ce.core.parser.unifile.Json2Service;
17 | import ce.core.parser.unifile.Json2ConnectResult;
18 | import ce.core.parser.unifile.Json2LoginResult;
19 | import ce.core.parser.unifile.Json2Account;
20 | import ce.core.parser.unifile.Json2File;
21 | import ce.core.parser.unifile.Json2LogoutResult;
22 | import ce.core.parser.unifile.Json2UploadResult;
23 | import ce.core.parser.unifile.Json2UnifileError;
24 |
25 | import ce.core.model.unifile.Service;
26 | import ce.core.model.unifile.ConnectResult;
27 | import ce.core.model.unifile.LoginResult;
28 | import ce.core.model.unifile.Account;
29 | import ce.core.model.unifile.File;
30 | import ce.core.model.unifile.LogoutResult;
31 | import ce.core.model.unifile.UploadResult;
32 | import ce.core.model.unifile.UnifileError;
33 |
34 | import js.html.Blob;
35 | import js.html.DOMFormData;
36 | import js.html.XMLHttpRequest;
37 |
38 | import haxe.ds.StringMap;
39 |
40 | using StringTools;
41 |
42 | class UnifileSrv {
43 |
44 | static inline var ENDPOINT_LIST_SERVICES : String = "services/list";
45 | static inline var ENDPOINT_CONNECT : String = "{srv}/connect";
46 | static inline var ENDPOINT_LOGIN : String = "{srv}/login";
47 | static inline var ENDPOINT_ACCOUNT : String = "{srv}/account";
48 | static inline var ENDPOINT_LOGOUT : String = "{srv}/logout";
49 | static inline var ENDPOINT_LS : String = "{srv}/exec/ls/{path}";
50 | static inline var ENDPOINT_RM : String = "{srv}/exec/rm/{path}";
51 | static inline var ENDPOINT_MKDIR : String = "{srv}/exec/mkdir/{path}";
52 | static inline var ENDPOINT_CP : String = "exec/cp";
53 | static inline var ENDPOINT_MV : String = "{srv}/exec/mv/{path}";
54 | static inline var ENDPOINT_GET : String = "{srv}/exec/get/{uri}";
55 | static inline var ENDPOINT_PUT : String = "{srv}/exec/put/{path}";
56 |
57 | public function new(config : Config) : Void {
58 |
59 | this.config = config;
60 | }
61 |
62 | var config : Config;
63 |
64 |
65 | ///
66 | // API
67 | //
68 |
69 | public function generateUrl(srv : String, path : String, filename : String) : String {
70 |
71 | return config.unifileEndpoint + ENDPOINT_GET.replace("{srv}", srv)
72 | .replace("{uri}", path.length > 1 ? path.substr(1) + filename : filename);
73 | }
74 |
75 | public function explodeUrl(url : String) : { srv : String, path : String, filename : String } {
76 |
77 | if (url.indexOf(config.unifileEndpoint) != 0) {
78 |
79 | throw "ERROR: can't convert url to path: " + url;
80 | }
81 | var parsedUrl : String = url.substr(config.unifileEndpoint.length);
82 |
83 | if (parsedUrl.indexOf("/exec/get/") != parsedUrl.indexOf("/")) {
84 |
85 | throw "ERROR: can't convert url to path: " + url;
86 | }
87 | var srv : String = parsedUrl.substr(0, parsedUrl.indexOf("/"));
88 |
89 | parsedUrl = parsedUrl.substr(parsedUrl.indexOf("/exec/get/") + "/exec/get/".length);
90 |
91 | var filename : String = "";
92 | var path : String = "";
93 |
94 | if (parsedUrl.lastIndexOf('/') > -1) {
95 |
96 | filename = parsedUrl.substr(parsedUrl.lastIndexOf('/')+1);
97 | path = parsedUrl.substr(0, parsedUrl.lastIndexOf('/')+1);
98 |
99 | } else {
100 |
101 | filename = parsedUrl;
102 | }
103 |
104 | return { 'srv': srv, 'path': path, 'filename': filename };
105 | }
106 |
107 | public function listServices(onSuccess : StringMap -> Void, onError : UnifileError -> Void) : Void {
108 |
109 | var req : XMLHttpRequest = new XMLHttpRequest();
110 |
111 | req.onload = function(?_) {
112 |
113 | if (req.status != 200) {
114 |
115 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
116 |
117 | onError(err);
118 |
119 | } else {
120 |
121 | var sl : Array = Json2Service.parseServiceCollection(req.responseText);
122 |
123 | var slm : StringMap = new StringMap();
124 |
125 | for (s in sl) {
126 |
127 | slm.set(s.name, s);
128 | }
129 |
130 | onSuccess(slm);
131 | }
132 | }
133 |
134 | req.onerror = function(?_) {
135 |
136 | onError({ success: false, code: 0, message: "The request has failed." });
137 | }
138 |
139 | req.open("GET", config.unifileEndpoint + ENDPOINT_LIST_SERVICES);
140 |
141 | req.send();
142 | }
143 |
144 | public function connect(srv : String, onSuccess : ConnectResult -> Void, onError : UnifileError -> Void) : Void {
145 |
146 | var req : XMLHttpRequest = new XMLHttpRequest();
147 |
148 | req.onload = function(?_) {
149 |
150 | if (req.status != 200) {
151 |
152 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
153 |
154 | onError(err);
155 |
156 | } else {
157 |
158 | onSuccess(Json2ConnectResult.parse(req.responseText));
159 | }
160 | }
161 |
162 | req.onerror = function(?_) {
163 |
164 | onError({ success: false, code: 0, message: "The request has failed." });
165 | }
166 |
167 | req.open("GET", config.unifileEndpoint + ENDPOINT_CONNECT.replace("{srv}", srv));
168 |
169 | req.send();
170 | }
171 |
172 | public function login(srv : String, onSuccess : LoginResult -> Void, onError : UnifileError -> Void) : Void {
173 |
174 | var req : XMLHttpRequest = new XMLHttpRequest();
175 |
176 | req.onload = function(?_) {
177 |
178 | if (req.status != 200) {
179 |
180 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
181 |
182 | onError(err);
183 |
184 | } else {
185 |
186 | onSuccess(Json2LoginResult.parse(req.responseText));
187 | }
188 | }
189 |
190 | req.onerror = function(?_) {
191 |
192 | onError({ success: false, code: 0, message: "The request has failed." });
193 | }
194 |
195 | req.open("GET", config.unifileEndpoint + ENDPOINT_LOGIN.replace("{srv}", srv));
196 |
197 | req.send();
198 | }
199 |
200 | public function account(srv : String, onSuccess : Account -> Void, onError : UnifileError -> Void) : Void {
201 |
202 | var req : XMLHttpRequest = new XMLHttpRequest();
203 |
204 | req.onload = function(?_) {
205 |
206 | if (req.status != 200) {
207 |
208 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
209 |
210 | onError(err);
211 |
212 | } else {
213 |
214 | onSuccess(Json2Account.parseAccount(req.responseText));
215 | }
216 | }
217 |
218 | req.onerror = function(?_) {
219 |
220 | onError({ success: false, code: 0, message: "The request has failed." });
221 | }
222 |
223 | req.open("POST", config.unifileEndpoint + ENDPOINT_ACCOUNT.replace("{srv}", srv));
224 |
225 | req.send();
226 | }
227 |
228 | public function logout(srv : String, onSuccess : LogoutResult -> Void, onError : UnifileError -> Void) : Void {
229 |
230 | var req : XMLHttpRequest = new XMLHttpRequest();
231 |
232 | req.onload = function(?_) {
233 |
234 | if (req.status != 200) {
235 |
236 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
237 |
238 | onError(err);
239 |
240 | } else {
241 |
242 | onSuccess(Json2LogoutResult.parse(req.responseText));
243 | }
244 | }
245 |
246 | req.onerror = function(?_) {
247 |
248 | onError({ success: false, code: 0, message: "The request has failed." });
249 | }
250 |
251 | req.open("GET", config.unifileEndpoint + ENDPOINT_LOGOUT.replace("{srv}", srv));
252 |
253 | req.send();
254 | }
255 |
256 | public function ls(srv : String, path : String, onSuccess : StringMap -> Void, onError : UnifileError -> Void) : Void {
257 |
258 | // on FF, ls// throws an error
259 | path = path == '/' ? '' : path;
260 |
261 | var req : XMLHttpRequest = new XMLHttpRequest();
262 |
263 | req.onload = function(?_) {
264 |
265 | if (req.status != 200) {
266 |
267 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
268 |
269 | onError(err);
270 |
271 | } else {
272 |
273 | var fa : Array = Json2File.parseFileCollection(req.responseText);
274 |
275 | var fsm : StringMap = new StringMap();
276 |
277 | for (f in fa) {
278 |
279 | fsm.set(f.name, f);
280 | }
281 |
282 | onSuccess(fsm);
283 | }
284 | }
285 |
286 | req.onerror = function(?_) {
287 |
288 | onError({ success: false, code: 0, message: "The request has failed." });
289 | }
290 |
291 | req.open("GET", config.unifileEndpoint + ENDPOINT_LS.replace("{srv}", srv).replace("{path}", path), true);
292 |
293 | req.send();
294 | }
295 |
296 | public function rm(srv : String, path : String, onSuccess : Void -> Void, onError : UnifileError -> Void) : Void {
297 |
298 | var req : XMLHttpRequest = new XMLHttpRequest();
299 |
300 | req.onload = function(?_) {
301 |
302 | if (req.status != 200) {
303 |
304 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
305 |
306 | onError(err);
307 |
308 | } else {
309 |
310 | onSuccess();
311 | }
312 | }
313 |
314 | req.onerror = function(?_) {
315 |
316 | onError({ success: false, code: 0, message: "The request has failed." });
317 | }
318 |
319 | req.open("GET", config.unifileEndpoint + ENDPOINT_RM.replace("{srv}", srv).replace("{path}", path), true);
320 |
321 | req.send();
322 | }
323 |
324 | public function mkdir(srv : String, path : String, onSuccess : Void -> Void, onError : UnifileError -> Void) : Void {
325 |
326 | var req : XMLHttpRequest = new XMLHttpRequest();
327 |
328 | req.onload = function(?_) {
329 |
330 | if (req.status != 200) {
331 |
332 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
333 |
334 | onError(err);
335 |
336 | } else {
337 |
338 | onSuccess();
339 | }
340 | }
341 |
342 | req.onerror = function(?_) {
343 |
344 | onError({ success: false, code: 0, message: "The request has failed." });
345 | }
346 |
347 | req.open("GET", config.unifileEndpoint + ENDPOINT_MKDIR.replace("{srv}", srv).replace("{path}", path));
348 |
349 | req.send();
350 | }
351 |
352 | public function cp() : Void {
353 |
354 |
355 | }
356 |
357 | public function mv(srv : String, oldPath : String, newPath : String, onSuccess : Void -> Void, onError : UnifileError -> Void) : Void {
358 |
359 | var req : XMLHttpRequest = new XMLHttpRequest();
360 |
361 | req.onload = function(?_) {
362 |
363 | if (req.status != 200) {
364 |
365 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
366 |
367 | onError(err);
368 |
369 | } else {
370 |
371 | onSuccess();
372 | }
373 | }
374 |
375 | req.onerror = function(?_) {
376 |
377 | onError({ success: false, code: 0, message: "The request has failed." });
378 | }
379 |
380 | req.open("GET", config.unifileEndpoint + ENDPOINT_MV.replace("{srv}", srv).replace("{path}", oldPath + ":" + newPath));
381 |
382 | req.send();
383 | }
384 |
385 | public function upload(? blobs : StringMap, ? files : js.html.FileList, srv : String, path : String, onSuccess : Void -> Void, onError : UnifileError -> Void) : Void {
386 |
387 | // enforce path as a folder path
388 | if (path != "" && path.lastIndexOf('/') != path.length - 1) { // TODO check in unifile if it's not a bug
389 |
390 | path += '/';
391 | }
392 | var formData : DOMFormData = new DOMFormData();
393 |
394 | if (files != null) {
395 |
396 | for (f in files) {
397 |
398 | if (Reflect.isObject(f)) { // raw data from drop event or input[type=file] contains methods we need to filter
399 | trace("appended "+f.name);
400 | untyped __js__("formData.append('data', f, f.name);"); // @see https://github.com/HaxeFoundation/haxe/issues/2867
401 | }
402 | }
403 | }
404 | if (blobs != null) {
405 |
406 | if (Lambda.count(blobs) == 1) { // FIXME this is a temporary workaround for following issue on FF: https://bugzilla.mozilla.org/show_bug.cgi?id=690659
407 |
408 | path += blobs.keys().next();
409 | }
410 | for (fn in blobs.keys()) {
411 |
412 | untyped __js__("formData.append('data', blobs.get(fn), fn);"); // @see https://github.com/HaxeFoundation/haxe/issues/2867
413 | }
414 | }
415 |
416 | var xhttp : XMLHttpRequest = new XMLHttpRequest();
417 |
418 | xhttp.open("POST", config.unifileEndpoint + ENDPOINT_PUT.replace("{srv}", srv).replace("{path}", path));
419 |
420 | xhttp.onload = function(?_) {
421 |
422 | // FIXME check UploadResult (fix on unifile side difference between one file and several files upload results)
423 | //var resp : UploadResult = Json2UploadResult.parse(xhttp.responseText);
424 |
425 | if (xhttp.status == 200) {
426 |
427 | onSuccess();
428 |
429 | } else {
430 |
431 | var err : UnifileError = Json2UnifileError.parseUnifileError(xhttp.responseText);
432 |
433 | onError(err);
434 | }
435 | };
436 |
437 | xhttp.onerror = function(?_) {
438 |
439 | onError({ success: false, code: 0, message: "The request has failed." });
440 | }
441 |
442 | xhttp.send(formData);
443 | }
444 |
445 | /**
446 | * Requests a file from a Unifile endpoint.
447 | */
448 | public function get(url : String, onSuccess : String -> Void, onError : UnifileError -> Void) : Void {
449 |
450 | var req : XMLHttpRequest = new XMLHttpRequest();
451 |
452 | req.onload = function(?_) {
453 |
454 | if (req.status != 200) {
455 |
456 | var err : UnifileError = Json2UnifileError.parseUnifileError(req.responseText);
457 |
458 | onError(err);
459 |
460 | } else {
461 |
462 | onSuccess(req.responseText);
463 | }
464 | }
465 |
466 | req.onerror = function(?_) {
467 |
468 | onError({ success: false, code: 0, message: "The request has failed." });
469 | }
470 |
471 | req.open("GET", url);
472 |
473 | req.send();
474 | }
475 | }
--------------------------------------------------------------------------------
/src/ce/core/view/AlertPopup.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 |
16 | using StringTools;
17 | using ce.util.HtmlTools;
18 |
19 | class AlertPopup {
20 |
21 | static inline var CLASS_ERROR : String = "error";
22 | static inline var CLASS_WARNING : String = "warning";
23 |
24 | static inline var SELECTOR_TEXT : String = ".txt";
25 | static inline var SELECTOR_CHOICE_TMPL : String = ".choice";
26 |
27 | public function new(elt : Element) {
28 |
29 | this.elt = elt;
30 |
31 | this.txtElt = elt.querySelector(SELECTOR_TEXT);
32 |
33 | this.choiceTmpl = txtElt.querySelector(SELECTOR_CHOICE_TMPL);
34 | txtElt.removeChild(choiceTmpl);
35 |
36 | this.choicesElts = [];
37 | }
38 |
39 | var elt : Element;
40 | var txtElt : Element;
41 |
42 | var choiceTmpl : Element;
43 |
44 | var choicesElts : Array;
45 |
46 |
47 | ///
48 | // API
49 | //
50 |
51 | public function setMsg(msg : String, ? level : Int = 2, ? choices : Array<{ msg : String, cb : Void -> Void }>) : Void {
52 |
53 | while (choicesElts.length > 0) {
54 |
55 | txtElt.removeChild(choicesElts.pop());
56 | }
57 | txtElt.textContent = msg;
58 |
59 | if (choices != null) {
60 |
61 | for (c in choices) {
62 |
63 | var nc : Element = cast choiceTmpl.cloneNode(true);
64 | var tc : { msg : String, cb : Void -> Void } = c;
65 | nc.textContent = tc.msg;
66 | nc.addEventListener("click", function(?_){ tc.cb(); });
67 | txtElt.appendChild(nc);
68 | }
69 | }
70 | switch (level) {
71 |
72 | case 0:
73 | elt.toggleClass(CLASS_ERROR, true);
74 | elt.toggleClass(CLASS_WARNING, false);
75 |
76 | case 1:
77 | elt.toggleClass(CLASS_ERROR, false);
78 | elt.toggleClass(CLASS_WARNING, true);
79 |
80 | default:
81 | elt.toggleClass(CLASS_ERROR, false);
82 | elt.toggleClass(CLASS_WARNING, false);
83 | }
84 |
85 | haxe.Timer.delay(function(){ txtElt.style.marginTop = "-" + Std.string(txtElt.offsetHeight / 2 + 20) + "px"; }, 0);
86 | }
87 | }
--------------------------------------------------------------------------------
/src/ce/core/view/Application.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import ce.core.model.SortField;
15 | import ce.core.model.SortOrder;
16 | import ce.core.model.Service;
17 |
18 | import ce.core.model.oauth.OAuthResult;
19 | import ce.core.parser.oauth.Str2OAuthResult;
20 |
21 | import ce.core.config.Config;
22 |
23 | import js.Browser;
24 | import js.html.Element;
25 |
26 | using ce.util.HtmlTools;
27 | using StringTools;
28 |
29 | class Application {
30 |
31 | static var oauthCbListener : String -> Void;
32 |
33 | @:expose('CEoauthCb')
34 | static function oauthCb(pStr : String) : Void { // FIXME this prevents from multi-instancing
35 |
36 | if (oauthCbListener != null) {
37 |
38 | oauthCbListener(pStr);
39 | }
40 | }
41 |
42 | static inline var PLACE_HOLDER_LOGOUT_NAME : String = "{name}";
43 |
44 | static inline var ID_APPLICATION : String = "cloud-explorer";
45 |
46 | static inline var CLASS_LOADING : String = "loading";
47 | static inline var CLASS_STARTING : String = "starting";
48 | static inline var CLASS_BROWSING : String = "browsing";
49 | static inline var CLASS_AUTHORIZING : String = "authorizing";
50 | static inline var CLASS_LOGGED_IN : String = "loggedin";
51 | static inline var CLASS_ALERTING : String = "alerting";
52 | static inline var CLASS_MAKING_NEW_FOLDER : String = "making-new-folder";
53 | static inline var CLASS_SELECTING : String = "selecting";
54 |
55 | static inline var CLASS_EXPORT_OVERWRITING : String = "export-overwriting";
56 |
57 | static inline var CLASS_MODE_SINGLE_FILE_SELECTION : String = "single-file-sel-mode";
58 | static inline var CLASS_MODE_SINGLE_FILE_EXPORT : String = "single-file-exp-mode";
59 | static inline var CLASS_MODE_IS_LOGGED_IN : String = "is-logged-in-mode";
60 | static inline var CLASS_MODE_REQUEST_AUTHORIZE : String = "request-authorize-mode";
61 |
62 | static inline var CLASS_ITEMS_LIST : String = "items-list";
63 | static inline var CLASS_ITEMS_ICONS : String = "items-icons";
64 |
65 | static inline var CLASS_PREFIX_SORTEDBY : String = "sortedby-";
66 | static inline var CLASS_PREFIX_SERVICE : String = "srv-";
67 |
68 | static inline var SELECTOR_LOGOUT_BTN : String = ".logoutBtn";
69 | static inline var SELECTOR_CLOSE_BTN : String = ".closeBtn";
70 | static inline var SELECTOR_HOME : String = ".home";
71 | static inline var SELECTOR_FILE_BROWSER : String = ".fileBrowser";
72 | static inline var SELECTOR_ALERT_POPUP : String = ".alertPopup";
73 | static inline var SELECTOR_AUTH_POPUP : String = ".authPopup";
74 | static inline var SELECTOR_BREADCRUMB : String = ".breadcrumb";
75 | static inline var SELECTOR_DROPZONE : String = ".dropzone";
76 | static inline var SELECTOR_EXPORT : String = ".export";
77 | static inline var SELECTOR_NEW_FOLDER_BTN : String = ".newFolderBtn";
78 | static inline var SELECTOR_PARENT_FOLDER_BTN : String = ".parentFolderBtn";
79 | static inline var SELECTOR_DELETE_BTN : String = ".deleteBtn";
80 | static inline var SELECTOR_ITEMS_LIST_BTN : String = ".listItemsBtn";
81 | static inline var SELECTOR_ITEMS_ICON_BTN : String = ".iconItemsBtn";
82 |
83 | public function new(iframe : js.html.IFrameElement, config : Config) {
84 |
85 | this.iframe = iframe;
86 | this.config = config;
87 |
88 | initFrame();
89 |
90 | oauthCbListener = listenOAuthCb;
91 | }
92 |
93 | var config : Config;
94 |
95 | var iframe : js.html.IFrameElement;
96 |
97 | var rootElt : Element;
98 |
99 |
100 | ///
101 | // CALLBACKS
102 | //
103 |
104 | public dynamic function onClicked() : Void { }
105 |
106 | public dynamic function onSortBtnClicked(f : SortField) : Void { }
107 |
108 | public dynamic function onViewReady() : Void { }
109 |
110 | public dynamic function onLogoutClicked() : Void { }
111 |
112 | public dynamic function onCloseClicked() : Void { }
113 |
114 | public dynamic function onServiceLoginRequest(name : String) : Void { }
115 |
116 | public dynamic function onServiceLogoutRequest(name : String) : Void { }
117 |
118 | public dynamic function onServiceClicked(name : String) : Void { }
119 |
120 | public dynamic function onFileClicked(id : String) : Void { }
121 |
122 | public dynamic function onFileSelectClicked(id : String) : Void { }
123 |
124 | public dynamic function onFileDeleteClicked(id : String) : Void { }
125 |
126 | public dynamic function onFileRenameRequested(id : String, value : String) : Void { }
127 |
128 | public dynamic function onFileCheckedStatusChanged(id : String) : Void { }
129 |
130 | public dynamic function onNavBtnClicked(srv : String, path : String) : Void { }
131 |
132 | public dynamic function onAuthorizationWindowBlocked() : Void { }
133 |
134 | public dynamic function onServiceAuthorizationDone(? r : Null) : Void { }
135 |
136 | public dynamic function onSaveExportClicked() : Void { }
137 |
138 | public dynamic function onOverwriteExportClicked() : Void { }
139 |
140 | public dynamic function onExportNameChanged() : Void { }
141 |
142 | public dynamic function onInputFilesChanged() : Void { }
143 |
144 | public dynamic function onFilesDropped(files : js.html.FileList) : Void { }
145 |
146 | public dynamic function onNewFolderClicked() : Void { }
147 |
148 | public dynamic function onParentFolderClicked() : Void { }
149 |
150 | public dynamic function onItemsListClicked() : Void { }
151 |
152 | public dynamic function onItemsIconClicked() : Void { }
153 |
154 | public dynamic function onDeleteClicked() : Void { }
155 |
156 | public dynamic function onNewFolderName() : Void { }
157 |
158 |
159 | ///
160 | // API
161 | //
162 |
163 | public var home (default, null) : Home;
164 |
165 | public var fileBrowser (default, null) : FileBrowser;
166 |
167 | public var authPopup (default, null) : AuthPopup;
168 |
169 | public var alertPopup (default, null) : AlertPopup;
170 |
171 | public var breadcrumb (default, null) : Breadcrumb;
172 |
173 | public var dropzone (default, null) : DropZone;
174 |
175 | public var export (default, null) : Export;
176 |
177 | public var location(get, null) : Null;
178 |
179 | public var closeBtn (default, null) : Button;
180 |
181 | public var newFolderBtn (default, null) : Button;
182 |
183 | public var parentFolderBtn (default, null) : Button;
184 |
185 | public var itemsListBtn (default, null) : Button;
186 |
187 | public var itemsIconBtn (default, null) : Button;
188 |
189 | public var deleteBtn (default, null) : Button;
190 |
191 | public var logoutBtn (default, null) : Button;
192 |
193 | public function setCurrentService(s : Service) : Void {
194 |
195 | rootElt.toggleClass(CLASS_PREFIX_SERVICE + Service.Dropbox, false);
196 | rootElt.toggleClass(CLASS_PREFIX_SERVICE + Service.Ftp, false);
197 | rootElt.toggleClass(CLASS_PREFIX_SERVICE + Service.Www, false);
198 |
199 | rootElt.toggleClass(CLASS_PREFIX_SERVICE + s, true);
200 | }
201 |
202 | public function setSortField(v : String) : Void {
203 |
204 | rootElt.toggleClass(CLASS_PREFIX_SORTEDBY + SortField.Name, false);
205 | rootElt.toggleClass(CLASS_PREFIX_SORTEDBY + SortField.Type, false);
206 | rootElt.toggleClass(CLASS_PREFIX_SORTEDBY + SortField.LastUpdate, false);
207 |
208 | rootElt.toggleClass(CLASS_PREFIX_SORTEDBY + v, true);
209 | }
210 |
211 | public function setSortOrder(v : String) : Void {
212 |
213 | rootElt.toggleClass(SortOrder.Asc, false);
214 | rootElt.toggleClass(SortOrder.Desc, false);
215 |
216 | rootElt.toggleClass(v, true);
217 | }
218 |
219 | public function setListDisplayMode() : Void {
220 |
221 | this.rootElt.toggleClass(CLASS_ITEMS_LIST, true);
222 | this.rootElt.toggleClass(CLASS_ITEMS_ICONS, false);
223 | }
224 |
225 | public function setIconDisplayMode() : Void {
226 |
227 | this.rootElt.toggleClass(CLASS_ITEMS_ICONS, true);
228 | this.rootElt.toggleClass(CLASS_ITEMS_LIST, false);
229 | }
230 |
231 | public function setDisplayed(v : Bool) : Void {
232 |
233 | iframe.style.display = v ? "block" : "none";
234 | }
235 |
236 | public function setLoaderDisplayed(v : Bool) : Void {
237 |
238 | rootElt.toggleClass(CLASS_LOADING , v);
239 | }
240 |
241 | public function setLogoutButtonDisplayed(v : Bool) : Void {
242 |
243 | rootElt.toggleClass(CLASS_LOGGED_IN , v);
244 | }
245 |
246 | public function setHomeDisplayed(v : Bool) : Void {
247 |
248 | if (v) {
249 |
250 | cleanPreviousState();
251 | }
252 |
253 | rootElt.toggleClass(CLASS_STARTING , v);
254 | }
255 |
256 | public function setFileBrowserDisplayed(v : Bool) : Void {
257 |
258 | if (v) {
259 |
260 | cleanPreviousState();
261 | }
262 |
263 | rootElt.toggleClass(CLASS_BROWSING , v);
264 | }
265 |
266 | public function setExportOverwriteDisplayed(v : Bool) : Void {
267 |
268 | rootElt.toggleClass(CLASS_EXPORT_OVERWRITING , v);
269 | }
270 |
271 | public function setAuthPopupDisplayed(v : Bool) : Void {
272 |
273 | rootElt.toggleClass(CLASS_AUTHORIZING , v);
274 | }
275 |
276 | public function setAlertPopupDisplayed(v : Bool) : Void {
277 |
278 | rootElt.toggleClass(CLASS_ALERTING , v);
279 | }
280 |
281 | public function setNewFolderDisplayed(v : Bool) : Void {
282 |
283 | if (!v) {
284 |
285 | fileBrowser.newFolderName = "";
286 | }
287 | rootElt.toggleClass(CLASS_MAKING_NEW_FOLDER , v);
288 |
289 | if (v) {
290 |
291 | fileBrowser.focusOnNewFolder();
292 | }
293 | }
294 |
295 | public function setSelecting(v : Bool) : Void {
296 |
297 | rootElt.toggleClass(CLASS_SELECTING , v);
298 | }
299 |
300 | public function openAuthorizationWindow(url : String) : Void {
301 |
302 | // note: we might need to improve this method in order to have different possible sizes by cloud service
303 | var authPopup = Browser.window.open(url, "authPopup", "height=829,width=1035");
304 |
305 | if (authPopup == null || authPopup.closed || authPopup.closed == null) {
306 |
307 | onAuthorizationWindowBlocked();
308 |
309 | } else {
310 |
311 | if (authPopup.focus != null) { authPopup.focus(); }
312 |
313 | var timer = new haxe.Timer(500);
314 |
315 | timer.run = function() {
316 | //trace("authPopup= "+authPopup+" authPopup.closed= "+authPopup.closed);
317 | if (authPopup.closed) {
318 |
319 | timer.stop();
320 |
321 | onServiceAuthorizationDone();
322 | }
323 | }
324 | }
325 | }
326 |
327 | public function setModeState(v : ce.core.model.Mode) : Void {
328 |
329 | var cms : Null = currentModeState();
330 | //trace("current UI mode is: "+cms);
331 | if (cms != null) {
332 |
333 | rootElt.toggleClass(cms , false);
334 | }
335 | if (v != null) {
336 |
337 | switch (v) {
338 |
339 | case SingleFileSelection(_):
340 |
341 | rootElt.toggleClass(CLASS_MODE_SINGLE_FILE_SELECTION , true);
342 |
343 | case SingleFileExport(_):
344 |
345 | rootElt.toggleClass(CLASS_MODE_SINGLE_FILE_EXPORT , true);
346 |
347 | case IsLoggedIn(_):
348 |
349 | rootElt.toggleClass(CLASS_MODE_IS_LOGGED_IN , true);
350 |
351 | case RequestAuthorize(_):
352 |
353 | rootElt.toggleClass(CLASS_MODE_REQUEST_AUTHORIZE , true);
354 | }
355 | }
356 | }
357 |
358 | ///
359 | // GETTER / SETTER
360 | //
361 |
362 | public function get_location() : Null {
363 |
364 | if (iframe == null) return null;
365 |
366 | return iframe.contentDocument.location.origin;
367 | }
368 |
369 |
370 | ///
371 | // INTERNALS
372 | //
373 |
374 | function listenOAuthCb(pStr : String) : Void {
375 |
376 | var o : OAuthResult = Str2OAuthResult.parse(pStr);
377 |
378 | onServiceAuthorizationDone(o);
379 | }
380 |
381 | function currentModeState() : Null {
382 |
383 | for (c in rootElt.className.split(" ")) {
384 |
385 | if( Lambda.has([CLASS_MODE_SINGLE_FILE_SELECTION, CLASS_MODE_SINGLE_FILE_EXPORT], c) ) {
386 |
387 | return c;
388 | }
389 | }
390 | return null;
391 | }
392 |
393 | function currentState() : Null {
394 |
395 | for (c in rootElt.className.split(" ")) {
396 |
397 | if( Lambda.has([CLASS_STARTING, CLASS_BROWSING], c) ) {
398 |
399 | return c;
400 | }
401 | }
402 | // if we're here, we have a problem (no current state ?!)
403 | return null;
404 | }
405 |
406 | private function cleanPreviousState() : Void {
407 |
408 | var cs : Null = currentState(); //trace("current state = "+cs);
409 |
410 | rootElt.toggleClass(CLASS_AUTHORIZING, false);
411 |
412 | if (cs != null) {
413 |
414 | rootElt.toggleClass(cs, false);
415 | }
416 | }
417 |
418 | private function initFrame() : Void {
419 |
420 | // init iframe
421 | iframe.style.display = "none";
422 | iframe.style.position = "absolute";
423 | iframe.style.top = iframe.style.left = "0";
424 | iframe.style.width = iframe.style.height = "100%";
425 |
426 | iframe.onload = function(?_){ initElts(); }
427 |
428 | iframe.src = config.path + "cloud-explorer.html";
429 |
430 | //Application.oauthCb = function(p : String) { trace("oauthCb p="+p); onServiceAuthorizationDone(p); } // FIXME that's not ideal and prevent from multi instancing
431 | }
432 |
433 | private function initElts() : Void {
434 |
435 | // select elements
436 | rootElt = iframe.contentDocument.getElementById(ID_APPLICATION);
437 |
438 | logoutBtn = new Button(rootElt.querySelector(SELECTOR_LOGOUT_BTN));
439 | logoutBtn.onClicked = onLogoutClicked;
440 |
441 | closeBtn = new Button(rootElt.querySelector(SELECTOR_CLOSE_BTN));
442 | closeBtn.onClicked = onCloseClicked;
443 |
444 | breadcrumb = new Breadcrumb(rootElt.querySelector(SELECTOR_BREADCRUMB));
445 | breadcrumb.onNavBtnClicked = function(srv : String, path : String) { onNavBtnClicked(srv, path); }
446 |
447 | export = new Export(rootElt.querySelector(SELECTOR_EXPORT));
448 | export.onSaveBtnClicked = function() { onSaveExportClicked(); }
449 | export.onOverwriteBtnClicked = function() { onOverwriteExportClicked(); }
450 | export.onExportNameChanged = function() { onExportNameChanged(); }
451 |
452 | home = new Home(rootElt.querySelector(SELECTOR_HOME));
453 | home.onServiceClicked = function(name : String) { onServiceClicked(name); }
454 |
455 | fileBrowser = new FileBrowser(rootElt.querySelector(SELECTOR_FILE_BROWSER));
456 | fileBrowser.onServiceLogoutRequest = function(name : String) { onServiceLogoutRequest(name); }
457 | fileBrowser.onServiceLoginRequest = function(name : String) { onServiceLoginRequest(name); }
458 | fileBrowser.onServiceClicked = function(name : String) { onServiceClicked(name); }
459 | fileBrowser.onFileClicked = function(id : String) { onFileClicked(id); }
460 | fileBrowser.onFileSelectClicked = function(id : String) { onFileSelectClicked(id); }
461 | fileBrowser.onFileDeleteClicked = function(id : String) { onFileDeleteClicked(id); }
462 | fileBrowser.onFileCheckedStatusChanged = function(id : String) { onFileCheckedStatusChanged(id); }
463 | fileBrowser.onFileRenameRequested = function(id : String, value : String) { onFileRenameRequested(id, value); }
464 | fileBrowser.onNewFolderName = function() { onNewFolderName(); }
465 | fileBrowser.onSortBtnClicked = function(f:SortField) { onSortBtnClicked(f); }
466 |
467 | dropzone = new DropZone(rootElt.querySelector(SELECTOR_DROPZONE));
468 | dropzone.onInputFilesChanged = function() { onInputFilesChanged(); }
469 | dropzone.onFilesDropped = function(files : js.html.FileList) { onFilesDropped(files); }
470 |
471 | authPopup = new AuthPopup(rootElt.querySelector(SELECTOR_AUTH_POPUP));
472 |
473 | alertPopup = new AlertPopup(rootElt.querySelector(SELECTOR_ALERT_POPUP));
474 |
475 | newFolderBtn = new Button(rootElt.querySelector(SELECTOR_NEW_FOLDER_BTN));
476 | newFolderBtn.onClicked = onNewFolderClicked;
477 |
478 | parentFolderBtn = new Button(rootElt.querySelector(SELECTOR_PARENT_FOLDER_BTN));
479 | parentFolderBtn.onClicked = onParentFolderClicked;
480 |
481 | itemsListBtn = new Button(rootElt.querySelector(SELECTOR_ITEMS_LIST_BTN));
482 | itemsListBtn.onClicked = onItemsListClicked;
483 |
484 | itemsIconBtn = new Button(rootElt.querySelector(SELECTOR_ITEMS_ICON_BTN));
485 | itemsIconBtn.onClicked = onItemsIconClicked;
486 |
487 | deleteBtn = new Button(rootElt.querySelector(SELECTOR_DELETE_BTN));
488 | deleteBtn.onClicked = onDeleteClicked;
489 |
490 | rootElt.addEventListener("click", function(?_){ onClicked(); });
491 |
492 | onViewReady();
493 | }
494 | }
--------------------------------------------------------------------------------
/src/ce/core/view/AuthPopup.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 |
16 | using StringTools;
17 |
18 | class AuthPopup {
19 |
20 | static inline var SELECTOR_LINK : String = "a";
21 | static inline var SELECTOR_TEXT : String = "span";
22 |
23 | static inline var PLACE_HOLDER_SRV_NAME : String = "{srvName}";
24 |
25 | public function new(elt : Element) {
26 |
27 | this.elt = elt;
28 |
29 | this.linkElt = elt.querySelector(SELECTOR_LINK);
30 | linkElt.addEventListener("click", function(?_){ onClicked(); });
31 |
32 | this.textElt = elt.querySelector(SELECTOR_TEXT);
33 | this.txtTmpl = textElt.textContent;
34 | }
35 |
36 | var elt : Element;
37 |
38 | var linkElt : Element;
39 | var textElt : Element;
40 |
41 | var txtTmpl : String;
42 |
43 | ///
44 | // CALLBACKS
45 | //
46 |
47 | public dynamic function onClicked() : Void { }
48 |
49 |
50 | ///
51 | // API
52 | //
53 |
54 | public function setServerName(srvName : String) {
55 |
56 | textElt.textContent = txtTmpl.replace(PLACE_HOLDER_SRV_NAME, srvName);
57 | }
58 | }
--------------------------------------------------------------------------------
/src/ce/core/view/Breadcrumb.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 |
16 | using StringTools;
17 |
18 | class Breadcrumb {
19 |
20 | static inline var SELECTOR_PATH_ITEM_TMPL : String = "span.pathIt";
21 | static inline var SELECTOR_PATH_SEP_TMPL : String = "span.sep";
22 |
23 | public function new(elt : Element) {
24 |
25 | this.elt = elt;
26 |
27 | this.pathItemTmpl = elt.querySelector(SELECTOR_PATH_ITEM_TMPL);
28 | elt.removeChild(pathItemTmpl);
29 | this.pathSepTmpl = elt.querySelector(SELECTOR_PATH_SEP_TMPL);
30 | elt.removeChild(pathSepTmpl);
31 | }
32 |
33 | var elt : Element;
34 |
35 | var pathItemTmpl : Element;
36 | var pathSepTmpl : Element;
37 |
38 |
39 | ///
40 | // CALLBACKS
41 | //
42 |
43 | public dynamic function onNavBtnClicked(srv : String, path : String) : Void { }
44 |
45 |
46 | ///
47 | // API
48 | //
49 |
50 | public function setBreadcrumbPath(srv : String, path : String) : Void {
51 |
52 | while (elt.childNodes.length > 0) {
53 |
54 | elt.removeChild(elt.firstChild);
55 | }
56 | var srvIt : Element = cast pathItemTmpl.cloneNode(true);
57 | srvIt.addEventListener("click", function(?_){ onNavBtnClicked(srv, "/"); });
58 | srvIt.textContent = srv;
59 |
60 | elt.appendChild(srvIt);
61 |
62 | var pathItems : Array = [];
63 |
64 | if (path.length > 0) {
65 |
66 | var parr : Array = path.split("/");
67 |
68 | while (parr.length > 0) {
69 |
70 | var itPath : String = "/" + parr.join("/") + "/";
71 | var pit : String = parr.pop();
72 |
73 | if (pit.trim() != "") {
74 |
75 | var nit : Element = cast pathItemTmpl.cloneNode(true);
76 | nit.addEventListener("click", function(?_){ onNavBtnClicked(srv, itPath); });
77 | nit.textContent = pit;
78 |
79 | pathItems.push(nit);
80 | }
81 | }
82 | }
83 | while (pathItems.length > 0) {
84 |
85 | elt.appendChild(pathSepTmpl.cloneNode(true));
86 | elt.appendChild(pathItems.pop());
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/ce/core/view/Button.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 |
16 | using ce.util.HtmlTools;
17 |
18 | class Button {
19 |
20 | static inline var ATTR_DISABLED : String = "disabled";
21 | static inline var ATTR_VALUE_DISABLED : String = "disabled";
22 |
23 | public function new(elt : Element) {
24 |
25 | this.elt = elt;
26 |
27 | this.elt.addEventListener( "click", function(?_){ onClicked(); } );
28 | }
29 |
30 | var elt : Element;
31 |
32 |
33 | ///
34 | // API
35 | //
36 |
37 | public dynamic function onClicked() : Void { }
38 |
39 | public var enabled (get, set) : Bool;
40 |
41 |
42 | ///
43 | // GETTER / SETTER
44 | //
45 |
46 | private function get_enabled() : Bool {
47 |
48 | return !elt.hasAttribute(ATTR_DISABLED);
49 | }
50 |
51 | private function set_enabled(v : Bool) : Bool {
52 |
53 | if (v && elt.hasAttribute(ATTR_DISABLED)) {
54 |
55 | elt.removeAttribute(ATTR_DISABLED);
56 | }
57 | if (!v && !elt.hasAttribute(ATTR_DISABLED)) {
58 |
59 | elt.setAttribute(ATTR_DISABLED, ATTR_VALUE_DISABLED);
60 | }
61 | return v;
62 | }
63 | }
--------------------------------------------------------------------------------
/src/ce/core/view/DropZone.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 | import js.html.InputElement;
16 | import js.html.FileList;
17 |
18 | using ce.util.HtmlTools;
19 |
20 | class DropZone {
21 |
22 | static inline var SELECTOR_INPUT : String = "div input";
23 | static inline var SELECTOR_BUTTON : String = "div button";
24 |
25 | static inline var CLASS_DRAGGINGOVER : String = "draggingover";
26 |
27 | public function new(elt : Element) {
28 |
29 | this.elt = elt;
30 |
31 | this.inputElt = cast elt.querySelector(SELECTOR_INPUT);
32 | inputElt.addEventListener("change", function(?_){ onInputFilesChanged(); });
33 |
34 | this.btnElt = elt.querySelector(SELECTOR_BUTTON);
35 | btnElt.addEventListener("click", function(?_){ onBtnClicked(); });
36 |
37 | this.elt.addEventListener('dragover', function(e) {
38 |
39 | e.preventDefault();
40 |
41 | e.dataTransfer.dropEffect = 'copy';
42 |
43 | return false;
44 | });
45 |
46 | this.elt.addEventListener('dragenter', function(e) {
47 |
48 | this.elt.toggleClass(CLASS_DRAGGINGOVER, true);
49 | });
50 |
51 | this.elt.addEventListener('dragleave', function(e) {
52 |
53 | this.elt.toggleClass(CLASS_DRAGGINGOVER, false);
54 | });
55 |
56 | this.elt.addEventListener('drop', function(e) {
57 |
58 | e.preventDefault();
59 | e.stopPropagation();
60 |
61 | this.elt.toggleClass(CLASS_DRAGGINGOVER, false); // useful ?
62 |
63 | var fileList : FileList = e.dataTransfer.files;
64 |
65 | if (fileList.length > 0) {
66 |
67 | onFilesDropped(fileList);
68 | }
69 | });
70 | }
71 |
72 | var elt : Element;
73 | var btnElt : Element;
74 |
75 | public var inputElt (default, null) : InputElement;
76 |
77 |
78 | ///
79 | // CALLBACKS
80 | //
81 |
82 | public dynamic function onInputFilesChanged() : Void { }
83 |
84 | public dynamic function onFilesDropped(files : FileList) : Void { }
85 |
86 |
87 | ///
88 | // INTERNALS
89 | //
90 |
91 | private function onBtnClicked() : Void {
92 |
93 | inputElt.click();
94 | }
95 | }
--------------------------------------------------------------------------------
/src/ce/core/view/Export.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 | import js.html.InputElement;
16 |
17 | using StringTools;
18 |
19 | class Export {
20 |
21 | static inline var SELECTOR_INPUT : String = "input";
22 | static inline var SELECTOR_PATH : String = "span.path";
23 | static inline var SELECTOR_EXT : String = "span.ext";
24 | static inline var SELECTOR_SAVE_BUTTON : String = ".saveBtn";
25 | static inline var SELECTOR_OVERWRITE_BUTTON : String = ".overwriteBtn";
26 |
27 | public function new(elt : Element) {
28 |
29 | this.elt = elt;
30 |
31 | this.inputElt = cast elt.querySelector(SELECTOR_INPUT);
32 | inputElt.addEventListener("input", function(?_) { onExportNameChanged(); });
33 |
34 | this.pathElt = elt.querySelector(SELECTOR_PATH);
35 |
36 | this.extElt = elt.querySelector(SELECTOR_EXT);
37 |
38 | this.saveBtnElt = elt.querySelector(SELECTOR_SAVE_BUTTON);
39 | saveBtnElt.addEventListener("click", function(?_){ onSaveBtnClicked(); });
40 |
41 | this.overwriteBtnElt = elt.querySelector(SELECTOR_OVERWRITE_BUTTON);
42 | overwriteBtnElt.addEventListener("click", function(?_){ onOverwriteBtnClicked(); });
43 | }
44 |
45 | var elt : Element;
46 |
47 | var inputElt : InputElement;
48 | var extElt : Element;
49 | var pathElt : Element;
50 | var saveBtnElt : Element;
51 | var overwriteBtnElt : Element;
52 |
53 | public var exportName (get, set) : Null;
54 |
55 | public var ext (null, set) : Null;
56 |
57 | public var path (null, set) : Null;
58 |
59 |
60 | ///
61 | // CALLBACKS
62 | //
63 |
64 | public dynamic function onNavBtnClicked(srv : String, path : String) : Void { }
65 |
66 | public dynamic function onSaveBtnClicked() : Void { }
67 |
68 | public dynamic function onOverwriteBtnClicked() : Void { }
69 |
70 | public dynamic function onExportNameChanged() : Void { }
71 |
72 |
73 | ///
74 | // GETTERS / SETTERS
75 | //
76 |
77 | public function get_exportName() : Null {
78 |
79 | return inputElt.value;
80 | }
81 |
82 | public function set_exportName(v : Null) : Null {
83 |
84 | inputElt.value = v;
85 |
86 | return v;
87 | }
88 |
89 | public function set_ext(v : Null) : Null {
90 |
91 | extElt.textContent = v;
92 |
93 | return v;
94 | }
95 |
96 | public function set_path(v : Null) : Null {
97 |
98 | pathElt.textContent = v;
99 |
100 | return v;
101 | }
102 | }
--------------------------------------------------------------------------------
/src/ce/core/view/FileBrowser.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 | import js.html.InputElement;
16 | import js.html.KeyboardEvent;
17 | import js.html.NodeList;
18 |
19 | import haxe.ds.StringMap;
20 |
21 | import ce.core.model.SortField;
22 | import ce.core.model.SortOrder;
23 |
24 | using ce.util.HtmlTools;
25 | using ce.util.FileTools;
26 | using StringTools;
27 |
28 | class FileBrowser {
29 |
30 | static inline var SELECTOR_SRV_LIST : String = ".services ul";
31 | static inline var SELECTOR_FILES_LIST : String = ".files ul";
32 |
33 | static inline var SELECTOR_SRV_ITEM_TMPL : String = "li";
34 | static inline var SELECTOR_NEW_FOLDER_ITEM : String = ".folder.new";
35 | static inline var SELECTOR_FOLDER_ITEM_TMPL : String = ".folder:nth-last-child(-n+1)";
36 | static inline var SELECTOR_FILE_ITEM_TMPL : String = ".file";
37 | static inline var SELECTOR_CONTEXT_MENU_ITEMS : String = "ul.contextMenu li";
38 |
39 | static inline var SELECTOR_NAME_BTN : String = ".titles .fileName";
40 | static inline var SELECTOR_TYPE_BTN : String = ".titles .fileType";
41 | static inline var SELECTOR_DATE_BTN : String = ".titles .lastUpdate";
42 |
43 | static inline var CLASS_SELECT_FOLDER : String = "selectFolders";
44 | static inline var CLASS_SRV_CONNECTED : String = "connected";
45 |
46 | public function new(elt : Element) {
47 |
48 | this.elt = elt;
49 |
50 | this.srvItemElts = new StringMap();
51 |
52 | this.srvListElt = elt.querySelector(SELECTOR_SRV_LIST);
53 | this.srvItemTmpl = srvListElt.querySelector(SELECTOR_SRV_ITEM_TMPL);
54 | srvListElt.removeChild(srvItemTmpl);
55 |
56 | this.fileListElt = elt.querySelector(SELECTOR_FILES_LIST);
57 |
58 | this.fileItemTmpl = fileListElt.querySelector(SELECTOR_FILE_ITEM_TMPL);
59 | fileListElt.removeChild(fileItemTmpl);
60 |
61 | this.newFolderItem = fileListElt.querySelector(SELECTOR_NEW_FOLDER_ITEM);
62 | this.newFolderInput = cast newFolderItem.querySelector("input");
63 | newFolderInput.addEventListener("keydown", function(e : KeyboardEvent){
64 |
65 | untyped {
66 | if (e.keyIdentifier != null && e.keyIdentifier.toLowerCase() == "enter" ||
67 | e.key != null && e.key.toLowerCase() == "enter") {
68 |
69 | onNewFolderName();
70 | }
71 | }
72 | });
73 | newFolderInput.addEventListener("focusout", function(?_){ onNewFolderName(); });
74 |
75 | this.folderItemTmpl = fileListElt.querySelector(SELECTOR_FOLDER_ITEM_TMPL);
76 | fileListElt.removeChild(folderItemTmpl);
77 |
78 | var nameBtn = elt.querySelector(SELECTOR_NAME_BTN);
79 | nameBtn.addEventListener("click", function(?_){ onSortBtnClicked(Name); });
80 | var typeBtn = elt.querySelector(SELECTOR_TYPE_BTN);
81 | typeBtn.addEventListener("click", function(?_){ onSortBtnClicked(Type); });
82 | var dateBtn = elt.querySelector(SELECTOR_DATE_BTN);
83 | dateBtn.addEventListener("click", function(?_){ onSortBtnClicked(LastUpdate); });
84 |
85 | this.fileListItems = [];
86 |
87 | this.filters = null;
88 | }
89 |
90 | var elt : Element;
91 |
92 | // lists
93 | var srvListElt : Element;
94 | var fileListElt : Element;
95 |
96 | // templates
97 | var srvItemTmpl : Element;
98 | var fileItemTmpl : Element;
99 | var folderItemTmpl : Element;
100 |
101 | // items
102 | var newFolderItem : Element;
103 | var newFolderInput : InputElement;
104 |
105 | var srvItemElts : StringMap;
106 |
107 | public var filters (default, set) : Null>;
108 |
109 | public var fileListItems (default, null) : Array;
110 |
111 | public var newFolderName (get, set) : Null;
112 |
113 |
114 | ///
115 | // CALLBACKS
116 | //
117 |
118 | public dynamic function onServiceLoginRequest(name : String) : Void { }
119 |
120 | public dynamic function onServiceLogoutRequest(name : String) : Void { }
121 |
122 | public dynamic function onServiceClicked(name : String) : Void { }
123 |
124 | public dynamic function onFileSelected(id : String) : Void { }
125 |
126 | public dynamic function onFileClicked(id : String) : Void { }
127 |
128 | public dynamic function onFileSelectClicked(id : String) : Void { }
129 |
130 | public dynamic function onFileDeleteClicked(id : String) : Void { }
131 |
132 | public dynamic function onFileCheckedStatusChanged(id : String) : Void { }
133 |
134 | public dynamic function onFileRenameRequested(id : String, value : String) : Void { }
135 |
136 | public dynamic function onNewFolderName() : Void { }
137 |
138 | public dynamic function onSortBtnClicked(field : SortField) : Void { }
139 |
140 |
141 | ///
142 | // API
143 | //
144 | /*
145 | public function setEmptyMsgDisplay(v : Bool) : Void {
146 |
147 |
148 | }
149 | */
150 |
151 | public function resetList() : Void {
152 |
153 | while(srvListElt.childNodes.length > 0) {
154 |
155 | srvListElt.removeChild(srvListElt.childNodes.item(0));
156 | }
157 | }
158 |
159 | public function removeService(name : String) : Void {
160 |
161 | srvListElt.removeChild(srvItemElts.get(name));
162 | }
163 |
164 | public function addService(name : String, displayName : String, ? connected : Bool) : Void {
165 |
166 | var newItem : Element = cast srvItemTmpl.cloneNode(true);
167 | newItem.className = name;
168 | //newItem.setAttribute("title", newItem.getAttribute("title").replace("{srvName}", displayName));
169 |
170 | newItem.addEventListener("click", function(?_){ onServiceClicked(name); });
171 |
172 | var lis : NodeList = newItem.querySelectorAll(SELECTOR_CONTEXT_MENU_ITEMS);
173 |
174 | for (i in 0...lis.length) {
175 |
176 | var li : Element = cast lis[i];
177 |
178 | li.textContent = li.textContent.replace("{srvName}", displayName);
179 | li.addEventListener("click", function(e:js.html.MouseEvent){
180 |
181 | e.stopPropagation();
182 |
183 | if (li.classList.contains("login")) {
184 |
185 | onServiceLoginRequest(name);
186 |
187 | } else if (li.classList.contains("logout")) {
188 |
189 | onServiceLogoutRequest(name);
190 | }
191 |
192 | });
193 | }
194 |
195 | srvListElt.appendChild(newItem);
196 |
197 | srvItemElts.set(name, newItem);
198 |
199 | if (connected) setSrvConnected(name , connected);
200 | }
201 |
202 | public function setSrvConnected(name : String, connected : Bool) : Void {
203 |
204 | srvItemElts.get(name).toggleClass(CLASS_SRV_CONNECTED , connected);
205 | }
206 |
207 | public function resetFileList() : Void {
208 |
209 | while(fileListItems.length > 0) {
210 |
211 | fileListElt.removeChild(fileListItems.pop().elt);
212 | }
213 | }
214 |
215 | public function addFolder(id : String, name : String, ? lastUpdate : Null, ? selectable : Bool = true) : Void {
216 |
217 | var newItem : Element = cast folderItemTmpl.cloneNode(true);
218 |
219 | var fli : FileListItem = new FileListItem(newItem);
220 | fli.name = name;
221 | fli.lastUpdate = lastUpdate;
222 | fli.onClicked = function() { onFileClicked(id); }
223 | fli.onSelectClicked = function() { onFileSelectClicked(id); }
224 | fli.onDeleteClicked = function() { onFileDeleteClicked(id); }
225 | fli.onRenameRequested = function() { onFileRenameRequested(id, fli.renameValue); }
226 | fli.onCheckedStatusChanged = function() { onFileCheckedStatusChanged(id); }
227 | fli.selectable = selectable;
228 |
229 | fileListItems.push(fli);
230 |
231 | fileListElt.insertBefore(newItem, newFolderItem);
232 | }
233 |
234 | public function addFile(id : String, name : String, type : Null, lastUpdate : Date) : Void {
235 |
236 | var newItem : Element = cast fileItemTmpl.cloneNode(true);
237 |
238 | var fli : FileListItem = new FileListItem(newItem);
239 | fli.name = name;
240 | if (type != null) {
241 |
242 | fli.type = type;
243 | }
244 | fli.lastUpdate = lastUpdate;
245 | fli.onClicked = function() { onFileClicked(id); }
246 | fli.onDeleteClicked = function() { onFileDeleteClicked(id); }
247 | fli.onRenameRequested = function() { onFileRenameRequested(id, fli.renameValue); }
248 | fli.onCheckedStatusChanged = function() { onFileCheckedStatusChanged(id); }
249 |
250 | fileListItems.push(fli);
251 |
252 | applyFilters(fli);
253 |
254 | fileListElt.insertBefore(newItem, newFolderItem);
255 | }
256 | /*
257 | return function(date, uppercase) {
258 | return new Date(date).toLocaleDateString()
259 | }
260 | */
261 |
262 | public function focusOnNewFolder() : Void {
263 |
264 | newFolderInput.focus();
265 | }
266 |
267 | public function sort(byField : SortField, order : SortOrder) : Void {
268 |
269 | fileListItems.sort(function(a:FileListItem,b:FileListItem){
270 |
271 | switch (order) {
272 |
273 | case Asc:
274 |
275 | return Reflect.getProperty(a, byField) > Reflect.getProperty(b, byField) ? 1 : -1;
276 |
277 | case Desc:
278 |
279 | return Reflect.getProperty(a, byField) < Reflect.getProperty(b, byField) ? 1 : -1;
280 | }
281 | });
282 |
283 | for (fit in fileListItems) {
284 |
285 | fileListElt.insertBefore(fit.elt, newFolderItem);
286 | }
287 | }
288 |
289 |
290 | ///
291 | // GETTERS / SETTERS
292 | //
293 |
294 | public function get_newFolderName() : Null {
295 |
296 | return newFolderInput.value;
297 | }
298 |
299 | public function set_newFolderName(v : Null) : Null {
300 |
301 | newFolderInput.value = v;
302 |
303 | return v;
304 | }
305 |
306 |
307 | public function set_filters(v : Null>) : Null> {
308 |
309 | if (filters == v) {
310 |
311 | return v;
312 | }
313 | filters = v;
314 |
315 | if (filters != null && filters.indexOf(ce.util.FileTools.DIRECTORY_MIME_TYPE) > -1) {
316 |
317 | elt.toggleClass(CLASS_SELECT_FOLDER, true);
318 |
319 | } else {
320 |
321 | elt.toggleClass(CLASS_SELECT_FOLDER, false);
322 | }
323 | for (f in fileListItems) {
324 |
325 | applyFilters(f);
326 | }
327 | return filters;
328 | }
329 |
330 | ///
331 | // INTERNALS
332 | //
333 |
334 | private function applyFilters(f : FileListItem) : Void {
335 |
336 | if (f.type != FileTools.DIRECTORY_MIME_TYPE) {
337 |
338 | if (filters == null || filters.indexOf(f.type) != -1) {
339 |
340 | f.filteredOut = false;
341 |
342 | } else {
343 |
344 | f.filteredOut = true;
345 | }
346 | }
347 | }
348 | }
349 |
350 | class FileListItem {
351 |
352 | static inline var CLASS_RENAMING : String = "renaming";
353 | static inline var CLASS_NOT_SELECTABLE : String = "nosel";
354 | static inline var CLASS_FILTERED_OUT : String = "filteredOut";
355 | static inline var CLASS_FOLDER : String = "folder"; // not ideal we have this in 3 constants FIXME
356 | static inline var CLASS_IMAGE : String = "image";
357 | static inline var CLASS_SOUND : String = "sound";
358 | static inline var CLASS_VIDEO : String = "video";
359 |
360 | public function new(elt : Element) {
361 |
362 | this.elt = elt;
363 |
364 | this.checkBoxElt = cast elt.querySelector("input[type='checkbox']");
365 | checkBoxElt.addEventListener("change", function(?_){ onCheckedStatusChanged(); });
366 |
367 | this.nameElt = elt.querySelector("span.fileName");
368 | nameElt.addEventListener( "click", function(?_){
369 |
370 | if (filteredOut) return;
371 |
372 | onClicked();
373 | } );
374 |
375 | this.renameInput = cast elt.querySelector("input[type='text']");
376 | renameInput.addEventListener("keydown", function(e : KeyboardEvent){
377 | untyped {
378 | if (e.keyIdentifier != null && e.keyIdentifier.toLowerCase() == "enter" ||
379 | e.key != null && e.key.toLowerCase() == "enter") {
380 |
381 | elt.toggleClass(CLASS_RENAMING, false);
382 | onRenameRequested();
383 | }
384 | }
385 | });
386 | renameInput.addEventListener("focusout", function(?_){
387 |
388 | elt.toggleClass(CLASS_RENAMING, false);
389 | onRenameRequested();
390 | });
391 |
392 | this.typeElt = elt.querySelector("span.fileType");
393 | this.dateElt = elt.querySelector("span.lastUpdate");
394 |
395 | this.renameBtn = elt.querySelector("button.rename");
396 | this.renameBtn.addEventListener( "click", function(?_){
397 |
398 | elt.toggleClass(CLASS_RENAMING, true);
399 |
400 | renameInput.value = nameElt.textContent;
401 | renameInput.focus();
402 | });
403 |
404 | this.deleteBtn = elt.querySelector("button.delete");
405 | this.deleteBtn.addEventListener( "click", function(?_){ onDeleteClicked(); } );
406 |
407 | this.selectBtn = elt.querySelector("button.select");
408 | if (selectBtn != null) {
409 | selectBtn.addEventListener( "click", function(?_){
410 |
411 | if (filteredOut) return;
412 |
413 | onSelectClicked();
414 | } );
415 | }
416 | }
417 |
418 | var checkBoxElt : InputElement;
419 | var nameElt : Element;
420 | var renameInput : InputElement;
421 | var typeElt : Element;
422 | var dateElt : Element;
423 |
424 | var renameBtn : Element;
425 | var deleteBtn : Element;
426 | var selectBtn : Null;
427 |
428 | ///
429 | // PROPERTIES
430 | //
431 |
432 | public var elt (default, null) : Element;
433 |
434 | public var isChecked (get, null) : Bool;
435 |
436 | public var name (get, set) : String;
437 |
438 | public var renameValue (get, set) : String;
439 |
440 | public var type (get, set) : String;
441 |
442 | @:isVar public var lastUpdate (get, set) : Date;
443 |
444 | public var selectable (get, set) : Bool;
445 |
446 | public var filteredOut (get, set) : Bool;
447 |
448 | ///
449 | // GETTERS / SETTERS
450 | //
451 |
452 | public function get_isChecked() : Bool {
453 |
454 | return checkBoxElt.checked;
455 | }
456 |
457 | public function get_renameValue() : String {
458 |
459 | return renameInput.value;
460 | }
461 |
462 | public function set_renameValue(v : String) : String {
463 |
464 | renameInput.value = v;
465 |
466 | return v;
467 | }
468 |
469 | public function get_name() : String {
470 |
471 | return nameElt.textContent;
472 | }
473 |
474 | public function set_name(v : String) : String {
475 |
476 | nameElt.textContent = v;
477 |
478 | return v;
479 | }
480 |
481 | public function get_type() : String {
482 |
483 | if (elt.hasClass(CLASS_FOLDER)) {
484 |
485 | return FileTools.DIRECTORY_MIME_TYPE;
486 | }
487 | return typeElt.textContent;
488 | }
489 |
490 | public function set_type(v : String) : String {
491 |
492 | typeElt.textContent = v;
493 |
494 | elt.toggleClass(CLASS_IMAGE, v.indexOf("image/") == 0);
495 | elt.toggleClass(CLASS_SOUND, v.indexOf("audio/") == 0);
496 | elt.toggleClass(CLASS_VIDEO, v.indexOf("video/") == 0);
497 |
498 | return v;
499 | }
500 |
501 | public function get_lastUpdate() : Null {
502 |
503 | return lastUpdate;
504 | }
505 |
506 | public function set_lastUpdate(v : Null) : Null {
507 |
508 | lastUpdate = v;
509 |
510 | if (v != null) {
511 |
512 | dateElt.textContent = DateTools.format(lastUpdate, "%d/%m/%Y"); // FIXME "%x %X" not implemented yet in haxe/js
513 |
514 | } else {
515 |
516 | dateElt.innerHTML = " ";
517 | }
518 | return v;
519 | }
520 |
521 | public function get_selectable() : Bool {
522 |
523 | return !elt.hasClass(CLASS_NOT_SELECTABLE);
524 | }
525 |
526 | public function set_selectable(v : Bool) : Bool {
527 |
528 | elt.toggleClass(CLASS_NOT_SELECTABLE, !v);
529 |
530 | return v;
531 | }
532 |
533 | public function get_filteredOut() : Bool {
534 |
535 | return elt.hasClass(CLASS_FILTERED_OUT);
536 | }
537 |
538 | public function set_filteredOut(v : Bool) : Bool {
539 |
540 | elt.toggleClass(CLASS_FILTERED_OUT, v);
541 |
542 | return v;
543 | }
544 |
545 | ///
546 | // CALLBACKS
547 | //
548 |
549 | public dynamic function onCheckedStatusChanged() : Void { }
550 |
551 | public dynamic function onDeleteClicked() : Void { }
552 |
553 | public dynamic function onRenameRequested() : Void { }
554 |
555 | public dynamic function onSelectClicked() : Void { }
556 |
557 | public dynamic function onClicked() : Void { }
558 | }
--------------------------------------------------------------------------------
/src/ce/core/view/Home.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.core.view;
13 |
14 | import js.html.Element;
15 |
16 | class Home {
17 |
18 | static inline var SELECTOR_SRV_LIST : String = "ul";
19 | static inline var SELECTOR_SRV_ITEM_TMPL : String = "li";
20 |
21 | public function new(elt : Element) {
22 |
23 | this.elt = elt;
24 |
25 | this.listElt = elt.querySelector(SELECTOR_SRV_LIST);
26 |
27 | this.srvItemTmpl = elt.querySelector(SELECTOR_SRV_ITEM_TMPL);
28 | listElt.removeChild(srvItemTmpl);
29 | }
30 |
31 | var elt : Element;
32 |
33 | var listElt : Element;
34 |
35 | var srvItemTmpl : Element;
36 |
37 |
38 | ///
39 | // CALLBACK
40 | //
41 |
42 | public dynamic function onServiceClicked(name : String) : Void { }
43 |
44 |
45 | ///
46 | // API
47 | //
48 |
49 | public function resetList() : Void {
50 |
51 | while(listElt.childNodes.length > 0) {
52 |
53 | listElt.removeChild(listElt.firstChild);
54 | }
55 | }
56 |
57 | public function addService(name : String, displayName : String, description : String) : Void {
58 |
59 | var newSrvIt : Element = cast srvItemTmpl.cloneNode(true);
60 |
61 | newSrvIt.textContent = displayName;
62 | newSrvIt.className = name;
63 |
64 | // TODO description as tooltip ?
65 |
66 | newSrvIt.addEventListener( "click", function(?_){ onServiceClicked(name); } );
67 |
68 | listElt.appendChild(newSrvIt);
69 | }
70 | }
--------------------------------------------------------------------------------
/src/ce/util/HtmlTools.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.util;
13 |
14 | import js.html.Element;
15 | import js.html.Event;
16 |
17 | #if js
18 | import js.html.Node;
19 | #else
20 | import cocktail.core.dom.Node;
21 | #end
22 |
23 | using Lambda;
24 |
25 | /**
26 | * This class provides "jquery-like" HTML Element manipulation helper methods.
27 | * Designed to be imported with 'using' and to be chainable.
28 | */
29 | class HtmlTools {
30 |
31 | /**
32 | * Returns an HTML element's classes as an array of lower-case strings.
33 | * Passing an array of string as the second parameter will replace the element's classes.
34 | */
35 | public static function classes( el:Element , ?cl : Array ) : Array {
36 |
37 | if( cl != null ){
38 | //trace('setting classes ${cl.join(",")}');
39 | el.className = cl.join(" ");
40 | }
41 | return el.className.split(" ")
42 | .filter( function(s) return ( s != '' ) )
43 | .map( function(s) return s.toLowerCase() );
44 | }
45 |
46 | /**
47 | * Toggles a class on an HTML element.
48 | * Returns the element
49 | */
50 | public static function toggleClass( el:Element, cl:String, flag : Bool ) : Element {
51 | if( flag ){
52 | addClass( el , cl );
53 | }else{
54 | removeClass( el , cl );
55 | }
56 | return el;
57 | }
58 |
59 | /**
60 | * Checks if the given class is present for the given HTML element.
61 | * The method ignores case.
62 | */
63 | public inline static function hasClass( el:Element, cl:String ) : Bool {
64 | return classes(el).has( cl.toLowerCase() );
65 | }
66 |
67 | /**
68 | * Adds a class to an HTML element
69 | * The 'cl' parameter can contain several space-separated classes
70 | * Returns the element
71 | */
72 | public static function addClass( el:Element , cl :String ){
73 |
74 | var cls = classes(el);
75 | var changed = false;
76 | for( c in cl.split(" ") ){
77 | if( !cls.has( c.toLowerCase() ) ){
78 | cls.push( c.toLowerCase() );
79 | changed = true;
80 | }
81 | }
82 | if( changed )
83 | classes(el,cls);
84 | return el;
85 | }
86 |
87 | /**
88 | * Removes a class from an HTML element
89 | * The 'cl' parameter can contain several space-separated classes
90 | * Returns the element
91 | */
92 | public static function removeClass( el:Element , cl :String){
93 | var cls = classes(el);
94 | var changed = false;
95 | for( c in cl.split(" ") ){
96 | if( cls.remove( c.toLowerCase() ) )
97 | changed = true;
98 | }
99 | if( changed )
100 | classes(el,cls);
101 | return el;
102 | }
103 |
104 | /**
105 | * Calculates an HTML element's "absolute" offset by recursively adding his ancestors' offsets
106 | * Returned x corresponds to offsetLeft, y corresponds to offsetRight
107 | */
108 | public static function offset( el:Element ) : { x : Int, y : Int }{
109 | var pos = { x : el.offsetLeft , y : el.offsetTop };
110 | var parent = parentElement(el);
111 | while( parent != null ){
112 | pos.x += parent.offsetLeft;
113 | pos.y += parent.offsetTop;
114 | parent = parentElement(parent);
115 | }
116 | return pos;
117 | }
118 |
119 | /**
120 | * Returns an HTML Element's parent HTML Element,
121 | * if the parent is not an Element (ie a Document), null is returned
122 | */
123 | public static function parentElement( el : Element ) : Element {
124 | var parent = el.parentNode;
125 | if( parent != null && parent.nodeType == #if js Node.ELEMENT_NODE #else cocktail.core.dom.DOMConstants.ELEMENT_NODE #end ){
126 | return cast(parent);
127 | }
128 | return null;
129 | }
130 |
131 | /**
132 | * Searches for a vendor-prefixed method called 'field' on an HTML Element,
133 | * and calls it with arguments 'args'.
134 | * For non-JS platforms (Cocktail), the 'field' method is called directly
135 | * TODO: Optimize by making it a macro
136 | */
137 | public static function vendorPrefixCall( el : Node , field : String , ?args : Array = null ) : Dynamic {
138 |
139 | if( args == null ) args = [];
140 |
141 | #if !js
142 | return Reflect.callMethod( el , Reflect.field( el, field ) , args );
143 | #else
144 |
145 | for( prefixed in vendorPrefix(field) ){
146 | var v = Reflect.field( el , prefixed );
147 | if( untyped __js__("typeof v") != "undefined" ){
148 | return Reflect.callMethod( el , v , args );
149 | }
150 | }
151 |
152 | return null;
153 |
154 | #end
155 | }
156 |
157 | /**
158 | * Return an array of all possible vendor-prefixed versions of 'field':
159 | * [not prefixed], webkit, moz, ms, o
160 | * Not supposed to work for CSS properties, only Javascript events, methods and properties
161 | */
162 | public static function vendorPrefix( field : String , ?capitalize : Bool = true ) : Array {
163 | var prefixes = ["","webkit","moz","ms","o"];
164 | var fields = [field];
165 |
166 | // exception for webkitIsFullScreen
167 | if( field == "fullScreen" ){
168 | fields.push("isFullScreen");
169 | }
170 |
171 | var prefixed = [];
172 | for( p in prefixes ){
173 | for( f in fields ){
174 | prefixed.push( p + ( capitalize ? ( f.substr(0,1).toUpperCase() + f.substr(1) ) : f ) );
175 | }
176 | }
177 | return prefixed;
178 | }
179 |
180 | /**
181 | * Searches for a vendor-prefixed property called 'field' on an HTML Element,
182 | * and returns its value.
183 | * For non-JS platforms (Cocktail), the 'field' method is called directly
184 | * TODO: Optimize by making it a macro
185 | */
186 | public static function vendorPrefixProperty( el : Node , field : String ) : Dynamic {
187 | #if !js
188 | // if we're in Cocktail = no prefix
189 | return Reflect.field(el , field);
190 | #else
191 | for( prefixed in vendorPrefix( field ) ){
192 | var v = Reflect.field( el , prefixed );
193 | if( untyped __js__("typeof v") != "undefined" ){
194 | return v;
195 | }
196 | }
197 | return null;
198 | #end
199 | }
200 |
201 | /**
202 | * Adds event listener to an HTML Node
203 | * 'event' may be a space separated list of event types
204 | */
205 | public inline static function addEvent( el : Node , event : String , callback : Event -> Void ){
206 | addEvents( el, event.split(" "), callback );
207 | }
208 |
209 | /**
210 | * Adds several event listeners to an HTML Node
211 | */
212 | public inline static function addEvents( el : Node , events : Array , callback : Event -> Void ){
213 | for( e in events ){
214 | el.addEventListener( e , callback );
215 | }
216 | }
217 |
218 | }
--------------------------------------------------------------------------------
/src/ce/util/OptionTools.hx:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloud Explorer, lightweight frontend component for file browsing with cloud storage services.
3 | * @see https://github.com/silexlabs/cloud-explorer
4 | *
5 | * Cloud Explorer works as a frontend interface for the unifile node.js module:
6 | * @see https://github.com/silexlabs/unifile
7 | *
8 | * @author Thomas Fétiveau, http://www.tokom.fr & Alexandre Hoyau, http://lexoyo.me
9 | * Copyrights SilexLabs 2013 - http://www.silexlabs.org/ -
10 | * License MIT
11 | */
12 | package ce.util;
13 |
14 | import ce.core.model.api.ExportOptions;
15 | import ce.core.model.api.PickOptions;
16 | import ce.core.model.api.ReadOptions;
17 | import ce.core.model.api.WriteOptions;
18 |
19 | class OptionTools {
20 |
21 | static public function normalizePickOptions(o : Null) : Null {
22 |
23 | if (o == null) return o;
24 |
25 | if (o.mimetype != null) o.mimetype = o.mimetype.toLowerCase();
26 | if (o.extension != null) o.extension = o.extension.toLowerCase();
27 |
28 | if (o.mimetypes != null) {
29 | for (mi in 0...o.mimetypes.length) {
30 | o.mimetypes[mi] = o.mimetypes[mi].toLowerCase();
31 | }
32 | }
33 | if (o.extensions != null) {
34 | for (ei in 0...o.extensions.length) {
35 | o.extensions[ei] = o.extensions[ei].toLowerCase();
36 | }
37 | }
38 |
39 | return o;
40 | }
41 |
42 | static public function normalizeExportOptions(o : Null) : Null {
43 |
44 | if (o == null) return o;
45 |
46 | if (o.mimetype != null) o.mimetype = o.mimetype.toLowerCase();
47 | if (o.extension != null) o.extension = o.extension.toLowerCase();
48 |
49 | return o;
50 | }
51 |
52 | static public function normalizeReadOptions(o : Null) : Null {
53 |
54 | // nothing
55 |
56 | return o;
57 | }
58 |
59 | static public function normalizeWriteOptions(o : Null) : Null {
60 |
61 | // nothing
62 |
63 | return o;
64 | }
65 | }
--------------------------------------------------------------------------------