attache_rails 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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