├── .editorconfig
├── .gitignore
├── .jshintrc
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── README.md
├── VERSION
├── filepicker_debug.js
├── index.js
├── karma.conf.js
├── package.json
├── src
├── communication
│ ├── comm.js
│ ├── comm_fallback.js
│ ├── cookies.js
│ └── handlers.js
├── dialog
│ ├── exporter.js
│ ├── modal.js
│ ├── picker.js
│ └── window.js
├── finalize.js
├── library
│ ├── conversions.js
│ ├── errors.js
│ ├── lib.js
│ ├── mimetypes.js
│ ├── services.js
│ └── urls.js
├── server
│ ├── ajax.js
│ ├── files.js
│ └── iframeAjax.js
├── setup-spec.js
├── setup.js
├── utils
│ ├── base64.js
│ ├── browser.js
│ ├── conversions_util.js
│ ├── json.js
│ ├── strutil.js
│ ├── util.js
│ └── window_utils.js
└── widgets
│ ├── dragdrop.js
│ ├── responsive_images.js
│ └── widgets.js
└── tests
└── unit
├── communication
├── comm-spec.js
├── cookies-spec.js
└── handlers-spec.js
├── dialog
├── exporter-spec.js
├── modal-spec.js
├── picker-spec.js
└── window-spec.js
├── images
├── a_test.png
├── b_test.png
└── c_test.png
├── init-spec.js
├── library
├── conversions-spec.js
├── errors-spec.js
├── lib-spec.js
├── mimetypes-spec.js
├── services-spec.js
└── urls-spec.js
├── responses
├── ajax_responses-spec.js
├── conversion_responses-spec.js
├── files_read_responses-spec.js
├── files_write_responses-spec.js
├── modal_responses-spec.js
└── responses_init-spec.js
├── server
├── ajax-spec.js
├── files-spec.js
└── iframeAjax-spec.js
├── utils
├── base64-spec.js
├── browser-spec.js
├── conversions_util-spec.js
├── json-spec.js
├── strutil-spec.js
└── util-spec.js
└── widgets
├── dragdrop-spec.js
├── responsive_images-spec.js
└── widgets-spec.js
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.js]
13 | indent_size = 4
14 |
15 | [*.md]
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tmp
3 | npm-debug.log
4 | dist.tar
5 | dist
6 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": false,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 2,
10 | "latedef": false,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": false,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "evil": true,
21 | "white": true,
22 | "validthis": true,
23 | "globals": {
24 | /* MOCHA */
25 | "describe" : false,
26 | "it" : false,
27 | "before" : false,
28 | "beforeEach" : false,
29 | "after" : false,
30 | "afterEach" : false,
31 | "expect" : false,
32 | "window": false,
33 | "navigator":false,
34 | "File": false,
35 | "document": false,
36 | "Image": false,
37 | "FileReader": false,
38 | "Blob": false,
39 | "filepicker": false
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .tmp
3 | npm-debug.log
4 | dist.tar
5 | src
6 | .jshintrc
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.12.0'
4 | cache:
5 | directories:
6 | - node_modules
7 | before_install:
8 | - npm i -g npm@^2.0.0
9 | install:
10 | - sudo apt-get update
11 | - sudo apt-get install xvfb
12 | - sudo apt-get install firefox
13 | - npm install
14 | script:
15 | - xvfb-run npm run test
16 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 | ### 2.4.20 (12.03.2018)
3 | - Bugfix for loading picker with video in iframe (chrome browser)
4 |
5 | ### 2.4.19 (23.02.2017)
6 | - Add changes for Cloudinary upload presets and resource type [#57](https://github.com/filepicker/filepicker-js/pull/57)
7 |
8 | ### 2.4.18 (03.10.2016)
9 | - Make try/catch block less aggressive [#46](https://github.com/filepicker/filepicker-js/pull/46)
10 |
11 | ### 2.4.17 (17.08.2016)
12 | - Fix storeUrl regex [#43](https://github.com/filepicker/filepicker-js/pull/43)
13 | - Guard for malformed messages [#44](https://github.com/filepicker/filepicker-js/pull/44)
14 |
15 | ### 2.4.16 (29.07.2016)
16 | - Bugfix for `logout()` method
17 |
18 | ### 2.4.15 (28.07.2016)
19 | - Added `logout()` method [#41](https://github.com/filepicker/filepicker-js/pull/41)
20 |
21 | ### 2.4.13 & 2.4.14 (30.06.2016)
22 | - Fixed issue [#21](https://github.com/filepicker/filepicker-js/issues/21)
23 |
24 | ### 2.4.12 (24.06.2016)
25 | - [Blobs](https://developer.mozilla.org/en-US/docs/Web/API/Blob) support in `filepicker.store()` method. [#37](https://github.com/filepicker/filepicker-js/pull/37)
26 |
27 | ### 2.4.11 (19.05.2016)
28 | - Pass `converted` property to blob in onSuccess callback
29 |
30 | ### 2.4.10 (05.05.2016)
31 | - Replace cdn urls for preview widget [#34](https://github.com/filepicker/filepicker-js/pull/34)
32 |
33 | ### 2.4.9 (04.05.2016)
34 | - Passing crop and rotation data to blob [#33](https://github.com/filepicker/filepicker-js/pull/33)
35 |
36 | ### 2.4.8 (25.04.2016)
37 | - Handle HTTP server errors [#32](https://github.com/filepicker/filepicker-js/pull/32)
38 |
39 | ### 2.4.7 (07.04.2016)
40 | - Extensions are case insensitive now [#31](https://github.com/filepicker/filepicker-js/pull/31)
41 |
42 | ### 2.4.5 (25.02.2016)
43 | - Widget button rebranded to FileStack [#29](https://github.com/filepicker/filepicker-js/pull/29)
44 |
45 | ### 2.4.4 (16.02.2016)
46 | - Add filtering support to audio client [#28](https://github.com/filepicker/filepicker-js/pull/28)
47 |
48 | ### 2.4.3 (09.02.2016)
49 | - Add storeRegion option to the picker. [#19](https://github.com/filepicker/filepicker-js/pull/19)
50 | - Added option for setting video & webcam resolution [#20](https://github.com/filepicker/filepicker-js/pull/20)
51 |
52 | ### 2.4.2 (08.02.2016)
53 | - Responsive images lookup can be triggered at any time. [#23](https://github.com/filepicker/filepicker-js/pull/23)
54 | - Fix typo in ```data-fp-custom-source-conatiner``` attribute name. [#27](https://github.com/filepicker/filepicker-js/pull/27)
55 |
56 | ### 2.4.1 (05.02.2016)
57 | - Allow every image to be processed. [#25](https://github.com/filepicker/filepicker-js/pull/25)
58 |
59 | ### 2.3.8 (22.01.2016)
60 | - Handle safari standalone mode [#11](https://github.com/filepicker/filepicker-js/pull/11)
61 |
62 | ### 2.3.7 (20.01.2016)
63 | - Parse options.noFileReader parameter for server vars [#17](https://github.com/filepicker/filepicker-js/pull/17)
64 |
65 | ### 2.3.6 (15.01.2016)
66 | - Bugfix for dialog close method.
67 |
68 | ### 2.3.5 (15.01.2016)
69 | - Add ability to close opened dialog from JavaScript. [#15](https://github.com/filepicker/filepicker-js/pull/15)
70 | - Reckognize .vob file extension. [#16](https://github.com/filepicker/filepicker-js/pull/16)
71 |
72 | ### 2.3.4 (12.01.2016)
73 | - Add custom source container and path options for html widgets attributes list. [#13](https://github.com/filepicker/filepicker-js/pull/13)
74 |
75 | ### 2.3.3 (16.12.2015)
76 | - Hotfix. Missing slash in processing base url.
77 |
78 | ### 2.3.2 (16.12.2015)
79 | - Set dynamicly domain for processing.
80 |
81 | ### 2.3.1 (23.11.2015)
82 | - Add custom source path as a picker option [#7](https://github.com/filepicker/filepicker-js/pull/7)
83 |
84 | ### 2.3.0 (18.11.2015)
85 | - Add custom source bucket name as a picker option [#5](https://github.com/filepicker/filepicker-js/pull/5)
86 | - Add custom css url option for viewer [#6](https://github.com/filepicker/filepicker-js/pull/6)
87 |
88 | ### 2.2.1 (5.11.2015)
89 | - Add custom text option to the widget options. [#4](https://github.com/filepicker/filepicker-js/pull/4)
90 |
91 | ### 2.2.0 (3.11.2015)
92 | - Add responsive images feature. [#1](https://github.com/filepicker/filepicker-js/pull/1)
93 |
94 | ### 2.1.3 (15.10.2015)
95 | - Add prepublish action. Make sure to build dist version before publishing to npm.
96 |
97 | ### 2.1.2 (15.10.2015)
98 | - Fix. Filtering allowed conversions. Use hasOwnProperty method.
99 |
100 | ### 2.1.1 (09.10.2015)
101 | - Do not use Array.filter and Array.indexOf methods. Support IE8.
102 |
103 | ### 2.1.0 (06.10.2015)
104 | - Add filepicker debug script.
105 | - Add client value to response object.
106 |
107 | ### 2.0.0 (28.09.2015)
108 | - init filepicker-js repository as separate module
109 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # filepicker-js
2 | Filepicker javascript client library.
3 |
4 | ## Usage
5 |
6 | In order to use filepicker javascript library in your project, you need to include the following script in your HTML:
7 |
8 | ```
9 |
10 | ```
11 |
12 | If you want to load the javascript in a non-blocking fashion, you can use this instead:
13 | ```
14 |
17 | ```
18 | Script above use latest library release. Assets are compressed (gzipped) and served via CDN.
19 | You can also link to specific version.
20 |
21 | [https://api.filepicker.io/v2/filepicker-2.1.3.js](https://api.filepicker.io/v2/filepicker-2.1.3.js)
22 | [https://api.filepicker.io/v2/filepicker-2.1.3.min.js](https://api.filepicker.io/v2/filepicker-2.1.3.min.js)
23 |
24 | See [Changelog](CHANGELOG.md)
25 |
26 | Filepicker library is avaliable via bower [Bower friendly repositorium](https://github.com/krystiangw/filepicker-js-bower)
27 |
28 | ```
29 | $ bower install filepicker-js --save
30 | ```
31 |
32 | And via npm + browserify
33 | ```
34 | $ npm install filepicker-js --save
35 | ```
36 |
37 | To use it with browseify place in your code:
38 | ```
39 | var filepickerLibrary = require('filepicker-js');
40 | ```
41 |
42 | Library provide ```window.filepicker``` with methods:
43 | ```setKey, pick, pickFolder, pickMultiple, pickAndStore, read, write, writeUrl, export, processImage, store, storeUrl, stat, metadata, remove, convert, constructWidget, makeDropPane```. See detailed [docs](https://www.filepicker.com/documentation/file_ingestion/javascript_api/pick?v=v2).
44 |
45 | Next thing to do is setting apikey. If you dont have one - register free account [here](https://www.filepicker.com/register/free). Setting key is possible in 2 ways:
46 |
47 | * use ```filepicker.setKey('yourApiKey')``` method.
48 | * as widget attribute ```data-fp-apikey="yourApiKey"```
49 |
50 |
51 | ## Contributing
52 | Contributing welcomed. First install npm dependencies.
53 | ```
54 | npm install
55 | ```
56 | To watch changes and build script run:
57 | ```
58 | npm run watch
59 | ```
60 | With jshint:
61 | ```
62 | npm run watch-linter
63 | ```
64 |
65 |
66 | ## Releasing
67 | 1. When updating version be sure to update it in all files:
68 |
69 | ```
70 | ./VERSION
71 | ./package.json
72 | ./src/library/lib.js
73 | ```
74 |
75 | 2. Set git tag with current version.
76 |
77 | 3. Be sure to update [npm package version](https://www.npmjs.com/package/filepicker-js) :
78 | ```
79 | npm publish
80 | ```
81 |
82 | 4. And [Bower-friendly version of filepicker-js](https://github.com/filepicker/filepicker-js-bower)
83 |
84 |
85 | ## Deployment
86 | ### Filepicker
87 |
88 | Use ansible script to deploy current version for filepicker.
89 |
90 | ```
91 | source ../vagrant/aws/new && ansible-playbook -i env/production/inventory filepicker_api/deploy_js_library_v2.yml
92 | ```
93 |
94 | * optionally to deploy from branch othter than master
95 | ```
96 | -e emergency_deploy="yes"
97 | ```
98 |
99 | * optionally not to overwrite edge version
100 | ```
101 | -e edge_version="no"
102 | ```
103 |
104 | It overwrites [filepicker.js](https://api.filepicker.io/v2/filepicker.js) with current version. It creates versioned files, eg for v2.4.0:
105 |
106 | * [filepicker-2.4.0.js](https://api.filepicker.io/v2/filepicker-2.4.0.js)
107 | * [filepicker-2.4.0.min.js](https://api.filepicker.io/v2/filepicker-2.4.0.min.js)
108 | * [filepicker_debug-2.4.0.js](https://api.filepicker.io/v2/filepicker_debug-2.4.0.js)
109 |
110 | ### Filestack
111 |
112 | ```
113 | source ~/.filepicker/aws_new && ansible-playbook -i env/production filestack_api/build_js.yml
114 | ```
115 |
116 | Its working basically the same. The only diffrents is domain and file name it creates.
117 | [https://api.filestackapi.com/filestack.js](https://api.filestackapi.com/filestack.js)
118 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 2.4.20
2 |
--------------------------------------------------------------------------------
/filepicker_debug.js:
--------------------------------------------------------------------------------
1 | //Include this script to help debug
2 |
3 | filepicker.debug = true;
4 |
5 | filepicker.error_map = {
6 | /*General*/
7 | 400: {
8 | message: "Invalid request to the server - do you need to pass a security policy and signature?",
9 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/400'
10 | },
11 | 403: {
12 | message: "Not authorized to make this request",
13 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/403'
14 | },
15 | 101: {
16 | message: 'The user closed the picker without choosing a file',
17 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/101'
18 | },
19 | 111: {
20 | message: "Your browser doesn't support reading from DOM File objects",
21 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/111'
22 | },
23 | 112: {
24 | message: "Your browser doesn't support reading from different domains",
25 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/112'
26 | },
27 | 113: {
28 | message: "The website of the URL you provided does not allow other domains to read data",
29 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/113'
30 | },
31 | 114: {
32 | message: "The website of the URL you provided had an error",
33 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/114'
34 | },
35 | 115: {
36 | message: "File not found",
37 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/115'
38 | },
39 | 118: {
40 | message: "Unknown read error",
41 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/118'
42 | },
43 | 121: {
44 | message: "The FPFile to write to cannot be found",
45 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/121'
46 | },
47 | 122: {
48 | message: "The Remote URL could not be reached",
49 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/122'
50 | },
51 | 123: {
52 | message: "Unknown write error",
53 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/123'
54 | },
55 | 131: {
56 | message: 'The user closed the dialog without exporting a file',
57 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/131'
58 | },
59 | 141: {
60 | message: "The FPFile to convert could not be found",
61 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/141'
62 | },
63 | 142: {
64 | message: "The FPFile could not be converted with the requested parameters",
65 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/142'
66 | },
67 | 143: {
68 | message: "Unknown error when converting the file",
69 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/143'
70 | },
71 | 151: {
72 | message: "The file store could not be reached",
73 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/151'
74 | },
75 | 152: {
76 | message: "The Remote URL could not be reached",
77 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/152'
78 | },
79 | 153: {
80 | message: "Unknown error when storing",
81 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/153'
82 | },
83 | 161: {
84 | message: "The file cannot be found",
85 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/161'
86 | },
87 | 162: {
88 | message: "Error fetching metadata",
89 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/162'
90 | },
91 | 163: {
92 | message: "Unknown error when fetching metadata",
93 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/163'
94 | },
95 | 171: {
96 | message: "The file cannot be found, and may have already been deleted",
97 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/171'
98 | },
99 | 172: {
100 | message: "The underlying content store could not be reached",
101 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/172'
102 | },
103 | 173: {
104 | message: "Unknown issue when removing",
105 | moreInfo: 'https://developers.filepicker.io/answers/jsErrors/173'
106 | }
107 | };
108 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | Module definition for browserify
3 | */
4 | require('./dist/filepicker');
5 | module.exports = filepicker;
6 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Thu Oct 08 2015 11:32:53 GMT+0200 (CEST)
3 |
4 | module.exports = function(config) {
5 | config.set({
6 |
7 | // base path that will be used to resolve all patterns (eg. files, exclude)
8 | basePath: '',
9 |
10 |
11 | // frameworks to use
12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
13 | frameworks: ['jasmine-ajax', 'jasmine'],
14 |
15 |
16 | // list of files / patterns to load in the browser
17 | files: [
18 | 'dist/filepicker-spec.js',
19 | 'tests/unit/init-spec.js',
20 | 'tests/unit/responses/*.js',
21 | 'tests/unit/**/*spec.js',
22 | ],
23 |
24 |
25 | // list of files to exclude
26 | exclude: [
27 | ],
28 |
29 |
30 | // preprocess matching files before serving them to the browser
31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
32 | preprocessors: {
33 | },
34 |
35 |
36 | // test results reporter to use
37 | // possible values: 'dots', 'progress'
38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
39 | reporters: ['progress'],
40 |
41 |
42 | // web server port
43 | port: 9876,
44 |
45 |
46 | // enable / disable colors in the output (reporters and logs)
47 | colors: true,
48 |
49 |
50 | // level of logging
51 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
52 | logLevel: config.LOG_INFO,
53 |
54 |
55 | // enable / disable watching file and executing tests whenever any file changes
56 | autoWatch: true,
57 |
58 |
59 | // start these browsers
60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
61 | browsers: ['Firefox'],
62 |
63 |
64 | // Continuous Integration mode
65 | // if true, Karma captures browsers, runs the tests and exits
66 | singleRun: false
67 | })
68 | }
69 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "filepicker-js",
3 | "version": "2.4.20",
4 | "description": "Filepicker client javascript library",
5 | "main": "index.js",
6 | "scripts": {
7 | "prepublish": "npm run build",
8 | "pretest": "npm run prebuild && uglifyjs ./src/setup-spec.js ./src/**/*.js ./src/finalize.js -o ./dist/filepicker-spec.js -b ",
9 | "test": "./node_modules/.bin/karma start --single-run",
10 | "prebuild": "rm -rf dist && mkdir dist",
11 | "postbuild": "uglifyjs dist/filepicker.js -o ./dist/filepicker.min.js --compress --mangle && cp filepicker_debug.js dist/",
12 | "build": "uglifyjs ./src/setup.js ./src/**/*.js ./src/finalize.js -o ./dist/filepicker.js -b ",
13 | "watch": "onchange ./src/*.js ./src/**/*.js -- npm run build",
14 | "watch-test": "onchange ./tests/unit/*.js ./tests/unit/**/*.js ./src/*.js ./src/**/*.js -- npm run test",
15 | "linter": "jshint ./src/*.js ./src/**/*.js",
16 | "watch-linter": "onchange ./src/*.js ./src/**/*.js -- npm run linter"
17 | },
18 | "repository": {
19 | "type": "git",
20 | "url": "https://github.com/filepicker/filepicker-js.git"
21 | },
22 | "keywords": [
23 | "filepicker",
24 | "file",
25 | "upload",
26 | "file",
27 | "processing",
28 | "store",
29 | "widget"
30 | ],
31 | "author": "krystiangw",
32 | "license": "MIT",
33 | "bugs": {
34 | "url": "https://github.com/filepicker/filepicker-js/issues"
35 | },
36 | "homepage": "https://github.com/filepicker/filepicker-js",
37 | "devDependencies": {
38 | "jasmine-core": "2.3.4",
39 | "jshint": "2.8.0",
40 | "karma": "0.13.19",
41 | "karma-firefox-launcher": "0.1.6",
42 | "karma-jasmine": "0.3.6",
43 | "karma-jasmine-ajax": "0.1.13",
44 | "onchange": "2.0.0",
45 | "uglify-js": "2.6.0"
46 | },
47 | "dependencies": {}
48 | }
49 |
--------------------------------------------------------------------------------
/src/communication/comm.js:
--------------------------------------------------------------------------------
1 | //comm.js
2 | 'use strict';
3 |
4 | filepicker.extend('comm', function(){
5 | var fp = this;
6 |
7 | var COMM_IFRAME_NAME = 'filepicker_comm_iframe';
8 | var API_IFRAME_NAME = 'fpapi_comm_iframe';
9 |
10 | /*
11 | * Opens the IFrame if there isn't one
12 | */
13 | var openCommIframe = function(){
14 | if (window.frames[COMM_IFRAME_NAME] === undefined) {
15 | //Attach a event handler
16 | openCommunicationsChannel();
17 |
18 | //Opening an iframe to send events
19 | var commIFrame;
20 | commIFrame = document.createElement('iframe');
21 | commIFrame.id = commIFrame.name = COMM_IFRAME_NAME;
22 | commIFrame.src = fp.urls.COMM;
23 | commIFrame.style.display = 'none';
24 | document.body.appendChild(commIFrame);
25 | }
26 | if (window.frames[API_IFRAME_NAME] === undefined) {
27 | //Attach a event handler
28 | openCommunicationsChannel();
29 |
30 | //Opening an iframe to send events
31 | var apiIFrame;
32 | apiIFrame = document.createElement('iframe');
33 | apiIFrame.id = apiIFrame.name = API_IFRAME_NAME;
34 | apiIFrame.src = fp.urls.API_COMM;
35 | apiIFrame.style.display = 'none';
36 | document.body.appendChild(apiIFrame);
37 | }
38 | };
39 |
40 | var communicationsHandler = function(event){
41 | if (event.origin !== fp.urls.BASE && event.origin !== fp.urls.DIALOG_BASE) {
42 | return;
43 | }
44 | try {
45 | var data = fp.json.parse(event.data);
46 | } catch(err) {
47 | console.log('[Filepicker] Failed processing message:', event.data);
48 | }
49 | if (data) {
50 | fp.handlers.run(data);
51 | }
52 | };
53 |
54 | /*
55 | * 1. Creates the general communcation handler
56 | * 2. Set to listen
57 | * ONLY RUN ONCE
58 | */
59 | var isOpen = false;
60 |
61 | var openCommunicationsChannel = function(){
62 | if (isOpen){
63 | return;
64 | } else {
65 | isOpen = true;
66 | }
67 |
68 |
69 | //Modern
70 | if (window.addEventListener) {
71 | window.addEventListener('message', communicationsHandler, false);
72 | //IE8, FF3
73 | } else if (window.attachEvent) {
74 | window.attachEvent('onmessage', communicationsHandler);
75 | //No hope
76 | } else {
77 | throw new fp.FilepickerException('Unsupported browser');
78 | }
79 | };
80 |
81 | var destroyCommIframe = function(){
82 | //Modern
83 | if (window.removeEventListener) {
84 | window.removeEventListener('message', communicationsHandler, false);
85 | //IE8, FF3
86 | } else if (window.attachEvent) {
87 | window.detachEvent('onmessage', communicationsHandler);
88 | //No hope
89 | } else {
90 | throw new fp.FilepickerException('Unsupported browser');
91 | }
92 |
93 | if (!isOpen){
94 | return;
95 | } else {
96 | isOpen = false;
97 | }
98 | //Also removing iframe
99 | var iframes = document.getElementsByName(COMM_IFRAME_NAME);
100 | for (var i = 0; i < iframes.length; i++){
101 | iframes[i].parentNode.removeChild(iframes[i]);
102 | }
103 | try{delete window.frames[COMM_IFRAME_NAME];}catch(e){}
104 | var api_iframes = document.getElementsByName(API_IFRAME_NAME);
105 | for (var j = 0; j < api_iframes.length; j++){
106 | api_iframes[j].parentNode.removeChild(api_iframes[j]);
107 | }
108 | try{delete window.frames[API_IFRAME_NAME];}catch(e){}
109 | };
110 |
111 | return {
112 | openChannel: openCommIframe,
113 | closeChannel: destroyCommIframe
114 | };
115 | });
116 |
--------------------------------------------------------------------------------
/src/communication/comm_fallback.js:
--------------------------------------------------------------------------------
1 | //comm_fallback.js
2 | 'use strict';
3 |
4 | filepicker.extend('comm_fallback', function(){
5 | var fp = this;
6 |
7 | var FP_COMM_IFRAME_NAME = 'filepicker_comm_iframe';
8 | var HOST_COMM_IFRAME_NAME = 'host_comm_iframe';
9 | var base_host_location = '';
10 | var hash_check_interval = 200;
11 |
12 | /*
13 | * Opens the IFrame if there isn't one
14 | */
15 | var openCommIframe = function(){
16 | openHostCommIframe();
17 | };
18 |
19 | //First we open a host comm iframe to test what the url is we'll be working with on the host
20 | //to make sure we don't run into redirect issues
21 | var openHostCommIframe = function(){
22 | if (window.frames[HOST_COMM_IFRAME_NAME] === undefined) {
23 | //Opening an iframe to send events
24 | var hostCommIFrame;
25 | hostCommIFrame = document.createElement('iframe');
26 | hostCommIFrame.id = hostCommIFrame.name = HOST_COMM_IFRAME_NAME;
27 | base_host_location = hostCommIFrame.src = fp.urls.constructHostCommFallback();
28 | hostCommIFrame.style.display = 'none';
29 | var onload = function(){
30 | base_host_location = hostCommIFrame.contentWindow.location.href;
31 | //Then we open the filepicker comm iframe
32 | openFPCommIframe();
33 | };
34 | if (hostCommIFrame.attachEvent) {
35 | hostCommIFrame.attachEvent('onload', onload);
36 | } else {
37 | hostCommIFrame.onload = onload;
38 | }
39 | document.body.appendChild(hostCommIFrame);
40 | }
41 | };
42 |
43 | var openFPCommIframe = function(){
44 | if (window.frames[FP_COMM_IFRAME_NAME] === undefined) {
45 | //Opening an iframe to send events
46 | var fpCommIFrame;
47 | fpCommIFrame = document.createElement('iframe');
48 | fpCommIFrame.id = fpCommIFrame.name = FP_COMM_IFRAME_NAME;
49 | fpCommIFrame.src = fp.urls.FP_COMM_FALLBACK + '?host_url=' + encodeURIComponent(base_host_location);
50 | fpCommIFrame.style.display = 'none';
51 | document.body.appendChild(fpCommIFrame);
52 | }
53 | openCommunicationsChannel();
54 | };
55 |
56 | /*
57 | * 1. Creates the general communcation handler
58 | * 2. Set to listen
59 | * ONLY RUN ONCE
60 | */
61 | var isOpen = false;
62 | var timer;
63 | var lastHash = '';
64 | var checkHash = function(){
65 | var comm_iframe = window.frames[FP_COMM_IFRAME_NAME];
66 | if (!comm_iframe) {return;}
67 | var host_iframe = comm_iframe.frames[HOST_COMM_IFRAME_NAME];
68 | if (!host_iframe) {return;}
69 |
70 | var hash = host_iframe.location.hash;
71 | //sanitization
72 | if (hash && hash.charAt(0) === '#') {
73 | hash = hash.substr(1);
74 | }
75 | if (hash === lastHash) {return;}
76 | lastHash = hash;
77 | if (!lastHash) {return;}
78 |
79 | var data;
80 | try{
81 | data = fp.json.parse(hash);
82 | } catch (e){}
83 |
84 | if (data) {
85 | fp.handlers.run(data);
86 | }
87 | };
88 |
89 | var openCommunicationsChannel = function(){
90 | if (isOpen){
91 | return;
92 | } else {
93 | isOpen = true;
94 | }
95 |
96 | timer = window.setInterval(checkHash, hash_check_interval);
97 | };
98 |
99 | var destroyCommIframe = function(){
100 | window.clearInterval(timer);
101 |
102 | if (!isOpen){
103 | return;
104 | } else {
105 | isOpen = false;
106 | }
107 | //Also removing iframe
108 | var iframes = document.getElementsByName(FP_COMM_IFRAME_NAME);
109 | for (var i = 0; i < iframes.length; i++){
110 | iframes[i].parentNode.removeChild(iframes[i]);
111 | }
112 | try{delete window.frames[FP_COMM_IFRAME_NAME];}catch(e){}
113 |
114 | iframes = document.getElementsByName(HOST_COMM_IFRAME_NAME);
115 | for (i = 0; i < iframes.length; i++){
116 | iframes[i].parentNode.removeChild(iframes[i]);
117 | }
118 | try{delete window.frames[HOST_COMM_IFRAME_NAME];}catch(e){}
119 | };
120 |
121 | var isEnabled = !('postMessage' in window);
122 | var setEnabled = function(enabled) {
123 | if (enabled !== isEnabled) {
124 | isEnabled = !!enabled;
125 | if (isEnabled) {
126 | activate();
127 | } else {
128 | deactivate();
129 | }
130 | }
131 | };
132 |
133 | var old_comm;
134 | var activate = function(){
135 | old_comm = fp.comm;
136 | fp.comm = {
137 | openChannel: openCommIframe,
138 | closeChannel: destroyCommIframe
139 | };
140 | };
141 |
142 | var deactivate = function(){
143 | fp.comm = old_comm;
144 | old_comm = undefined;
145 | };
146 |
147 | if (isEnabled) {
148 | activate();
149 | }
150 |
151 | return {
152 | openChannel: openCommIframe,
153 | closeChannel: destroyCommIframe,
154 | isEnabled: isEnabled
155 | };
156 | });
157 |
--------------------------------------------------------------------------------
/src/communication/cookies.js:
--------------------------------------------------------------------------------
1 | //cookies.js
2 | 'use strict';
3 |
4 | filepicker.extend('cookies', function(){
5 | var fp = this;
6 |
7 | var getReceiveCookiesMessage = function(callback) {
8 | var handler = function(data) {
9 | if (data.type !== 'ThirdPartyCookies'){
10 | return;
11 | }
12 | fp.cookies.THIRD_PARTY_COOKIES = !!data.payload;
13 | if (callback && typeof callback === 'function'){ callback(!!data.payload);}
14 | };
15 | return handler;
16 | };
17 |
18 | var checkThirdParty = function(callback) {
19 | var handler = getReceiveCookiesMessage(callback);
20 | fp.handlers.attach('cookies', handler);
21 |
22 | fp.comm.openChannel();
23 | };
24 |
25 | return {
26 | checkThirdParty: checkThirdParty
27 | };
28 | });
29 |
--------------------------------------------------------------------------------
/src/communication/handlers.js:
--------------------------------------------------------------------------------
1 | //handlers.js
2 | 'use strict';
3 |
4 | filepicker.extend('handlers', function(){
5 | var fp = this;
6 | var storage = {};
7 |
8 | var attachHandler = function(id, handler){
9 | if (storage.hasOwnProperty(id)){
10 | storage[id].push(handler);
11 | } else {
12 | storage[id] = [handler];
13 | }
14 | return handler;
15 | };
16 |
17 | var detachHandler = function(id, fn){
18 | var handlers = storage[id];
19 | if (!handlers) {
20 | return;
21 | }
22 |
23 | if (fn) {
24 | for (var i = 0; i < handlers.length; i++) {
25 | if (handlers[i] === fn) {
26 | handlers.splice(i,1);
27 | break;
28 | }
29 | }
30 | if (handlers.length === 0) {
31 | delete(storage[id]);
32 | }
33 | } else {
34 | delete(storage[id]);
35 | }
36 | };
37 |
38 | var run = function(data){
39 | if (data == null || data.id == null) {
40 | return false;
41 | }
42 | var callerId = data.id;
43 | if (storage.hasOwnProperty(callerId)){
44 | //have to grab first in case someone removes mid-go
45 | var handlers = storage[callerId];
46 | for (var i = 0; i < handlers.length; i++) {
47 | handlers[i](data);
48 | }
49 | return true;
50 | }
51 | return false;
52 | };
53 |
54 | return {
55 | attach: attachHandler,
56 | detach: detachHandler,
57 | run: run
58 | };
59 | });
60 |
--------------------------------------------------------------------------------
/src/dialog/exporter.js:
--------------------------------------------------------------------------------
1 | //exporter.js
2 | 'use strict';
3 |
4 | filepicker.extend('exporter', function(){
5 | var fp = this;
6 |
7 | var normalizeOptions = function(options) {
8 | var normalize = function(singular, plural, def){
9 | if (options[plural] && !fp.util.isArray(options[plural])) {
10 | options[plural] = [options[plural]];
11 | } else if (options[singular]) {
12 | options[plural] = [options[singular]];
13 | } else if (def) {
14 | options[plural] = def;
15 | }
16 | };
17 |
18 | if (options.mimetype && options.extension) {
19 | throw fp.FilepickerException('Error: Cannot pass in both mimetype and extension parameters to the export function');
20 | }
21 | normalize('service', 'services');
22 | if (options.services) {
23 | for (var i = 0; i < options.services.length; i++) {
24 | var service = (''+options.services[i]).replace(' ','');
25 | var sid = fp.services[service];
26 | options.services[i] = (sid === undefined ? service : sid);
27 | }
28 | }
29 | if (options.openTo) {
30 | options.openTo = fp.services[options.openTo] || options.openTo;
31 | }
32 |
33 | fp.util.setDefault(options, 'container', fp.browser.openInModal() ? 'modal' : 'window');
34 | };
35 |
36 | var getExportHandler = function(onSuccess, onError) {
37 | var handler = function(data) {
38 | if (data.type !== 'filepickerUrl'){
39 | return;
40 | }
41 |
42 | if (data.error) {
43 | fp.util.console.error(data.error);
44 | onError(fp.errors.FPError(132));
45 | } else {
46 | var fpfile = {};
47 | //TODO: change payload to not require parsing
48 | fpfile.url = data.payload.url;
49 | fpfile.filename = data.payload.data.filename;
50 | fpfile.mimetype = data.payload.data.type;
51 | fpfile.size = data.payload.data.size;
52 | fpfile.client = data.payload.data.client;
53 | //TODO: get writeable
54 | fpfile.isWriteable = true;
55 | onSuccess(fpfile);
56 | }
57 |
58 | //Try to close a modal if it exists.
59 | fp.modal.close();
60 | };
61 | return handler;
62 | };
63 |
64 | var createExporter = function(input, options, onSuccess, onError) {
65 | normalizeOptions(options);
66 |
67 | var api = {
68 | close: function () {
69 | fp.modal.close();
70 | }
71 | };
72 |
73 | if (options.debug) {
74 | //return immediately, but still async
75 | setTimeout(function(){
76 | onSuccess({
77 | id:1,
78 | url: 'https://www.filepicker.io/api/file/-nBq2onTSemLBxlcBWn1',
79 | filename: 'test.png',
80 | mimetype: 'image/png',
81 | size:58979,
82 | client: 'computer'
83 | });
84 | }, 1);
85 | return api;
86 | }
87 |
88 | if (fp.cookies.THIRD_PARTY_COOKIES === undefined) {
89 | //if you want a modal, then we need to wait until we know if 3rd party cookies allowed.
90 | var alreadyHandled = false;
91 | fp.cookies.checkThirdParty(function(){
92 | if (!alreadyHandled) {
93 | createExporter(input, options, onSuccess, onError);
94 | alreadyHandled = true;
95 | }
96 | });
97 | return api;
98 | }
99 |
100 | var id = fp.util.getId();
101 |
102 | //Wrapper around on success to make sure we don't also fire on close
103 | var finished = false;
104 | var onSuccessMark = function(fpfile){
105 | finished = true;
106 | onSuccess(fpfile);
107 | };
108 | var onErrorMark = function(fperror){
109 | finished = true;
110 | onError(fperror);
111 | };
112 |
113 | var onClose = function(){
114 | if (!finished) {
115 | finished = true;
116 | onError(fp.errors.FPError(131));
117 | }
118 | };
119 |
120 | fp.window.open(options.container, fp.urls.constructExportUrl(input, options, id), onClose);
121 | fp.handlers.attach(id, getExportHandler(onSuccessMark, onErrorMark));
122 |
123 | return api;
124 | };
125 |
126 | return {
127 | createExporter: createExporter
128 | };
129 | });
130 |
--------------------------------------------------------------------------------
/src/dialog/modal.js:
--------------------------------------------------------------------------------
1 | //modal.js
2 | 'use strict';
3 |
4 | filepicker.extend('modal', function(){
5 | var fp = this,
6 | SHADE_NAME = 'filepicker_shade',
7 | WINDOW_CONTAINER_NAME = 'filepicker_dialog_container';
8 |
9 |
10 | var originalBody = getHtmlTag();
11 | if (originalBody) {
12 | var originalOverflow = originalBody.style.overflow;
13 | }
14 |
15 |
16 | /*
17 | * Make the code for the modal
18 | */
19 | var generateModal = function(modalUrl, onClose){
20 | appendStyle();
21 | var shade = createModalShade(onClose),
22 | container = createModalContainer(),
23 | close = createModalClose(onClose),
24 | modal = document.createElement('iframe');
25 |
26 | modal.name = fp.window.WINDOW_NAME;
27 | modal.id = fp.window.WINDOW_NAME;
28 | modal.style.width = '100%';
29 | modal.style.height = '100%';
30 | modal.style.border = 'none';
31 | modal.style.position = 'relative';
32 |
33 | //IE...
34 | modal.setAttribute('border',0);
35 | modal.setAttribute('frameborder',0);
36 | modal.setAttribute('frameBorder',0);
37 | modal.setAttribute('marginwidth',0);
38 | modal.setAttribute('marginheight',0);
39 | modal.setAttribute('allow', 'microphone; camera');
40 |
41 | modal.src = modalUrl;
42 | container.appendChild(modal);
43 |
44 | shade.appendChild(close);
45 | shade.appendChild(container);
46 | document.body.appendChild(shade);
47 | // So user can't scrol page under modal
48 | var body = getHtmlTag();
49 | if (body) {
50 | body.style.overflow = 'hidden';
51 | }
52 | return modal;
53 | };
54 |
55 | var createModalShade = function(onClose) {
56 | var shade = document.createElement('div');
57 | shade.id = SHADE_NAME;
58 | shade.className = 'fp__overlay';
59 | shade.onclick = getCloseModal(onClose);
60 | return shade;
61 | };
62 |
63 | var createModalContainer = function() {
64 | var modalcontainer = document.createElement('div');
65 | modalcontainer.id = WINDOW_CONTAINER_NAME;
66 | modalcontainer.className = 'fp__container';
67 | // modalcontainer.style.background = '#ecedec url("https://www.filepicker.io/static/img/spinner.gif") no-repeat 50% 50%';
68 | return modalcontainer;
69 | };
70 |
71 | var createModalClose = function(onClose) {
72 | var close = document.createElement('div');
73 | close.className = 'fp__close';
74 | var closeAnchor = document.createElement('a');
75 | closeAnchor.appendChild(document.createTextNode('X'));
76 | close.appendChild(closeAnchor);
77 | closeAnchor.onclick = getCloseModal(onClose);
78 | // Close modal on esc
79 | document.onkeydown = function(evt) {
80 | evt = evt || window.event;
81 | if (evt.keyCode === 27) {
82 | getCloseModal(onClose)();
83 | }
84 | };
85 | return close;
86 | };
87 |
88 | var getCloseModal = function(onClose, force){
89 | force = !!force;
90 | return function(){
91 | if (fp.uploading && !force) {
92 | if (!window.confirm('You are currently uploading. If you choose "OK", the window will close and your upload will not finish. Do you want to stop uploading and close the window?')) {
93 | return;
94 | }
95 | }
96 | fp.uploading = false;
97 | document.onkeydown = null;
98 |
99 | setOriginalOverflow();
100 |
101 | var shade = document.getElementById(SHADE_NAME);
102 | if (shade) {
103 | document.body.removeChild(shade);
104 | }
105 | var container = document.getElementById(WINDOW_CONTAINER_NAME);
106 | if (container) {
107 | document.body.removeChild(container);
108 | }
109 | try{delete window.frames[fp.window.WINDOW_NAME];}catch(e){}
110 | if (onClose){onClose();}
111 | };
112 | };
113 |
114 | function hide() {
115 | var shade = document.getElementById(SHADE_NAME);
116 | if (shade) {
117 | shade.hidden = true;
118 | }
119 | var container = document.getElementById(WINDOW_CONTAINER_NAME);
120 | if (container) {
121 | container.hidden = true;
122 | }
123 | setOriginalOverflow();
124 | }
125 |
126 | function setOriginalOverflow(){
127 | var body = getHtmlTag();
128 | if (body) {
129 | body.style.overflow = originalOverflow;
130 | }
131 | }
132 |
133 | function appendStyle(){
134 | var css = '.fp__overlay {top: 0;right: 0;bottom: 0;left: 0;z-index: 1000;background: rgba(0, 0, 0, 0.8);}' +
135 |
136 | '.fp__close {top: 104px; right: 108px; width: 35px; height: 35px; z-index: 20; cursor: pointer}' +
137 | '@media screen and (max-width: 768px), screen and (max-height: 500px) {.fp__close {top: 15px; right: 12px;}}' +
138 | '.fp__close a {text-indent: -9999px; overflow: hidden; display: block; width: 100%; height: 100%; background: url(https://d1zyh3sbxittvg.cloudfront.net/close.png) 50% 50% no-repeat;}' +
139 | '.fp__close a:hover {background-color: rgba(0,0,0, .02); opacity: .8;}' +
140 | '@media screen and (max-width: 768px), screen and (max-height: 500px) {top: 14px; right: 14px;}' +
141 | '.fp__copy {display: none;}' +
142 |
143 | '.fp__container {-webkit-overflow-scrolling: touch; overflow: hidden; min-height: 300px; top: 100px;right: 100px;bottom: 100px;left: 100px;background: #eee; box-sizing:content-box; -webkit-box-sizing:content-box; -moz-box-sizing:content-box;}' +
144 |
145 | '@media screen and (max-width: 768px), screen and (max-height: 500px) {.fp__copy {bottom: 0; left: 0; right: 0; height: 20px; background: #333;}}' +
146 | '@media screen and (max-width: 768px), screen and (max-height: 500px) {.fp__copy a {margin-left: 5px;}}' +
147 | '@media screen and (max-width: 768px), screen and (max-height: 500px) {.fp__container {top: 0;right: 0;bottom: 0;left: 0;}}' +
148 |
149 | '.fp__overlay, .fp__close, .fp__copy, .fp__container {position: fixed;}';
150 |
151 | var head = document.head || document.getElementsByTagName('head')[0],
152 | style = document.createElement('style');
153 |
154 | style.type = 'text/css';
155 | if (style.styleSheet){
156 | style.styleSheet.cssText = css;
157 | } else {
158 | style.appendChild(document.createTextNode(css));
159 | }
160 |
161 | head.appendChild(style);
162 | }
163 |
164 | function getHtmlTag(){
165 | try {
166 | return document.getElementsByTagName('html')[0];
167 | } catch(err) {
168 | return null;
169 | }
170 | }
171 |
172 | var closeModal = getCloseModal(function(){});
173 |
174 | return {
175 | generate: generateModal,
176 | close: closeModal,
177 | hide: hide
178 | };
179 | });
180 |
--------------------------------------------------------------------------------
/src/dialog/picker.js:
--------------------------------------------------------------------------------
1 | //picker.js
2 | 'use strict';
3 |
4 | filepicker.extend('picker', function(){
5 | var fp = this;
6 |
7 | var normalizeOptions = function(options) {
8 | var normalize = function(singular, plural, def){
9 | if (options[plural]) {
10 | if (!fp.util.isArray(options[plural])) {
11 | options[plural] = [options[plural]];
12 | }
13 | } else if (options[singular]) {
14 | options[plural] = [options[singular]];
15 | } else if (def) {
16 | options[plural] = def;
17 | }
18 | };
19 |
20 | normalize('service', 'services');
21 | normalize('mimetype', 'mimetypes');
22 | normalize('extension', 'extensions');
23 |
24 | if (options.services) {
25 | for (var i = 0; i < options.services.length; i++) {
26 | var service = (''+options.services[i]).replace(' ','');
27 |
28 | if (fp.services[service] !== undefined) {//we use 0, so can't use !
29 | service = fp.services[service];
30 | }
31 |
32 | options.services[i] = service;
33 | }
34 | }
35 |
36 | if (options.mimetypes && options.extensions) {
37 | throw fp.FilepickerException('Error: Cannot pass in both mimetype and extension parameters to the pick function');
38 | }
39 | if (!options.mimetypes && !options.extensions){
40 | options.mimetypes = ['*/*'];
41 | }
42 |
43 | if (options.openTo) {
44 | options.openTo = fp.services[options.openTo] || options.openTo;
45 | }
46 | fp.util.setDefault(options, 'container', fp.browser.openInModal() ? 'modal' : 'window');
47 | };
48 |
49 | var getPickHandler = function(onSuccess, onError, onProgress) {
50 | var handler = function(data) {
51 | if (filterDataType(data, onProgress)) {
52 | return;
53 | }
54 |
55 | fp.uploading = false;
56 |
57 | if (data.error) {
58 | fp.util.console.error(data.error);
59 | if (data.error.code) {
60 | onError(fp.errors.FPError(data.error.code));
61 | } else {
62 | onError(fp.errors.FPError(102));
63 | //Try to close a modal if it exists.
64 | fp.modal.close();
65 | }
66 | } else {
67 | var fpfile = fpfileFromPayload(data.payload);
68 | //TODO: change payload to not require parsing
69 | onSuccess(fpfile);
70 | //Try to close a modal if it exists.
71 | fp.modal.close();
72 | }
73 | };
74 | return handler;
75 | };
76 |
77 | var getPickFolderHandler = function(onSuccess, onError, onProgress) {
78 | var handler = function(data) {
79 | if (filterDataType(data, onProgress)) {
80 | return;
81 | }
82 | fp.uploading = false;
83 |
84 | if (data.error) {
85 | fp.util.console.error(data.error);
86 | onError(fp.errors.FPError(102));
87 | } else {
88 | data.payload.data.url = data.payload.url;
89 | onSuccess(data.payload.data);
90 | }
91 |
92 | //Try to close a modal if it exists.
93 | fp.modal.close();
94 | };
95 | return handler;
96 | };
97 |
98 | var getUploadingHandler = function(onUploading) {
99 | onUploading = onUploading || function(){};
100 | var handler = function(data) {
101 | if (data.type !== 'uploading') {
102 | return;
103 | }
104 | fp.uploading = !!data.payload;
105 | onUploading(fp.uploading);
106 | };
107 | return handler;
108 | };
109 |
110 | var addIfExist = function(data, fpfile, key) {
111 | if (data[key]) {
112 | fpfile[key] = data[key];
113 | }
114 | };
115 |
116 | var fpfileFromPayload = function(payload) {
117 | var fpfile = {};
118 | var url = payload.url;
119 | if (url && url.url) {
120 | url = url.url;
121 | }
122 | fpfile.url = url;
123 | var data = payload.url.data || payload.data;
124 | fpfile.filename = data.filename;
125 | fpfile.mimetype = data.type;
126 | fpfile.size = data.size;
127 | if (data.cropped !== undefined) {
128 | fpfile.cropped = data.cropped;
129 | }
130 | if (data.rotated !== undefined) {
131 | fpfile.rotated = data.rotated;
132 | }
133 | if (data.converted !== undefined) {
134 | fpfile.converted = data.converted;
135 | }
136 |
137 | addIfExist(data, fpfile, 'id');
138 | addIfExist(data, fpfile, 'key');
139 | addIfExist(data, fpfile, 'container');
140 | addIfExist(data, fpfile, 'path');
141 | addIfExist(data, fpfile, 'client');
142 | addIfExist(data, fpfile, 'cloudinary_resource_type');
143 |
144 | //TODO: get writeable
145 | fpfile.isWriteable = true;
146 |
147 | return fpfile;
148 | };
149 |
150 | var getPickMultipleHandler = function(onSuccess, onError, onProgress) {
151 | var handler = function(data) {
152 | if (filterDataType(data, onProgress)) {
153 | return;
154 | }
155 | fp.uploading = false;
156 |
157 | if (data.error) {
158 | fp.util.console.error(data.error);
159 | onError(fp.errors.FPError(102));
160 | } else {
161 | var fpfiles = [];
162 |
163 |
164 | //TODO: change payload to not require parsing
165 | if (!fp.util.isArray(data.payload)) {
166 | data.payload = [data.payload];
167 | }
168 | for (var i = 0; i < data.payload.length; i++) {
169 | var fpfile = fpfileFromPayload(data.payload[i]);
170 | fpfiles.push(fpfile);
171 | }
172 | onSuccess(fpfiles);
173 | }
174 |
175 | //Try to close a modal if it exists.
176 | fp.modal.close();
177 | };
178 | return handler;
179 | };
180 |
181 | var createPicker = function(options, onSuccess, onError, multiple, folder, onProgress, convertFile) {
182 | normalizeOptions(options);
183 |
184 | var api = {
185 | close: function () {
186 | fp.modal.close();
187 | }
188 | };
189 |
190 | if (options.debug) {
191 |
192 | var dumy_data = {
193 | id:1,
194 | url: 'https://www.filepicker.io/api/file/-nBq2onTSemLBxlcBWn1',
195 | filename:'test.png',
196 | mimetype: 'image/png',
197 | size:58979,
198 | client:'computer'
199 | };
200 |
201 | var dumy_callback;
202 |
203 | if (multiple || options.storeLocation) {
204 | dumy_callback = [dumy_data, dumy_data, dumy_data];
205 | } else {
206 | dumy_callback = dumy_data;
207 | }
208 |
209 | //return immediately, but still async
210 | setTimeout(function(){
211 | onSuccess(dumy_callback);
212 | }, 1);
213 | return api;
214 | }
215 |
216 | if (fp.cookies.THIRD_PARTY_COOKIES === undefined) {
217 | //if you want a modal, then we need to wait until we know if 3rd party cookies allowed.
218 | var alreadyHandled = false;
219 | fp.cookies.checkThirdParty(function(){
220 | if (!alreadyHandled) {
221 | createPicker(options, onSuccess, onError, !!multiple, folder, onProgress);
222 | alreadyHandled = true;
223 | }
224 | });
225 | return api;
226 | }
227 |
228 | var id = fp.util.getId();
229 |
230 | //Wrapper around on success to make sure we don't also fire on close
231 | var finished = false;
232 | var onSuccessMark = function(fpfile){
233 | if (options.container === 'window') {
234 | window.onbeforeunload = null;
235 | }
236 | finished = true;
237 | onSuccess(fpfile);
238 | };
239 | var onErrorMark = function(fperror){
240 | finished = true;
241 | onError(fperror);
242 | };
243 |
244 | var onClose = function(){
245 | if (!finished) {
246 | finished = true;
247 | onError(fp.errors.FPError(101));
248 | }
249 | };
250 |
251 | var url;
252 | var handler;
253 | if (convertFile) {
254 | url = fp.urls.constructConvertUrl(options, id);
255 | handler = getPickHandler(onSuccessMark, onErrorMark, onProgress);
256 | } else if (multiple) {
257 | url = fp.urls.constructPickUrl(options, id, true);
258 | handler = getPickMultipleHandler(onSuccessMark, onErrorMark, onProgress);
259 | } else if (folder) {
260 | url = fp.urls.constructPickFolderUrl(options, id);
261 | handler = getPickFolderHandler(onSuccessMark, onErrorMark, onProgress);
262 | } else {
263 | url = fp.urls.constructPickUrl(options, id, false);
264 | handler = getPickHandler(onSuccessMark, onErrorMark, onProgress);
265 | }
266 |
267 | fp.window.open(options.container, url, onClose);
268 | fp.handlers.attach(id, handler);
269 |
270 | var key = id+'-upload';
271 | fp.handlers.attach(key, getUploadingHandler(function(){
272 | fp.handlers.detach(key);
273 | }));
274 |
275 | return api;
276 | };
277 |
278 | function filterDataType(data, onProgress){
279 | if (data.type === 'filepickerProgress'){
280 | fp.uploading = true;
281 | if (onProgress) {
282 | onProgress(data.payload.data);
283 | }
284 | } else if (data.type === 'notUploading') {
285 | fp.uploading = false;
286 | } else if (data.type === 'closeModal') {
287 | fp.modal.close();
288 | } else if (data.type === 'hideModal') {
289 | fp.modal.hide();
290 | } else if (data.type === 'filepickerUrl' || data.type === 'serverHttpError') {
291 | return false;
292 | }
293 | return true;
294 | }
295 |
296 | return {
297 | createPicker: createPicker
298 | };
299 | });
300 |
--------------------------------------------------------------------------------
/src/dialog/window.js:
--------------------------------------------------------------------------------
1 | //windows.js
2 | 'use strict';
3 |
4 | filepicker.extend('window', function(){
5 | var fp = this;
6 |
7 | var DIALOG_TYPES = {
8 | OPEN:'/dialog/open/',
9 | SAVEAS:'/dialog/save/'
10 | };
11 |
12 | var WINDOW_NAME = 'filepicker_dialog';
13 | var WINDOW_PROPERTIES = 'left=100,top=100,height=600,width=800,menubar=no,toolbar=no,location=no,personalbar=no,status=no,resizable=yes,scrollbars=yes,dependent=yes,dialog=yes';
14 | var CLOSE_CHECK_INTERVAL = 100;
15 |
16 | var openWindow = function(container, src, onClose) {
17 | onClose = onClose || function(){};
18 | if (!container && fp.browser.openInModal()){
19 | container = 'modal';
20 | } else if (!container) {
21 | container = 'window';
22 | }
23 |
24 | if (container === 'window') {
25 | var name = WINDOW_NAME + fp.util.getId();
26 | window.onbeforeunload = function confirmExit() {
27 | return 'Filepicker upload does not complete.';
28 | };
29 | var win = window.open(src, name, WINDOW_PROPERTIES);
30 | if (!win) {
31 | window.onbeforeunload = null;
32 | window.alert('Please disable your popup blocker to upload files.');
33 | }
34 |
35 | var closeCheck = window.setInterval(function(){
36 | if (!win || win.closed) {
37 | window.onbeforeunload = null;
38 | window.clearInterval(closeCheck);
39 | onClose();
40 | }
41 | }, CLOSE_CHECK_INTERVAL);
42 | } else if (container === 'modal') {
43 | fp.modal.generate(src, onClose);
44 | } else {
45 | var container_iframe = document.getElementById(container);
46 | if (!container_iframe) {
47 | throw new fp.FilepickerException('Container "'+container+'" not found. This should either be set to "window","modal", or the ID of an iframe that is currently in the document.');
48 | }
49 | container_iframe.src = src;
50 | }
51 | };
52 |
53 | return {
54 | open: openWindow,
55 | WINDOW_NAME: WINDOW_NAME
56 | };
57 | });
58 |
--------------------------------------------------------------------------------
/src/finalize.js:
--------------------------------------------------------------------------------
1 | // finalize.js
2 | 'use strict';
3 |
4 | (function(){
5 | //setup functions
6 | filepicker.internal(function(){
7 | var fp = this;
8 | fp.util.addOnLoad(fp.cookies.checkThirdParty);
9 | fp.util.addOnLoad(fp.widgets.buildWidgets);
10 | fp.util.addOnLoad(fp.responsiveImages.activate);
11 | });
12 |
13 | //Now we wipe our superpowers
14 | delete filepicker.internal;
15 | delete filepicker.extend;
16 |
17 | //process the queue
18 | var queue = filepicker._queue || [];
19 | var args;
20 | var len = queue.length;
21 | if (len) {
22 | for (var i = 0; i < len; i++) {
23 | args = queue[i];
24 | filepicker[args[0]].apply(filepicker, args[1]);
25 | }
26 | }
27 |
28 | //remove the queue
29 | if (filepicker._queue) {
30 | delete filepicker._queue;
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/src/library/conversions.js:
--------------------------------------------------------------------------------
1 | //conversions.js
2 | 'use strict';
3 |
4 | filepicker.extend('conversions', function(){
5 | var fp = this;
6 |
7 | var valid_parameters = {
8 | align:'string',
9 | blurAmount:'number',
10 | crop:'string or array',
11 | crop_first:'boolean',
12 | compress:'boolean',
13 | exif:'string or boolean',
14 | filter:'string',
15 | fit:'string',
16 | format:'string',
17 | height:'number',
18 | policy:'string',
19 | quality:'number',
20 | page:'number',
21 | rotate:'string or number',
22 | secure:'boolean',
23 | sharpenAmount:'number',
24 | signature:'string',
25 | storeAccess:'string',
26 | storeContainer:'string',
27 | storeRegion:'string',
28 | storeLocation:'string',
29 | storePath:'string',
30 | text:'string',
31 | cloudinaryUploadPreset: 'string',
32 | text_align:'string',
33 | text_color:'string',
34 | text_font:'string',
35 | text_padding:'number',
36 | text_size:'number',
37 | watermark:'string',
38 | watermark_position:'string',
39 | watermark_size:'number',
40 | width:'number'
41 | };
42 |
43 | // Only for params that differ
44 | var rest_map = {
45 | w: 'width',
46 | h: 'height'
47 | };
48 |
49 | var mapRestParams = function(options){
50 | var obj = {};
51 | for (var key in options) {
52 | obj[rest_map[key] || key] = options[key];
53 | // need to convert string to number
54 | if (valid_parameters[rest_map[key] || key] === 'number') {
55 | obj[rest_map[key] || key] = Number(options[key]);
56 | }
57 | }
58 | return obj;
59 | };
60 |
61 | var checkParameters = function(options) {
62 | var found;
63 | for (var key in options) {
64 | found = false;
65 | for (var test in valid_parameters) {
66 | if (key === test) {
67 | found = true;
68 | if (valid_parameters[test].indexOf(fp.util.typeOf(options[key])) === -1) {
69 | throw new fp.FilepickerException('Conversion parameter '+key+' is not the right type: '+options[key]+'. Should be a '+valid_parameters[test]);
70 | }
71 | }
72 | }
73 | if (!found) {
74 | throw new fp.FilepickerException('Conversion parameter '+key+' is not a valid parameter.');
75 | }
76 | }
77 | };
78 |
79 | var convert = function(fp_url, options, onSuccess, onError, onProgress){
80 | checkParameters(options);
81 |
82 | if (options.crop && fp.util.isArray(options.crop)) {
83 | options.crop = options.crop.join(',');
84 | }
85 | fp.ajax.post(fp_url+'/convert', {
86 | data: options,
87 | json: true,
88 | success: function(fpfile) {
89 | onSuccess(fp.util.standardizeFPFile(fpfile));
90 | },
91 | error: function(msg, status, xhr) {
92 | if (msg === 'not_found') {
93 | onError(new fp.errors.FPError(141));
94 | } else if (msg === 'bad_params') {
95 | onError(new fp.errors.FPError(142));
96 | } else if (msg === 'not_authorized') {
97 | onError(new fp.errors.FPError(403));
98 | } else {
99 | onError(new fp.errors.FPError(143));
100 | }
101 | },
102 | progress: onProgress
103 | });
104 | };
105 |
106 | return {
107 | convert: convert,
108 | mapRestParams: mapRestParams
109 | };
110 | });
111 |
--------------------------------------------------------------------------------
/src/library/errors.js:
--------------------------------------------------------------------------------
1 | //errors.js
2 | 'use strict';
3 |
4 | filepicker.extend('errors', function(){
5 | var fp = this;
6 |
7 | var FPError = function(code) {
8 | if (this === window) { return new FPError(code);}
9 |
10 | this.code = code;
11 | if (filepicker.debug) {
12 | var info = filepicker.error_map[this.code];
13 | this.message = info.message;
14 | this.moreInfo = info.moreInfo;
15 | this.toString = function(){
16 | return 'FPError '+this.code+': '+this.message+'. For help, see '+this.moreInfo;
17 | };
18 | } else {
19 | this.toString = function(){return 'FPError '+this.code+'. Include filepicker_debug.js for more info';};
20 | }
21 | return this;
22 | };
23 | //Telling router how to call us
24 | FPError.isClass = true;
25 |
26 | //The defualt error handler
27 | var handleError = function(fperror) {
28 | if (filepicker.debug) {
29 | fp.util.console.error(fperror.toString());
30 | }
31 | };
32 |
33 | return {
34 | FPError: FPError,
35 | handleError: handleError
36 | };
37 | }, true);
38 |
--------------------------------------------------------------------------------
/src/library/mimetypes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | filepicker.extend('mimetypes', function(){
4 | var fp = this;
5 |
6 | /*We have a mimetype map to make up for the fact that browsers
7 | * don't yet recognize all the mimetypes we need to support*/
8 | var mimetype_extension_map = {
9 | '.stl':'application/sla',
10 | '.hbs':'text/html',
11 | '.pdf':'application/pdf',
12 | '.jpg':'image/jpeg',
13 | '.jpeg':'image/jpeg',
14 | '.jpe':'image/jpeg',
15 | '.imp':'application/x-impressionist',
16 | '.vob': 'video/dvd'
17 | };
18 |
19 | var mimetype_bad_array = [ 'application/octet-stream',
20 | 'application/download',
21 | 'application/force-download',
22 | 'octet/stream',
23 | 'application/unknown',
24 | 'application/x-download',
25 | 'application/x-msdownload',
26 | 'application/x-secure-download'];
27 |
28 | var getMimetype = function(file) {
29 | if (file.type) {
30 | var type = file.type;
31 | type = type.toLowerCase();
32 | var bad_type = false;
33 | for (var n = 0; n < mimetype_bad_array.length; n++){
34 | bad_type = bad_type || type === mimetype_bad_array[n];
35 | }
36 | if (!bad_type){
37 | return file.type;
38 | }
39 | }
40 | var filename = file.name || file.fileName;
41 | var extension = filename.match(/\.\w*$/);
42 | if (extension) {
43 | return mimetype_extension_map[extension[0].toLowerCase()] || '';
44 | } else {
45 | if (file.type){
46 | //Might be a bad type, but better then nothing
47 | return file.type;
48 | } else {
49 | return '';
50 | }
51 | }
52 | };
53 |
54 | var matchesMimetype = function(test, against) {
55 | if (!test) {return against === '*/*';}
56 |
57 | test = fp.util.trim(test).toLowerCase();
58 | against = fp.util.trim(against).toLowerCase();
59 |
60 | // Firefox has some oddities as it allows the user to overwrite mimetypes.
61 | // These are some of the silly mimetypes that have no meaning at all
62 | for (var n = 0; n < mimetype_bad_array.length; n++){
63 | if (test === mimetype_bad_array[n]){
64 | return true;
65 | }
66 | }
67 |
68 | var test_parts = test.split('/'),
69 | against_parts = against.split('/');
70 |
71 | //comparing types
72 | if (against_parts[0] === '*') {return true;}
73 | if (against_parts[0] !== test_parts[0]) {return false;}
74 | //comparing subtypes
75 | if (against_parts[1] === '*') {return true;}
76 | return against_parts[1] === test_parts[1];
77 | };
78 |
79 | return {
80 | getMimetype: getMimetype,
81 | matchesMimetype: matchesMimetype
82 | };
83 | });
84 |
--------------------------------------------------------------------------------
/src/library/services.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | filepicker.extend('services', function(){
4 | /**
5 | * @ServiceEnum: the services we support
6 | * Don't use 0 as it might be confused with false
7 | */
8 | return {
9 | COMPUTER: 1,
10 | DROPBOX: 2,
11 | FACEBOOK: 3,
12 | GITHUB: 4,
13 | GMAIL: 5,
14 | IMAGE_SEARCH: 6,
15 | URL: 7,
16 | WEBCAM: 8,
17 | GOOGLE_DRIVE: 9,
18 | SEND_EMAIL: 10,
19 | INSTAGRAM: 11,
20 | FLICKR: 12,
21 | VIDEO: 13,
22 | EVERNOTE: 14,
23 | PICASA: 15,
24 | WEBDAV: 16,
25 | FTP: 17,
26 | ALFRESCO: 18,
27 | BOX: 19,
28 | SKYDRIVE: 20,
29 | GDRIVE: 21,
30 | CUSTOMSOURCE: 22,
31 | CLOUDDRIVE: 23,
32 | GENERIC: 24,
33 | CONVERT: 25,
34 | AUDIO: 26
35 | };
36 | }, true);
37 |
--------------------------------------------------------------------------------
/src/library/urls.js:
--------------------------------------------------------------------------------
1 | //urls.js
2 | 'use strict';
3 |
4 | filepicker.extend('urls', function(){
5 | var fp = this;
6 |
7 | var base = 'https://www.filepicker.io';
8 | if (window.filepicker.hostname) {
9 | base = window.filepicker.hostname;
10 | }
11 | var dialog_base = base.replace('www', 'dialog'),
12 | pick_url = dialog_base + '/dialog/open/',
13 | export_url = dialog_base + '/dialog/save/',
14 | convert_url = dialog_base + '/dialog/process/',
15 | pick_folder_url = dialog_base + '/dialog/folder/',
16 | store_url = base + '/api/store/';
17 |
18 | var allowedConversions = ['crop', 'rotate', 'filter'];
19 |
20 | var constructPickUrl = function(options, id, multiple) {
21 | return pick_url+ constructModalQuery(options, id)+
22 | (multiple ? '&multi='+!!multiple : '')+
23 | (options.mimetypes !== undefined ? '&m='+options.mimetypes.join(',') : '')+
24 | (options.extensions !== undefined ? '&ext='+options.extensions.join(',') : '')+
25 | (options.maxSize ? '&maxSize='+options.maxSize: '')+
26 | (options.customSourceContainer ? '&customSourceContainer='+options.customSourceContainer: '')+
27 | (options.customSourcePath ? '&customSourcePath='+options.customSourcePath: '')+
28 | (options.maxFiles ? '&maxFiles='+options.maxFiles: '')+
29 | (options.folders !== undefined ? '&folders='+options.folders : '')+
30 | (options.storeLocation ? '&storeLocation='+options.storeLocation : '')+
31 | (options.storePath ? '&storePath='+options.storePath : '')+
32 | (options.storeContainer ? '&storeContainer='+options.storeContainer : '')+
33 | (options.storeRegion ? '&storeRegion='+options.storeRegion : '')+
34 | (options.cloudinaryUploadPreset ? '&cloudinaryUploadPreset='+options.cloudinaryUploadPreset: '')+
35 | (options.storeAccess ? '&storeAccess='+options.storeAccess : '')+
36 | ((options.webcam && options.webcam.webcamDim) ? '&wdim='+options.webcam.webcamDim.join(',') : '')+
37 | (options.webcamDim ? '&wdim='+options.webcamDim.join(',') : '')+
38 | ((options.webcam && options.webcam.videoRes) ? '&videoRes='+options.webcam.videoRes: '')+
39 | ((options.webcam && options.webcam.videoLen) ? '&videoLen='+options.webcam.videoLen: '')+
40 | ((options.webcam && options.webcam.audioLen) ? '&audioLen='+options.webcam.audioLen: '')+
41 | constructConversionsQuery(options.conversions);
42 | };
43 |
44 | var constructConvertUrl = function(options, id) {
45 | var url = options.convertUrl;
46 | if (url.indexOf('&') >= 0 || url.indexOf('?') >= 0) {
47 | url = encodeURIComponent(url);
48 | }
49 | return convert_url+ constructModalQuery(options, id)+
50 | '&curl='+url+
51 | constructConversionsQuery(options.conversions);
52 | };
53 |
54 |
55 |
56 | var constructPickFolderUrl = function(options, id) {
57 | return pick_folder_url + constructModalQuery(options, id);
58 | };
59 |
60 | var constructExportUrl = function(url, options, id) {
61 | if (url.indexOf('&') >= 0 || url.indexOf('?') >= 0) {
62 | url = encodeURIComponent(url);
63 | }
64 | return export_url+ constructModalQuery(options, id)+
65 | '&url='+url+
66 | (options.mimetype !== undefined ? '&m='+options.mimetype : '')+
67 | (options.extension !== undefined ? '&ext='+options.extension : '')+
68 | (options.suggestedFilename ? '&defaultSaveasName='+options.suggestedFilename : '');
69 | };
70 |
71 | var constructStoreUrl = function(options) {
72 | return store_url + options.location +
73 | '?key='+fp.apikey+
74 | (options.base64decode ? '&base64decode=true' : '')+
75 | (options.mimetype ? '&mimetype='+options.mimetype : '')+
76 | (options.filename ? '&filename='+encodeURIComponent(options.filename) : '')+
77 | (options.path ? '&path='+options.path : '')+
78 | (options.container ? '&container='+options.container : '')+
79 | (options.access ? '&access='+options.access : '')+
80 | constructSecurityQuery(options)+
81 | '&plugin='+getPlugin();
82 | };
83 |
84 | var constructWriteUrl = function(fp_url, options) {
85 | //to make sure that fp_url already has a ?
86 | return fp_url +
87 | '?nonce=fp'+
88 | (!!options.base64decode ? '&base64decode=true' : '')+
89 | (options.mimetype ? '&mimetype='+options.mimetype : '')+
90 | constructSecurityQuery(options)+
91 | '&plugin='+getPlugin();
92 | };
93 |
94 | var constructHostCommFallback = function(){
95 | var parts = fp.util.parseUrl(window.location.href);
96 | return parts.origin+'/404';
97 | };
98 |
99 | function constructModalQuery(options, id) {
100 | return '?key='+fp.apikey+
101 | '&id='+id+
102 | '&referrer='+window.location.hostname+
103 | '&iframe='+(options.container !== 'window')+
104 | '&version='+fp.API_VERSION+
105 | (options.services ? '&s='+options.services.join(',') : '')+
106 | (options.container !== undefined ? '&container='+ options.container : 'modal')+
107 | (options.openTo ? '&loc='+options.openTo : '')+
108 | '&language='+(options.language || fp.browser.getLanguage())+
109 | (options.mobile !== undefined ? '&mobile='+options.mobile : '')+
110 | // v2
111 | (options.backgroundUpload !== undefined ? '&bu='+options.backgroundUpload : '')+
112 | (options.cropRatio ? '&cratio='+options.cropRatio : '')+
113 | (options.cropDim ? '&cdim='+options.cropDim.join(',') : '')+
114 | (options.cropMax ? '&cmax='+options.cropMax.join(',') : '')+
115 | (options.cropMin ? '&cmin='+options.cropMin.join(',') : '')+
116 | (options.cropForce !== undefined ? '&cforce='+options.cropForce : '')+
117 | (options.hide !== undefined ? '&hide='+options.hide : '')+
118 | (options.customCss ? '&css='+encodeURIComponent(options.customCss) : '')+
119 | (options.customText ? '&text='+encodeURIComponent(options.customText) : '')+
120 | (options.imageMin ? '&imin='+options.imageMin.join(',') : '')+
121 | (options.imageMax ? '&imax='+options.imageMax.join(',') : '')+
122 | (options.imageDim ? '&idim='+options.imageDim.join(',') : '')+
123 | (options.imageQuality ? '&iq='+options.imageQuality : '')+
124 | (options.noFileReader ? '&nfl='+options.noFileReader : '')+
125 | (fp.util.isCanvasSupported() ? '' : '&canvas=false')+
126 | (options.redirectUrl ? '&redirect_url='+options.redirectUrl : '')+
127 | /*
128 | prevent from showing close button twice
129 | */
130 | ((options.showClose && options.container !== 'modal') ? '&showClose='+options.showClose : '')+
131 | constructSecurityQuery(options)+
132 | '&plugin='+getPlugin();
133 | }
134 |
135 | function constructSecurityQuery(options) {
136 | return (options.signature ? '&signature='+options.signature : '')+
137 | (options.policy ? '&policy='+options.policy : '');
138 | }
139 |
140 | function getPlugin(){
141 | return filepicker.plugin || 'js_lib';
142 | }
143 |
144 |
145 | function constructConversionsQuery(conversions){
146 | conversions = conversions || [];
147 | var allowed = [],
148 | i ,
149 | j;
150 |
151 | /*
152 | Use for in loop.
153 | Array.filter && Array.indexOf not supported in IE8
154 | */
155 |
156 | for (i in conversions) {
157 | for (j in allowedConversions) {
158 | if (conversions[i] === allowedConversions[j] && conversions.hasOwnProperty(i)) {
159 | allowed.push(conversions[i]);
160 | }
161 | }
162 | }
163 |
164 | /*
165 | Only crop by default
166 | */
167 | if (!allowed.length) {
168 | allowed.push('crop');
169 | }
170 | return '&co='+allowed.join(',');
171 | }
172 |
173 |
174 | return {
175 | BASE: base,
176 | DIALOG_BASE: dialog_base,
177 | API_COMM: base + '/dialog/comm_iframe/',
178 | COMM: dialog_base + '/dialog/comm_iframe/',
179 | FP_COMM_FALLBACK: dialog_base + '/dialog/comm_hash_iframe/',
180 | STORE: store_url,
181 | PICK: pick_url,
182 | EXPORT: export_url,
183 | LOGOUT: base + '/api/clients/unauth',
184 | constructPickUrl: constructPickUrl,
185 | constructConvertUrl: constructConvertUrl,
186 | constructPickFolderUrl: constructPickFolderUrl,
187 | constructExportUrl: constructExportUrl,
188 | constructWriteUrl: constructWriteUrl,
189 | constructStoreUrl: constructStoreUrl,
190 | constructHostCommFallback: constructHostCommFallback,
191 | getPlugin: getPlugin
192 | };
193 | });
194 |
--------------------------------------------------------------------------------
/src/server/ajax.js:
--------------------------------------------------------------------------------
1 | //ajax.js
2 | 'use strict';
3 | /*jshint eqeqeq:false */
4 |
5 | filepicker.extend('ajax', function(){
6 | var fp = this;
7 |
8 | var get_request = function(url, options) {
9 | options.method = 'GET';
10 | make_request(url, options);
11 | };
12 |
13 | var post_request = function(url, options) {
14 | options.method = 'POST';
15 | url += (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheBust='+fp.util.getId();
16 | make_request(url, options);
17 | };
18 |
19 | var toQueryString = function(object, base) {
20 | var queryString = [];
21 | for (var key in object) {
22 | var value = object[key];
23 | if (base){
24 | key = base + '. + key + ';
25 | }
26 | var result;
27 | switch (fp.util.typeOf(value)){
28 | case 'object': result = toQueryString(value, key); break;
29 | case 'array':
30 | var qs = {};
31 | for (var i = 0; i < value.length; i++) {
32 | qs[i] = value[i];
33 | }
34 | result = toQueryString(qs, key);
35 | break;
36 | default: result = key + '=' + encodeURIComponent(value); break;
37 | }
38 | if (value !== null){
39 | queryString.push(result);
40 | }
41 | }
42 |
43 | return queryString.join('&');
44 | };
45 |
46 | var getXhr = function() {
47 | try{
48 | // Modern browsers
49 | return new window.XMLHttpRequest();
50 | } catch (e){
51 | // IE
52 | try{
53 | return new window.ActiveXObject('Msxml2.XMLHTTP');
54 | } catch (e) {
55 | try{
56 | return new window.ActiveXObject('Microsoft.XMLHTTP');
57 | } catch (e){
58 | // Something went wrong
59 | return null;
60 | }
61 | }
62 | }
63 | };
64 |
65 | var make_request = function(url, options) {
66 | //setting defaults
67 | url = url || '';
68 | var method = options.method ? options.method.toUpperCase() : 'POST';
69 | var success = options.success || function(){};
70 | var error = options.error || function(){};
71 | var async = options.async === undefined ? true : options.async;
72 | var data = options.data || null;
73 | var processData = options.processData === undefined ? true : options.processData;
74 | var headers = options.headers || {};
75 |
76 | var urlParts = fp.util.parseUrl(url);
77 | var origin = window.location.protocol + '//' + window.location.host;
78 | var crossdomain = origin !== urlParts.origin;
79 | var finished = false;
80 | url += (url.indexOf('?') >= 0 ? '&' : '?') + 'plugin=' + fp.urls.getPlugin();
81 | //var crossdomain = window.location
82 | if (data && processData) {
83 | data = toQueryString(options.data);
84 | }
85 |
86 | //creating the request
87 | var xhr;
88 | if (options.xhr) {
89 | xhr = options.xhr;
90 | } else {
91 | xhr = getXhr();
92 | if (!xhr) {
93 | options.error('Ajax not allowed');
94 | return xhr;
95 | }
96 | }
97 |
98 | if (crossdomain && window.XDomainRequest && !('withCredentials' in xhr)) {
99 | return new XDomainAjax(url, options);
100 | }
101 |
102 | if (options.progress && xhr.upload) {
103 | xhr.upload.addEventListener('progress', function(e){
104 | if (e.lengthComputable) {
105 | options.progress(Math.round((e.loaded * 95) / e.total));
106 | }
107 | }, false);
108 | }
109 |
110 | //Handlers
111 | var onStateChange = function(){
112 | if(xhr.readyState == 4 && !finished){
113 | if (options.progress) {options.progress(100);}
114 | if (xhr.status >= 200 && xhr.status < 300) {
115 | //TODO - look into using xhr.responseType and xhr.response for binary blobs. Not sure what to return
116 | var resp = xhr.responseText;
117 | if (options.json) {
118 | try {
119 | resp = fp.json.decode(resp);
120 | } catch (e) {
121 | onerror.call(xhr, 'Invalid json: '+resp);
122 | return;
123 | }
124 | }
125 | success(resp, xhr.status, xhr);
126 | finished = true;
127 | } else {
128 | onerror.call(xhr, xhr.responseText);
129 | finished = true;
130 | }
131 | }
132 | };
133 | xhr.onreadystatechange = onStateChange;
134 |
135 | var onerror = function(err) {
136 | //already handled
137 | if (finished) {return;}
138 |
139 | if (options.progress) {options.progress(100);}
140 |
141 | finished = true;
142 | if (this.status == 400) {
143 | error('bad_params', this.status, this);
144 | return;
145 | } else if (this.status == 403) {
146 | error('not_authorized', this.status, this);
147 | return;
148 | } else if (this.status == 404) {
149 | error('not_found', this.status, this);
150 | return;
151 | }
152 | if (crossdomain) {
153 | if (this.readyState == 4 && this.status === 0) {
154 | error('CORS_not_allowed', this.status, this);
155 | return;
156 | } else {
157 | error('CORS_error', this.status, this);
158 | return;
159 | }
160 | }
161 |
162 | //if we're here, we don't know what happened
163 | error(err, this.status, this);
164 | };
165 |
166 | xhr.onerror = onerror;
167 |
168 | //Executing the request
169 | if (data && method == 'GET') {
170 | url += (url.indexOf('?') !== -1 ? '&' : '?') + data;
171 | data = null;
172 | }
173 |
174 | if (options.withCredentials) {
175 | xhr.withCredentials = true;
176 | }
177 |
178 | xhr.open(method, url, async);
179 | if (options.json) {
180 | xhr.setRequestHeader('Accept', 'application/json, text/javascript');
181 | } else {
182 | xhr.setRequestHeader('Accept', 'text/javascript, text/html, application/xml, text/xml, */*');
183 | }
184 |
185 | var contentType = headers['Content-Type'] || headers['content-type'];
186 | if (data && processData && (method == 'POST' || method == 'PUT') && contentType === undefined) {
187 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=utf-8');
188 | }
189 |
190 | if (headers) {
191 | for (var key in headers) {
192 | xhr.setRequestHeader(key, headers[key]);
193 | }
194 | }
195 |
196 | xhr.send(data);
197 |
198 | return xhr;
199 | };
200 |
201 | //Ajax using XDomainRequest - different enough from normal xhr that we do it separately
202 | var XDomainAjax = function(url, options) {
203 | if (!window.XDomainRequest) {return null;}
204 |
205 | var method = options.method ? options.method.toUpperCase() : 'POST';
206 | var success = options.success || function(){};
207 | var error = options.error || function(){};
208 | var data = options.data || {};
209 |
210 | //protocol of the url must match our protocol
211 | if (window.location.protocol == 'http:') {
212 | url = url.replace('https:','http:');
213 | } else if (window.location.protocol == 'https:') {
214 | url = url.replace('http:','https:');
215 | }
216 |
217 | /*
218 | if (options.headers['Content-Type']) {
219 | //custom content type, so we smush the data into a {data: data}
220 | data = {'data': data};
221 | data.mimetype = options.headers['Content-Type'];
222 | }
223 | */
224 |
225 | if (options.async) {
226 | throw new fp.FilepickerException('Asyncronous Cross-domain requests are not supported');
227 | }
228 |
229 | //Only supports get and post
230 | if (method !== 'GET' && method !== 'POST') {
231 | data._method = method;
232 | method = 'POST';
233 | }
234 |
235 | if (options.processData !== false) {
236 | data = data ? toQueryString(data) : null;
237 | }
238 |
239 | //Executing the request
240 | if (data && method == 'GET') {
241 | url += (url.indexOf('?') >= 0 ? '&' : '?') + data;
242 | data = null;
243 | }
244 |
245 | //so we know it's an xdr and can handle appropriately
246 | url += (url.indexOf('?') >= 0 ? '&' : '?') + '_xdr=true&_cacheBust='+fp.util.getId();
247 |
248 | var xdr = new window.XDomainRequest();
249 | xdr.onload = function() {
250 | var resp = xdr.responseText;
251 | if (options.progress) {options.progress(100);}
252 | if (options.json) {
253 | try {
254 | resp = fp.json.decode(resp);
255 | } catch (e) {
256 | error('Invalid json: ' + resp, 200, xdr);
257 | return;
258 | }
259 | }
260 | //assume status == 200, since we can't get it for real
261 | success(resp, 200, xdr);
262 | };
263 | xdr.onerror = function() {
264 | if (options.progress) {options.progress(100);}
265 | error(xdr.responseText || 'CORS_error', this.status || 500, this);
266 | };
267 | //Must have an onprogress or ie will abort
268 | xdr.onprogress = function(){};
269 | xdr.ontimeout = function(){};
270 | xdr.timeout = 30000;
271 | //we can't set any headers
272 | xdr.open(method, url, true);
273 | xdr.send(data);
274 | return xdr;
275 | };
276 |
277 | return {
278 | get: get_request,
279 | post: post_request,
280 | request: make_request
281 | };
282 | });
283 |
--------------------------------------------------------------------------------
/src/server/iframeAjax.js:
--------------------------------------------------------------------------------
1 | //iframeAjax.js
2 | 'use strict';
3 |
4 | filepicker.extend('iframeAjax', function(){
5 | var fp = this;
6 |
7 | var IFRAME_ID = 'ajax_iframe';
8 |
9 | //we can only have one out at a time
10 | var queue = [];
11 | var running = false;
12 |
13 | var get_request = function(url, options) {
14 | options.method = 'GET';
15 | make_request(url, options);
16 | };
17 |
18 | var post_request = function(url, options) {
19 | options.method = 'POST';
20 | url += (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheBust='+fp.util.getId();
21 | make_request(url, options);
22 | };
23 |
24 | var runQueue = function(){
25 | if (queue.length > 0) {
26 | var next = queue.shift();
27 | make_request(next.url, next.options);
28 | }
29 | };
30 |
31 | //Take the data, wrap it in an input and a form, submit that into an iframe, and get the response
32 | var make_request = function(url, options) {
33 | if (running) {
34 | queue.push({url: url, options: options});
35 | return;
36 | }
37 |
38 | url += (url.indexOf('?') >= 0 ? '&' : '?') + 'plugin=' + fp.urls.getPlugin() + '&_cacheBust=' + fp.util.getId();
39 | url += '&Content-Type=text%2Fhtml';
40 |
41 | fp.comm.openChannel();
42 |
43 | //Opening an iframe to make the request to
44 | var uploadIFrame;
45 | //IE makes us do rediculous things -
46 | //http://terminalapp.net/submitting-a-form-with-target-set-to-a-script-generated-iframe-on-ie/
47 | try {
48 | uploadIFrame = document.createElement('