attache_rails 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1fa11ba11f4fc5b00b924fd2491a67aa27a94a8
4
- data.tar.gz: 9f2648a031e25cfd750d6c8d22f1b3f49ad931bd
3
+ metadata.gz: edd291b58ed01055cda499254592cfd028ed2cde
4
+ data.tar.gz: e8c95574587c899c798cb3fe550523a86222ffa7
5
5
  SHA512:
6
- metadata.gz: bbbea818903c87c0a1bcbbab938df1cd424326cabfeeda4529ab5b14d0e8c5aaa83acac38084974db12cea1b6071c6523ab9bca8c027495a94509ff5541963df
7
- data.tar.gz: fc14a4292d0ebebb5f878c484c33bee445ea3b9dc31849ee4b41a17bdc2de8713e5669c24bdfdcd36555743134aa9fdbb1d165f348b5b12a0b10ed9ad252aa91
6
+ metadata.gz: c6b5fa2be93e5a90b22bd39e58330eeaf663733af892a0c98ddd7c76fa2500ba8d3b6805b10a33dd1ffaddaf1f7c4b81654991845991b5e151f14e6f5b31a937
7
+ data.tar.gz: 9889e52f28712d7c5a10a50933ec7d637cb45a111b51fbcd92620989dfe883e88a71d1a464910b67a4068be658dcbe63b765ab8feabb6393035993ad02e6f942
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Ruby on Rails integration for [attache server](https://github.com/choonkeat/attache)
4
4
 
5
- NOTE: You can learn how to use this gem by looking at the [changes made to our example app](https://github.com/choonkeat/attache-railsapp/commit/16cb1274dcce5be01b6c9d42ad60c30c106ad7f9) or follow the step by step instructions in this `README`
5
+ NOTE: You can learn how to use this gem by looking at the [changes made to our example app](https://github.com/choonkeat/attache-railsapp/compare/fc47c17...master) or follow the step by step instructions in this `README`
6
6
 
7
7
  ## Dependencies
8
8
 
@@ -32,10 +32,19 @@ Or you can include the various scripts yourself
32
32
 
33
33
  ``` javascript
34
34
  //= require attache/cors_upload
35
+ //= require attache/file_input
35
36
  //= require attache/bootstrap3
36
37
  //= require attache/ujs
37
38
  ```
38
39
 
40
+ If you want to customize the file upload look and feel, define your own react `<AttacheFilePreview/>` renderer *before* including the attache js. For example,
41
+
42
+ ``` javascript
43
+ //= require ./my_attache_file_preview.js
44
+ //= require attache
45
+ ```
46
+
47
+
39
48
  ## Usage
40
49
 
41
50
  ### Database
@@ -138,7 +147,6 @@ Alternatively, you can get the list of urls and manipulate it however you want
138
147
  * If this variable is not set, then upload requests will not be signed & `ATTACHE_UPLOAD_DURATION` will be ignored
139
148
  * If this variable is set, it must be the same value as `SECRET_KEY` is set on the `attache` server
140
149
 
141
-
142
150
  # License
143
151
 
144
152
  MIT
@@ -1,106 +1,48 @@
1
- var AttacheFileInput = React.createClass({displayName: "AttacheFileInput",
2
-
3
- getInitialState: function() {
4
- var files = {};
5
- var array = ([].concat(JSON.parse(this.props['data-value'])));
6
- $.each(array, function(uid, json) {
7
- if (json) files[uid] = { path: json };
8
- });
9
- return {files: files};
10
- },
11
-
12
- onRemove: function(uid, e) {
13
- delete this.state.files[uid];
14
- this.setState(this.state);
15
- e.preventDefault();
16
- e.stopPropagation();
17
- },
18
-
19
- onChange: function() {
20
- var file_element = this.getDOMNode().firstChild;
21
- // user cancelled file chooser dialog. ignore
22
- if (file_element.files.length == 0) return;
23
- this.state.files = {};
24
- this.setState(this.state);
25
- // upload the file via CORS
26
- var that = this;
27
- new CORSUpload({
28
- file_element: file_element, onComplete: this.setFileValue, onProgress: this.setFileValue,
29
- onError: function(uid, status) { that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' }); }
30
- });
31
- // we don't want the file binary to be uploaded in the main form
32
- file_element.value = '';
33
- },
34
-
35
- setFileValue: function(key, value) {
36
- this.state.files[key] = value;
37
- this.setState(this.state);
38
- },
39
-
40
- render: function() {
41
- var that = this;
42
- var previews = [];
43
- $.each(that.state.files, function(key, result) {
44
- var json = JSON.stringify(result);
45
- if (result.path) {
46
- var parts = result.path.split('/');
47
- parts.splice(parts.length-1, 0, encodeURIComponent(that.props['data-geometry'] || '128x128#'));
48
- result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
1
+ if (typeof AttacheFilePreview === 'undefined') {
2
+
3
+ var AttacheFilePreview = React.createClass({displayName: "AttacheFilePreview",
4
+
5
+ getInitialState: function() {
6
+ return { srcWas: '' };
7
+ },
8
+
9
+ onSrcLoaded: function() {
10
+ this.setState({ srcWas: this.props.src });
11
+ },
12
+
13
+ render: function() {
14
+ // progressbar
15
+ if (this.state.srcWas != this.props.src) {
16
+ var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
17
+ var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
18
+ var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
19
+ var pctStyle = { width: pctString, minWidth: '3em' };
20
+ var progress = (
21
+ React.createElement("div", {className: "progress"},
22
+ React.createElement("div", {className: className, role: "progressbar", "aria-valuenow": this.props.percentLoaded, "aria-valuemin": "0", "aria-valuemax": "100", style: pctStyle},
23
+ pctDesc
24
+ )
25
+ )
26
+ );
49
27
  }
50
- previews.push(
51
- React.createElement("div", {className: "thumbnail"},
52
- React.createElement("input", {type: "hidden", name: that.props.name, value: result.path, readOnly: "true"}),
53
- React.createElement(AttacheFilePreview, React.__spread({}, result, {key: key, onRemove: that.onRemove.bind(that, key)}))
54
- )
55
- );
56
- });
57
- return (
58
- React.createElement("label", {htmlFor: this.props.id, className: "attache-file-selector"},
59
- React.createElement("input", React.__spread({type: "file"}, this.props, {onChange: this.onChange})),
60
- previews
61
- )
62
- );
63
- }
64
- });
65
-
66
- var AttacheFilePreview = React.createClass({displayName: "AttacheFilePreview",
67
-
68
- getInitialState: function() {
69
- return { srcWas: '' };
70
- },
71
28
 
72
- removeProgressBar: function() {
73
- this.setState({ srcWas: this.props.src });
74
- },
75
-
76
- render: function() {
77
- var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
78
- var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
79
- var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
80
- var img = (this.props.src ? (React.createElement("img", {src: this.props.src, onLoad: this.removeProgressBar})) : '');
81
- var pctStyle = { width: pctString, minWidth: '3em' };
82
- var cptStyle = { textOverflow: "ellipsis" };
83
- var caption = React.createElement("div", {className: "pull-left", style: cptStyle}, this.props.filename || this.props.path && this.props.path.split('/').pop());
29
+ // img tag
30
+ if (this.props.src) {
31
+ var img = React.createElement("img", {src: this.props.src, onLoad: this.onSrcLoaded});
32
+ }
84
33
 
85
- if (this.state.srcWas != this.props.src) {
86
- var progress = (
87
- React.createElement("div", {className: "progress"},
88
- React.createElement("div", {className: className, role: "progressbar", "aria-valuenow": this.props.percentLoaded, "aria-valuemin": "0", "aria-valuemax": "100", style: pctStyle},
89
- pctDesc
34
+ // combined
35
+ return (
36
+ React.createElement("div", {className: "attache-file-preview"},
37
+ progress,
38
+ img,
39
+ React.createElement("div", {className: "clearfix"},
40
+ React.createElement("div", {className: "pull-left", style: "textOverflow: 'ellipsis;'"}, this.props.filename),
41
+ React.createElement("a", {href: "#remove", className: "pull-right", onClick: this.props.onRemove, title: "Click to remove"}, "×")
90
42
  )
91
43
  )
92
44
  );
93
45
  }
46
+ });
94
47
 
95
- return (
96
- React.createElement("div", {className: "attache-file-preview"},
97
- progress,
98
- img,
99
- React.createElement("div", {className: "clearfix"},
100
- caption,
101
- React.createElement("a", {href: "#remove", className: "pull-right", onClick: this.props.onRemove, title: "Click to remove"}, "×")
102
- )
103
- )
104
- );
105
- }
106
- });
48
+ }
@@ -1,106 +1,48 @@
1
- var AttacheFileInput = React.createClass({
2
-
3
- getInitialState: function() {
4
- var files = {};
5
- var array = ([].concat(JSON.parse(this.props['data-value'])));
6
- $.each(array, function(uid, json) {
7
- if (json) files[uid] = { path: json };
8
- });
9
- return {files: files};
10
- },
11
-
12
- onRemove: function(uid, e) {
13
- delete this.state.files[uid];
14
- this.setState(this.state);
15
- e.preventDefault();
16
- e.stopPropagation();
17
- },
18
-
19
- onChange: function() {
20
- var file_element = this.getDOMNode().firstChild;
21
- // user cancelled file chooser dialog. ignore
22
- if (file_element.files.length == 0) return;
23
- this.state.files = {};
24
- this.setState(this.state);
25
- // upload the file via CORS
26
- var that = this;
27
- new CORSUpload({
28
- file_element: file_element, onComplete: this.setFileValue, onProgress: this.setFileValue,
29
- onError: function(uid, status) { that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' }); }
30
- });
31
- // we don't want the file binary to be uploaded in the main form
32
- file_element.value = '';
33
- },
34
-
35
- setFileValue: function(key, value) {
36
- this.state.files[key] = value;
37
- this.setState(this.state);
38
- },
39
-
40
- render: function() {
41
- var that = this;
42
- var previews = [];
43
- $.each(that.state.files, function(key, result) {
44
- var json = JSON.stringify(result);
45
- if (result.path) {
46
- var parts = result.path.split('/');
47
- parts.splice(parts.length-1, 0, encodeURIComponent(that.props['data-geometry'] || '128x128#'));
48
- result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
1
+ if (typeof AttacheFilePreview === 'undefined') {
2
+
3
+ var AttacheFilePreview = React.createClass({
4
+
5
+ getInitialState: function() {
6
+ return { srcWas: '' };
7
+ },
8
+
9
+ onSrcLoaded: function() {
10
+ this.setState({ srcWas: this.props.src });
11
+ },
12
+
13
+ render: function() {
14
+ // progressbar
15
+ if (this.state.srcWas != this.props.src) {
16
+ var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
17
+ var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
18
+ var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
19
+ var pctStyle = { width: pctString, minWidth: '3em' };
20
+ var progress = (
21
+ <div className="progress">
22
+ <div className={className} role="progressbar" aria-valuenow={this.props.percentLoaded} aria-valuemin="0" aria-valuemax="100" style={pctStyle}>
23
+ {pctDesc}
24
+ </div>
25
+ </div>
26
+ );
49
27
  }
50
- previews.push(
51
- <div className="thumbnail">
52
- <input type="hidden" name={that.props.name} value={result.path} readOnly="true" />
53
- <AttacheFilePreview {...result} key={key} onRemove={that.onRemove.bind(that, key)}/>
54
- </div>
55
- );
56
- });
57
- return (
58
- <label htmlFor={this.props.id} className="attache-file-selector">
59
- <input type="file" {...this.props} onChange={this.onChange}/>
60
- {previews}
61
- </label>
62
- );
63
- }
64
- });
65
-
66
- var AttacheFilePreview = React.createClass({
67
-
68
- getInitialState: function() {
69
- return { srcWas: '' };
70
- },
71
28
 
72
- removeProgressBar: function() {
73
- this.setState({ srcWas: this.props.src });
74
- },
75
-
76
- render: function() {
77
- var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
78
- var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
79
- var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
80
- var img = (this.props.src ? (<img src={this.props.src} onLoad={this.removeProgressBar} />) : '');
81
- var pctStyle = { width: pctString, minWidth: '3em' };
82
- var cptStyle = { textOverflow: "ellipsis" };
83
- var caption = <div className="pull-left" style={cptStyle}>{this.props.filename || this.props.path && this.props.path.split('/').pop()}</div>;
29
+ // img tag
30
+ if (this.props.src) {
31
+ var img = <img src={this.props.src} onLoad={this.onSrcLoaded} />;
32
+ }
84
33
 
85
- if (this.state.srcWas != this.props.src) {
86
- var progress = (
87
- <div className="progress">
88
- <div className={className} role="progressbar" aria-valuenow={this.props.percentLoaded} aria-valuemin="0" aria-valuemax="100" style={pctStyle}>
89
- {pctDesc}
34
+ // combined
35
+ return (
36
+ <div className="attache-file-preview">
37
+ {progress}
38
+ {img}
39
+ <div className="clearfix">
40
+ <div className="pull-left" style="textOverflow: 'ellipsis;'">{this.props.filename}</div>
41
+ <a href="#remove" className="pull-right" onClick={this.props.onRemove} title="Click to remove">&times;</a>
90
42
  </div>
91
43
  </div>
92
44
  );
93
45
  }
46
+ });
94
47
 
95
- return (
96
- <div className="attache-file-preview">
97
- {progress}
98
- {img}
99
- <div className="clearfix">
100
- {caption}
101
- <a href="#remove" className="pull-right" onClick={this.props.onRemove} title="Click to remove">&times;</a>
102
- </div>
103
- </div>
104
- );
105
- }
106
- });
48
+ }
@@ -1,11 +1,11 @@
1
- var CORSUpload = (function() {
1
+ var AttacheCORSUpload = (function() {
2
2
  var counter = 0;
3
3
 
4
- CORSUpload.prototype.onComplete = function(uid, json) { };
5
- CORSUpload.prototype.onProgress = function(uid, json) { };
6
- CORSUpload.prototype.onError = function(uid, status) { alert(status); };
4
+ AttacheCORSUpload.prototype.onComplete = function(uid, json) { };
5
+ AttacheCORSUpload.prototype.onProgress = function(uid, json) { };
6
+ AttacheCORSUpload.prototype.onError = function(uid, status) { alert(status); };
7
7
 
8
- function CORSUpload(options) {
8
+ function AttacheCORSUpload(options) {
9
9
  if (options == null) options = {};
10
10
  for (option in options) {
11
11
  this[option] = options[option];
@@ -13,7 +13,7 @@ var CORSUpload = (function() {
13
13
  this.handleFileSelect(options.file_element);
14
14
  }
15
15
 
16
- CORSUpload.prototype.handleFileSelect = function(file_element) {
16
+ AttacheCORSUpload.prototype.handleFileSelect = function(file_element) {
17
17
  var f, files, output, _i, _len, _results, url, $ele;
18
18
  $ele = $(file_element);
19
19
  url = $ele.data('uploadurl');
@@ -37,7 +37,7 @@ var CORSUpload = (function() {
37
37
  return _results;
38
38
  };
39
39
 
40
- CORSUpload.prototype.createCORSRequest = function(method, url) {
40
+ AttacheCORSUpload.prototype.createCORSRequest = function(method, url) {
41
41
  var xhr;
42
42
  xhr = new XMLHttpRequest();
43
43
  if (xhr.withCredentials != null) {
@@ -51,7 +51,7 @@ var CORSUpload = (function() {
51
51
  return xhr;
52
52
  };
53
53
 
54
- CORSUpload.prototype.performUpload = function(file, url) {
54
+ AttacheCORSUpload.prototype.performUpload = function(file, url) {
55
55
  var this_s3upload, xhr;
56
56
  this_s3upload = this;
57
57
  url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'file=' + encodeURIComponent(file.name);
@@ -80,6 +80,6 @@ var CORSUpload = (function() {
80
80
  return xhr.send(file);
81
81
  };
82
82
 
83
- return CORSUpload;
83
+ return AttacheCORSUpload;
84
84
 
85
85
  })();
@@ -0,0 +1,71 @@
1
+ var AttacheFileInput = React.createClass({displayName: "AttacheFileInput",
2
+
3
+ getInitialState: function() {
4
+ var files = {};
5
+ var array = ([].concat(JSON.parse(this.props['data-value'])));
6
+ $.each(array, function(uid, json) {
7
+ if (json) files[uid] = { path: json };
8
+ });
9
+ return {files: files};
10
+ },
11
+
12
+ onRemove: function(uid, e) {
13
+ delete this.state.files[uid];
14
+ this.setState(this.state);
15
+ e.preventDefault();
16
+ e.stopPropagation();
17
+ },
18
+
19
+ onChange: function() {
20
+ var file_element = this.getDOMNode().firstChild;
21
+ // user cancelled file chooser dialog. ignore
22
+ if (file_element.files.length == 0) return;
23
+ this.state.files = {};
24
+ this.setState(this.state);
25
+ // upload the file via CORS
26
+ var that = this;
27
+ new AttacheCORSUpload({
28
+ file_element: file_element,
29
+ onComplete: this.setFileValue,
30
+ onProgress: this.setFileValue,
31
+ onError: function(uid, status) {
32
+ that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' });
33
+ }
34
+ });
35
+
36
+ // we don't want the file binary to be uploaded in the main form
37
+ // so the actual file input is neutered
38
+ file_element.value = '';
39
+ },
40
+
41
+ setFileValue: function(key, value) {
42
+ this.state.files[key] = value;
43
+ this.setState(this.state);
44
+ },
45
+
46
+ render: function() {
47
+ var that = this;
48
+ var previews = [];
49
+ $.each(that.state.files, function(key, result) {
50
+ var json = JSON.stringify(result);
51
+ if (result.path) {
52
+ var parts = result.path.split('/');
53
+ parts.splice(parts.length-1, 0, encodeURIComponent(that.props['data-geometry'] || '128x128#'));
54
+ result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
55
+ result.filename = result.src.split('/').pop().split(/[#?]/).shift();
56
+ }
57
+ previews.push(
58
+ React.createElement("div", {className: "attache-file-input"},
59
+ React.createElement("input", {type: "hidden", name: that.props.name, value: result.path, readOnly: "true"}),
60
+ React.createElement(AttacheFilePreview, React.__spread({}, result, {key: key, onRemove: that.onRemove.bind(that, key)}))
61
+ )
62
+ );
63
+ });
64
+ return (
65
+ React.createElement("label", {htmlFor: this.props.id, className: "attache-file-selector"},
66
+ React.createElement("input", React.__spread({type: "file"}, this.props, {onChange: this.onChange})),
67
+ previews
68
+ )
69
+ );
70
+ }
71
+ });
@@ -0,0 +1,71 @@
1
+ var AttacheFileInput = React.createClass({
2
+
3
+ getInitialState: function() {
4
+ var files = {};
5
+ var array = ([].concat(JSON.parse(this.props['data-value'])));
6
+ $.each(array, function(uid, json) {
7
+ if (json) files[uid] = { path: json };
8
+ });
9
+ return {files: files};
10
+ },
11
+
12
+ onRemove: function(uid, e) {
13
+ delete this.state.files[uid];
14
+ this.setState(this.state);
15
+ e.preventDefault();
16
+ e.stopPropagation();
17
+ },
18
+
19
+ onChange: function() {
20
+ var file_element = this.getDOMNode().firstChild;
21
+ // user cancelled file chooser dialog. ignore
22
+ if (file_element.files.length == 0) return;
23
+ this.state.files = {};
24
+ this.setState(this.state);
25
+ // upload the file via CORS
26
+ var that = this;
27
+ new AttacheCORSUpload({
28
+ file_element: file_element,
29
+ onComplete: this.setFileValue,
30
+ onProgress: this.setFileValue,
31
+ onError: function(uid, status) {
32
+ that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' });
33
+ }
34
+ });
35
+
36
+ // we don't want the file binary to be uploaded in the main form
37
+ // so the actual file input is neutered
38
+ file_element.value = '';
39
+ },
40
+
41
+ setFileValue: function(key, value) {
42
+ this.state.files[key] = value;
43
+ this.setState(this.state);
44
+ },
45
+
46
+ render: function() {
47
+ var that = this;
48
+ var previews = [];
49
+ $.each(that.state.files, function(key, result) {
50
+ var json = JSON.stringify(result);
51
+ if (result.path) {
52
+ var parts = result.path.split('/');
53
+ parts.splice(parts.length-1, 0, encodeURIComponent(that.props['data-geometry'] || '128x128#'));
54
+ result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
55
+ result.filename = result.src.split('/').pop().split(/[#?]/).shift();
56
+ }
57
+ previews.push(
58
+ <div className="attache-file-input">
59
+ <input type="hidden" name={that.props.name} value={result.path} readOnly="true" />
60
+ <AttacheFilePreview {...result} key={key} onRemove={that.onRemove.bind(that, key)}/>
61
+ </div>
62
+ );
63
+ });
64
+ return (
65
+ <label htmlFor={this.props.id} className="attache-file-selector">
66
+ <input type="file" {...this.props} onChange={this.onChange}/>
67
+ {previews}
68
+ </label>
69
+ );
70
+ }
71
+ });
@@ -1,3 +1,4 @@
1
1
  //= require attache/cors_upload
2
+ //= require attache/file_input
2
3
  //= require attache/bootstrap3
3
4
  //= require attache/ujs
@@ -1,3 +1,3 @@
1
1
  module AttacheRails
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attache_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - choonkeat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-26 00:00:00.000000000 Z
11
+ date: 2015-03-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -24,6 +24,8 @@ files:
24
24
  - app/assets/javascripts/attache/bootstrap3.js
25
25
  - app/assets/javascripts/attache/bootstrap3.js.jsx
26
26
  - app/assets/javascripts/attache/cors_upload.js
27
+ - app/assets/javascripts/attache/file_input.js
28
+ - app/assets/javascripts/attache/file_input.js.jsx
27
29
  - app/assets/javascripts/attache/ujs.js
28
30
  - lib/attache_rails.rb
29
31
  - lib/attache_rails/engine.rb