attache-rails 0.4.1 → 1.0.0

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: f691ca01b84ed9632ecbfd03562f5b6b945ad67b
4
- data.tar.gz: 609ef783d0cd979e6182f1ce3f6f6bb9ec4fabd5
3
+ metadata.gz: 9d102ed58a94d38425b805352694aa5011340006
4
+ data.tar.gz: 62d859eb1f12efe92f25f4a3ef2da1a28d00ed87
5
5
  SHA512:
6
- metadata.gz: 460e940fc24d692f513dd8d678dd04953ddb7910e8dd1e9b9dc631dd4ea6e985ad79ee78c764fdb6f6321b78b5509ca146b14912484415008deb4e593eb0e679
7
- data.tar.gz: 17422cee374be8e77da8a3b8720627ee0b402a71d2f51cdf4422ab524c0bc2634220f777706ab7fcc0662aa872175a1e2d3b7f723131d8cf623812c91487a926
6
+ metadata.gz: f2977a0f4f997292c63d7aa824209dc113c655385c50f23ca64007bbdca8b7ed38e2e1f674bd7bceb6140191003d2c1b76f162879fc6ac41b8f0185d4b1d6865
7
+ data.tar.gz: eeddf1577a1f8824800cdddb005a686748eec36f8784612e836fa125362b2ce5ef2f8d11ec5cf3844a3bcc8e8e34768289dda8504904a4a5fb34f61226ff922d
data/README.md CHANGED
@@ -31,7 +31,7 @@ Add the attache javascript to your `application.js`
31
31
  //= require attache
32
32
  ```
33
33
 
34
- If you want to customize the file upload look and feel, define your own react `<AttacheFilePreview/>` renderer *before* including the attache js. For example,
34
+ If you want to customize the file upload look and feel, define your own React `<AttacheFilePreview/>`, `<AttacheHeader/>`, `<AttachePlaceholder/>` renderer *before* including the attache js. For example,
35
35
 
36
36
  ``` javascript
37
37
  //= require ./my_attache_file_preview.js
@@ -1,4 +1,423 @@
1
- //= require attache/cors_upload
2
- //= require attache/bootstrap3
3
- //= require attache/file_input
4
- //= require attache/ujs
1
+ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2
+ 'use strict';
3
+
4
+ var _file_input = require('./attache/file_input');
5
+
6
+ var upgradeFileInput = function upgradeFileInput() {
7
+ var safeWords = { 'class': 'className', 'for': 'htmlFor' };
8
+ var sel = document.getElementsByClassName('enable-attache');
9
+ var ele, attrs, name, value;
10
+ for (var i = sel.length - 1; i >= 0; i--) {
11
+ ele = sel[i];
12
+ attrs = {};
13
+ for (var j = 0; j < ele.attributes.length; j++) {
14
+ name = ele.attributes[j].name;
15
+ value = ele.attributes[j].value;
16
+ if (name === 'class') value = value.replace('enable-attache', 'attache-enabled');
17
+ name = safeWords[name] || name;
18
+ attrs[name] = value;
19
+ }
20
+ var wrap = document.createElement('div');
21
+ ele.parentNode.replaceChild(wrap, ele);
22
+ ReactDOM.render(React.createElement(_file_input.AttacheFileInput, React.__spread({}, attrs)), wrap);
23
+ }
24
+ }; /*global $*/
25
+ /*global React*/
26
+ /*global ReactDOM*/
27
+
28
+ $(document).on('page:change', upgradeFileInput);
29
+ $(upgradeFileInput);
30
+
31
+ },{"./attache/file_input":4}],2:[function(require,module,exports){
32
+ 'use strict';
33
+
34
+ Object.defineProperty(exports, "__esModule", {
35
+ value: true
36
+ });
37
+ /*global $*/
38
+ /*global React*/
39
+
40
+ var Bootstrap3FilePreview = exports.Bootstrap3FilePreview = React.createClass({
41
+ displayName: 'Bootstrap3FilePreview',
42
+ getInitialState: function getInitialState() {
43
+ return { srcWas: '' };
44
+ },
45
+ onSrcLoaded: function onSrcLoaded(event) {
46
+ this.setState({ srcWas: this.props.src });
47
+ $(event.target).trigger('attache:imgload');
48
+ },
49
+ onSrcError: function onSrcError(event) {
50
+ $(event.target).trigger('attache:imgerror');
51
+ },
52
+ render: function render() {
53
+ var previewClassName = 'attache-file-preview';
54
+
55
+ // progressbar
56
+ if (this.state.srcWas !== this.props.src) {
57
+ previewClassName = previewClassName + ' attache-loading';
58
+ var className = this.props.className || 'progress-bar progress-bar-striped active' + (this.props.src ? ' progress-bar-success' : '');
59
+ var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + '%';
60
+ var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
61
+ var pctStyle = { width: pctString, minWidth: '3em' };
62
+ var progress = React.createElement(
63
+ 'div',
64
+ { className: 'progress' },
65
+ React.createElement(
66
+ 'div',
67
+ {
68
+ className: className,
69
+ role: 'progressbar',
70
+ 'aria-valuenow': this.props.percentLoaded,
71
+ 'aria-valuemin': '0',
72
+ 'aria-valuemax': '100',
73
+ style: pctStyle },
74
+ pctDesc
75
+ )
76
+ );
77
+ }
78
+
79
+ // img tag
80
+ if (this.props.src) {
81
+ var img = React.createElement('img', { src: this.props.src, onLoad: this.onSrcLoaded, onError: this.onSrcError });
82
+ }
83
+
84
+ // combined
85
+ return React.createElement(
86
+ 'div',
87
+ { className: previewClassName },
88
+ progress,
89
+ img,
90
+ React.createElement(
91
+ 'div',
92
+ { className: 'clearfix' },
93
+ React.createElement(
94
+ 'div',
95
+ { className: 'pull-left' },
96
+ this.props.filename
97
+ ),
98
+ React.createElement(
99
+ 'a',
100
+ {
101
+ href: '#remove',
102
+ className: 'pull-right',
103
+ onClick: this.props.onRemove,
104
+ title: 'Click to remove' },
105
+ '×'
106
+ )
107
+ )
108
+ );
109
+ }
110
+ });
111
+
112
+ var Bootstrap3Placeholder = exports.Bootstrap3Placeholder = React.createClass({
113
+ displayName: 'Bootstrap3Placeholder',
114
+ render: function render() {
115
+ return React.createElement(
116
+ 'div',
117
+ { className: 'attache-file-preview' },
118
+ React.createElement('img', { src: this.props.src })
119
+ );
120
+ }
121
+ });
122
+
123
+ var Bootstrap3Header = exports.Bootstrap3Header = React.createClass({
124
+ displayName: 'Bootstrap3Header',
125
+ render: function render() {
126
+ return React.createElement('noscript', null);
127
+ }
128
+ });
129
+
130
+ },{}],3:[function(require,module,exports){
131
+ 'use strict';
132
+
133
+ var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
134
+
135
+ Object.defineProperty(exports, "__esModule", {
136
+ value: true
137
+ });
138
+
139
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
140
+
141
+ /*global $*/
142
+ /*global alert*/
143
+ /*global XMLHttpRequest*/
144
+ /*global XDomainRequest*/
145
+
146
+ var counter = 0;
147
+
148
+ var CORSUpload = exports.CORSUpload = (function () {
149
+ function CORSUpload(options) {
150
+ _classCallCheck(this, CORSUpload);
151
+
152
+ if (options == null) options = {};
153
+ var option;
154
+ for (option in options) {
155
+ this[option] = options[option];
156
+ }
157
+ }
158
+
159
+ // for overwriting
160
+
161
+ _createClass(CORSUpload, [{
162
+ key: 'createLocalThumbnail',
163
+ value: function createLocalThumbnail() {}
164
+ }, {
165
+ key: 'onComplete',
166
+ value: function onComplete(uid, json) {}
167
+ }, {
168
+ key: 'onProgress',
169
+ value: function onProgress(uid, json) {}
170
+ }, {
171
+ key: 'onError',
172
+ value: function onError(uid, status) {
173
+ alert(status);
174
+ }
175
+ }, {
176
+ key: 'handleFileSelect',
177
+ value: function handleFileSelect() {
178
+ var f, _i, _len, _results, url, $ele, prefix;
179
+ $ele = $(this.file_element);
180
+ url = $ele.data('uploadurl');
181
+ if ($ele.data('hmac')) {
182
+ url = url + '?hmac=' + encodeURIComponent($ele.data('hmac')) + '&uuid=' + encodeURIComponent($ele.data('uuid')) + '&expiration=' + encodeURIComponent($ele.data('expiration')) + '';
183
+ }
184
+
185
+ prefix = Date.now() + '_';
186
+ _results = [];
187
+ for (_i = 0, _len = this.files.length; _i < _len; _i++) {
188
+ f = this.files[_i];
189
+ this.createLocalThumbnail(f); // if any
190
+ f.uid = prefix + counter++;
191
+ this.onProgress(f.uid, { src: f.src, filename: f.name, percentLoaded: 0, bytesLoaded: 0, bytesTotal: f.size });
192
+ _results.push(this.performUpload(f, url));
193
+ }
194
+ return _results;
195
+ }
196
+ }, {
197
+ key: 'createCORSRequest',
198
+ value: function createCORSRequest(method, url) {
199
+ var xhr;
200
+ xhr = new XMLHttpRequest();
201
+ if (xhr.withCredentials != null) {
202
+ xhr.open(method, url, true);
203
+ } else if (typeof XDomainRequest !== 'undefined') {
204
+ xhr = new XDomainRequest();
205
+ xhr.open(method, url);
206
+ } else {
207
+ xhr = null;
208
+ }
209
+ return xhr;
210
+ }
211
+ }, {
212
+ key: 'performUpload',
213
+ value: function performUpload(file, url) {
214
+ var this_s3upload, xhr;
215
+ this_s3upload = this;
216
+ url = url + (url.indexOf('?') === -1 ? '?' : '&') + 'file=' + encodeURIComponent(file.name);
217
+ xhr = this.createCORSRequest('PUT', url);
218
+ if (!xhr) {
219
+ this.onError(file.uid, 'CORS not supported');
220
+ } else {
221
+ xhr.onload = function (e) {
222
+ if (xhr.status === 200) {
223
+ this_s3upload.onComplete(file.uid, JSON.parse(e.target.responseText));
224
+ } else {
225
+ return this_s3upload.onError(file.uid, xhr.status + ' ' + xhr.statusText);
226
+ }
227
+ };
228
+ xhr.onerror = function () {
229
+ return this_s3upload.onError(file.uid, 'Unable to reach server');
230
+ };
231
+ xhr.upload.onprogress = function (e) {
232
+ var percentLoaded;
233
+ if (e.lengthComputable) {
234
+ percentLoaded = Math.round(e.loaded / e.total * 100);
235
+ return this_s3upload.onProgress(file.uid, { src: file.src, filename: file.name, percentLoaded: percentLoaded, bytesLoaded: e.loaded, bytesTotal: e.total });
236
+ }
237
+ };
238
+ }
239
+ return xhr.send(file);
240
+ }
241
+ }]);
242
+
243
+ return CORSUpload;
244
+ })();
245
+
246
+ },{}],4:[function(require,module,exports){
247
+ 'use strict';
248
+
249
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /*global $*/
250
+ /*global window*/
251
+ /*global React*/
252
+ /*global ReactDOM*/
253
+
254
+ Object.defineProperty(exports, "__esModule", {
255
+ value: true
256
+ });
257
+ exports.AttacheFileInput = undefined;
258
+
259
+ var _cors_upload = require('./cors_upload');
260
+
261
+ var _bootstrap = require('./bootstrap3');
262
+
263
+ var AttacheFileInput = exports.AttacheFileInput = React.createClass({
264
+ displayName: 'AttacheFileInput',
265
+ getInitialState: function getInitialState() {
266
+ var files = {};
267
+ if (this.props['data-value']) {
268
+ $.each(JSON.parse(this.props['data-value']), function (uid, json) {
269
+ if (json) files[uid] = json;
270
+ });
271
+ }
272
+ return { files: files, attaches_discarded: [], uploading: 0 };
273
+ },
274
+ onRemove: function onRemove(uid, e) {
275
+ e.preventDefault();
276
+ e.stopPropagation();
277
+
278
+ var fieldname = ReactDOM.findDOMNode(this).firstChild.name; // when 'user[avatar]'
279
+ var newfield = fieldname.replace(/\w+\](\[\]|)$/, 'attaches_discarded][]'); // become 'user[attaches_discarded][]'
280
+
281
+ this.state.attaches_discarded.push({ fieldname: newfield, path: this.state.files[uid].path });
282
+ delete this.state.files[uid];
283
+
284
+ this.setState(this.state);
285
+ },
286
+ performUpload: function performUpload(file_element, files) {
287
+ // user cancelled file chooser dialog. ignore
288
+ if (!files || files.length === 0) return;
289
+ if (!this.props.multiple) {
290
+ this.state.files = {};
291
+ files = [files[0]]; // array of 1 element
292
+ }
293
+
294
+ this.setState(this.state);
295
+ // upload the file via CORS
296
+ var that = this;
297
+
298
+ that.state.uploading = that.state.uploading + files.length;
299
+ if (!that.state.submit_buttons) that.state.submit_buttons = $("button,input[type='submit']", $(file_element).parents('form')[0]).filter(':not(:disabled)');
300
+
301
+ var upload = new _cors_upload.CORSUpload({
302
+ file_element: file_element,
303
+ files: files,
304
+ onProgress: this.setFileValue,
305
+ onComplete: function onComplete() {
306
+ that.state.uploading--;
307
+ that.setFileValue.apply(this, arguments);
308
+ },
309
+ onError: function onError(uid, status) {
310
+ that.state.uploading--;
311
+ that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' });
312
+ }
313
+ });
314
+ upload.handleFileSelect();
315
+
316
+ // we don't want the file binary to be uploaded in the main form
317
+ // so the actual file input is neutered
318
+ file_element.value = '';
319
+ },
320
+ onChange: function onChange() {
321
+ var file_element = ReactDOM.findDOMNode(this).firstChild;
322
+ this.performUpload(file_element, file_element && file_element.files);
323
+ },
324
+ onDragOver: function onDragOver(e) {
325
+ e.stopPropagation();
326
+ e.preventDefault();
327
+ $(ReactDOM.findDOMNode(this)).addClass('attache-dragover');
328
+ },
329
+ onDragLeave: function onDragLeave(e) {
330
+ e.stopPropagation();
331
+ e.preventDefault();
332
+ $(ReactDOM.findDOMNode(this)).removeClass('attache-dragover');
333
+ },
334
+ onDrop: function onDrop(e) {
335
+ e.stopPropagation();
336
+ e.preventDefault();
337
+ var file_element = ReactDOM.findDOMNode(this).firstChild;
338
+ this.performUpload(file_element, e.target.files || e.dataTransfer.files);
339
+ $(ReactDOM.findDOMNode(this)).removeClass('attache-dragover');
340
+ },
341
+ setFileValue: function setFileValue(key, value) {
342
+ this.state.files[key] = value;
343
+ this.setState(this.state);
344
+ },
345
+ render: function render() {
346
+ var that = this;
347
+ var Header = window.AttacheHeader || _bootstrap.Bootstrap3Header;
348
+ var FilePreview = window.AttacheFilePreview || _bootstrap.Bootstrap3FilePreview;
349
+ var Placeholder = window.AttachePlaceholder || _bootstrap.Bootstrap3Placeholder;
350
+
351
+ if (that.state.uploading > 0) {
352
+ that.state.submit_buttons.attr('disabled', true);
353
+ } else if (that.state.submit_buttons) {
354
+ that.state.submit_buttons.attr('disabled', null);
355
+ }
356
+
357
+ var previews = [];
358
+ $.each(that.state.files, function (key, result) {
359
+ // json is input[value], drop non essential values
360
+ var copy = JSON.parse(JSON.stringify(result));
361
+ delete copy.src;
362
+ delete copy.filename;
363
+ var json = JSON.stringify(copy);
364
+ //
365
+ result.multiple = that.props.multiple;
366
+ if (result.path) {
367
+ var parts = result.path.split('/');
368
+ result.filename = parts.pop().split(/[#?]/).shift();
369
+ parts.push(encodeURIComponent(that.props['data-geometry'] || '128x128#'));
370
+ parts.push(encodeURIComponent(result.filename));
371
+ result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
372
+ }
373
+ var previewKey = 'preview' + key;
374
+ previews.push(React.createElement(
375
+ 'div',
376
+ { key: previewKey, className: 'attache-file-input' },
377
+ React.createElement('input', {
378
+ type: 'hidden',
379
+ name: that.props.name,
380
+ value: json,
381
+ readOnly: 'true' }),
382
+ React.createElement(FilePreview, _extends({}, result, { key: key, onRemove: that.onRemove.bind(that, key) }))
383
+ ));
384
+ });
385
+
386
+ var placeholders = [];
387
+ if (previews.length === 0 && that.props['data-placeholder']) {
388
+ $.each(JSON.parse(that.props['data-placeholder']), function (uid, src) {
389
+ placeholders.push(React.createElement(Placeholder, _extends({ key: 'placeholder' }, that.props, { src: src })));
390
+ });
391
+ }
392
+
393
+ var discards = [];
394
+ $.each(that.state.attaches_discarded, function (index, discard) {
395
+ var discardKey = 'discard' + discard.path;
396
+ discards.push(React.createElement('input', {
397
+ key: discardKey,
398
+ type: 'hidden',
399
+ name: discard.fieldname,
400
+ value: discard.path }));
401
+ });
402
+
403
+ var className = ['attache-file-selector', 'attache-placeholders-count-' + placeholders.length, 'attache-previews-count-' + previews.length, this.props['data-classname']].join(' ').trim();
404
+ return React.createElement(
405
+ 'label',
406
+ {
407
+ htmlFor: that.props.id,
408
+ className: className,
409
+ onDragOver: this.onDragOver,
410
+ onDragLeave: this.onDragLeave,
411
+ onDrop: this.onDrop },
412
+ React.createElement('input', _extends({ type: 'file' }, that.props, { onChange: this.onChange })),
413
+ React.createElement('input', { type: 'hidden', name: that.props.name, value: '' }),
414
+ React.createElement(Header, that.props),
415
+ previews,
416
+ placeholders,
417
+ discards
418
+ );
419
+ }
420
+ });
421
+
422
+ },{"./bootstrap3":2,"./cors_upload":3}]},{},[1])
423
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy5udm0vdjQuMS4wL2xpYi9ub2RlX21vZHVsZXMvYnJvd3NlcmlmeS9ub2RlX21vZHVsZXMvYnJvd3Nlci1wYWNrL19wcmVsdWRlLmpzIiwic3JjL2phdmFzY3JpcHRzL2F0dGFjaGUuanMiLCJzcmMvamF2YXNjcmlwdHMvYXR0YWNoZS9ib290c3RyYXAzLmpzIiwic3JjL2phdmFzY3JpcHRzL2F0dGFjaGUvY29yc191cGxvYWQuanMiLCJzcmMvamF2YXNjcmlwdHMvYXR0YWNoZS9maWxlX2lucHV0LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7OztBQ01BLElBQUksZ0JBQWdCLEdBQUcsU0FBbkIsZ0JBQWdCLEdBQWU7QUFDakMsTUFBSSxTQUFTLEdBQUcsRUFBRSxPQUFPLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsQ0FBQTtBQUMxRCxNQUFJLEdBQUcsR0FBRyxRQUFRLENBQUMsc0JBQXNCLENBQUMsZ0JBQWdCLENBQUMsQ0FBQTtBQUMzRCxNQUFJLEdBQUcsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQTtBQUMzQixPQUFLLElBQUksQ0FBQyxHQUFHLEdBQUcsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxFQUFFLEVBQUU7QUFDeEMsT0FBRyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQTtBQUNaLFNBQUssR0FBRyxFQUFFLENBQUE7QUFDVixTQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDLFVBQVUsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxFQUFFLEVBQUU7QUFDOUMsVUFBSSxHQUFHLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFBO0FBQzdCLFdBQUssR0FBRyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQTtBQUMvQixVQUFJLElBQUks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Q0FBQyxXQUFXLEFBQUM7QUFDOUIsY0FBTSxFQUFFLElBQUksQ0FBQyxNQUFNLEFBQUM7TUFDcEIsd0NBQU8sSUFBSSxFQUFDLE1BQU0sSUFBSyxJQUFJLENBQUMsS0FBSyxJQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxBQUFDLElBQUc7TUFDOUQsK0JBQU8sSUFBSSxFQUFDLFFBQVEsRUFBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEFBQUMsRUFBQyxLQUFLLEVBQUMsRUFBRSxHQUFHO01BQ3ZELG9CQUFDLE1BQU0sRUFBSyxJQUFJLENBQUMsS0FBSyxDQUFJO01BQ3pCLFFBQVE7TUFDUixZQUFZO01BQ1osUUFBUTtLQUNILENBQ1A7R0FDRjtDQUNGLENBQUMsQ0FBQSIsImZpbGUiOiJnZW5lcmF0ZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlc0NvbnRlbnQiOlsiKGZ1bmN0aW9uIGUodCxuLHIpe2Z1bmN0aW9uIHMobyx1KXtpZighbltvXSl7aWYoIXRbb10pe3ZhciBhPXR5cGVvZiByZXF1aXJlPT1cImZ1bmN0aW9uXCImJnJlcXVpcmU7aWYoIXUmJmEpcmV0dXJuIGEobywhMCk7aWYoaSlyZXR1cm4gaShvLCEwKTt2YXIgZj1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK28rXCInXCIpO3Rocm93IGYuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixmfXZhciBsPW5bb109e2V4cG9ydHM6e319O3Rbb11bMF0uY2FsbChsLmV4cG9ydHMsZnVuY3Rpb24oZSl7dmFyIG49dFtvXVsxXVtlXTtyZXR1cm4gcyhuP246ZSl9LGwsbC5leHBvcnRzLGUsdCxuLHIpfXJldHVybiBuW29dLmV4cG9ydHN9dmFyIGk9dHlwZW9mIHJlcXVpcmU9PVwiZnVuY3Rpb25cIiYmcmVxdWlyZTtmb3IodmFyIG89MDtvPHIubGVuZ3RoO28rKylzKHJbb10pO3JldHVybiBzfSkiLCIvKmdsb2JhbCAkKi9cbi8qZ2xvYmFsIFJlYWN0Ki9cbi8qZ2xvYmFsIFJlYWN0RE9NKi9cblxuaW1wb3J0IHsgQXR0YWNoZUZpbGVJbnB1dCB9IGZyb20gJy4vYXR0YWNoZS9maWxlX2lucHV0J1xuXG52YXIgdXBncmFkZUZpbGVJbnB1dCA9IGZ1bmN0aW9uICgpIHtcbiAgdmFyIHNhZmVXb3JkcyA9IHsgJ2NsYXNzJzogJ2NsYXNzTmFtZScsICdmb3InOiAnaHRtbEZvcicgfVxuICB2YXIgc2VsID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnZW5hYmxlLWF0dGFjaGUnKVxuICB2YXIgZWxlLCBhdHRycywgbmFtZSwgdmFsdWVcbiAgZm9yICh2YXIgaSA9IHNlbC5sZW5ndGggLSAxOyBpID49IDA7IGktLSkge1xuICAgIGVsZSA9IHNlbFtpXVxuICAgIGF0dHJzID0ge31cbiAgICBmb3IgKHZhciBqID0gMDsgaiA8IGVsZS5hdHRyaWJ1dGVzLmxlbmd0aDsgaisrKSB7XG4gICAgICBuYW1lID0gZWxlLmF0dHJpYnV0ZXNbal0ubmFtZVxuICAgICAgdmFsdWUgPSBlbGUuYXR0cmlidXRlc1tqXS52YWx1ZVxuICAgICAgaWYgKG5hbWUgPT09ICdjbGFzcycpIHZhbHVlID0gdmFsdWUucmVwbGFjZSgnZW5hYmxlLWF0dGFjaGUnLCAnYXR0YWNoZS1lbmFibGVkJylcbiAgICAgIG5hbWUgPSBzYWZlV29yZHNbbmFtZV0gfHwgbmFtZVxuICAgICAgYXR0cnNbbmFtZV0gPSB2YWx1ZVxuICAgIH1cbiAgICB2YXIgd3JhcCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2RpdicpXG4gICAgZWxlLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKHdyYXAsIGVsZSlcbiAgICBSZWFjdERPTS5yZW5kZXIoUmVhY3QuY3JlYXRlRWxlbWVudChBdHRhY2hlRmlsZUlucHV0LCBSZWFjdC5fX3NwcmVhZCh7fSwgYXR0cnMpKSwgd3JhcClcbiAgfVxufVxuXG4kKGRvY3VtZW50KS5vbigncGFnZTpjaGFuZ2UnLCB1cGdyYWRlRmlsZUlucHV0KVxuJCh1cGdyYWRlRmlsZUlucHV0KVxuIiwiLypnbG9iYWwgJCovXG4vKmdsb2JhbCBSZWFjdCovXG5cbmV4cG9ydCB2YXIgQm9vdHN0cmFwM0ZpbGVQcmV2aWV3ID0gUmVhY3QuY3JlYXRlQ2xhc3Moe1xuICBnZXRJbml0aWFsU3RhdGUgKCkge1xuICAgIHJldHVybiB7IHNyY1dhczogJycgfVxuICB9LFxuXG4gIG9uU3JjTG9hZGVkIChldmVudCkge1xuICAgIHRoaXMuc2V0U3RhdGUoeyBzcmNXYXM6IHRoaXMucHJvcHMuc3JjIH0pXG4gICAgJChldmVudC50YXJnZXQpLnRyaWdnZXIoJ2F0dGFjaGU6aW1nbG9hZCcpXG4gIH0sXG5cbiAgb25TcmNFcnJvciAoZXZlbnQpIHtcbiAgICAkKGV2ZW50LnRhcmdldCkudHJpZ2dlcignYXR0YWNoZTppbWdlcnJvcicpXG4gIH0sXG5cbiAgcmVuZGVyICgpIHtcbiAgICB2YXIgcHJldmlld0NsYXNzTmFtZSA9ICdhdHRhY2hlLWZpbGUtcHJldmlldydcblxuICAgIC8vIHByb2dyZXNzYmFyXG4gICAgaWYgKHRoaXMuc3RhdGUuc3JjV2FzICE9PSB0aGlzLnByb3BzLnNyYykge1xuICAgICAgcHJldmlld0NsYXNzTmFtZSA9IHByZXZpZXdDbGFzc05hbWUgKyAnIGF0dGFjaGUtbG9hZGluZydcbiAgICAgIHZhciBjbGFzc05hbWUgPSB0aGlzLnByb3BzLmNsYXNzTmFtZSB8fCAncHJvZ3Jlc3MtYmFyIHByb2dyZXNzLWJhci1zdHJpcGVkIGFjdGl2ZScgKyAodGhpcy5wcm9wcy5zcmMgPyAnIHByb2dyZXNzLWJhci1zdWNjZXNzJyA6ICcnKVxuICAgICAgdmFyIHBjdFN0cmluZyA9IHRoaXMucHJvcHMucGN0U3RyaW5nIHx8ICh0aGlzLnByb3BzLnNyYyA/IDEwMCA6IHRoaXMucHJvcHMucGVyY2VudExvYWRlZCkgKyAnJSdcbiAgICAgIHZhciBwY3REZXNjID0gdGhpcy5wcm9wcy5wY3REZXNjIHx8ICh0aGlzLnByb3BzLnNyYyA/ICdMb2FkaW5nLi4uJyA6IHBjdFN0cmluZylcbiAgICAgIHZhciBwY3RTdHlsZSA9IHsgd2lkdGg6IHBjdFN0cmluZywgbWluV2lkdGg6ICczZW0nIH1cbiAgICAgIHZhciBwcm9ncmVzcyA9IChcbiAgICAgIDxkaXYgY2xhc3NOYW1lPVwicHJvZ3Jlc3NcIj5cbiAgICAgICAgPGRpdlxuICAgICAgICAgIGNsYXNzTmFtZT17Y2xhc3NOYW1lfVxuICAgICAgICAgIHJvbGU9XCJwcm9ncmVzc2JhclwiXG4gICAgICAgICAgYXJpYS12YWx1ZW5vdz17dGhpcy5wcm9wcy5wZXJjZW50TG9hZGVkfVxuICAgICAgICAgIGFyaWEtdmFsdWVtaW49XCIwXCJcbiAgICAgICAgICBhcmlhLXZhbHVlbWF4PVwiMTAwXCJcbiAgICAgICAgICBzdHlsZT17cGN0U3R5bGV9PlxuICAgICAgICAgIHtwY3REZXNjfVxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIGltZyB0YWdcbiAgICBpZiAodGhpcy5wcm9wcy5zcmMpIHtcbiAgICAgIHZhciBpbWcgPSA8aW1nIHNyYz17dGhpcy5wcm9wcy5zcmN9IG9uTG9hZD17dGhpcy5vblNyY0xvYWRlZH0gb25FcnJvcj17dGhpcy5vblNyY0Vycm9yfSAvPlxuICAgIH1cblxuICAgIC8vIGNvbWJpbmVkXG4gICAgcmV0dXJuIChcbiAgICA8ZGl2IGNsYXNzTmFtZT17cHJldmlld0NsYXNzTmFtZX0+XG4gICAgICB7cHJvZ3Jlc3N9XG4gICAgICB7aW1nfVxuICAgICAgPGRpdiBjbGFzc05hbWU9XCJjbGVhcmZpeFwiPlxuICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cInB1bGwtbGVmdFwiPlxuICAgICAgICAgIHt0aGlzLnByb3BzLmZpbGVuYW1lfVxuICAgICAgICA8L2Rpdj5cbiAgICAgICAgPGFcbiAgICAgICAgICBocmVmPVwiI3JlbW92ZVwiXG4gICAgICAgICAgY2xhc3NOYW1lPVwicHVsbC1yaWdodFwiXG4gICAgICAgICAgb25DbGljaz17dGhpcy5wcm9wcy5vblJlbW92ZX1cbiAgICAgICAgICB0aXRsZT1cIkNsaWNrIHRvIHJlbW92ZVwiPiZ0aW1lczs8L2E+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgICApXG4gIH1cbn0pXG5cbmV4cG9ydCB2YXIgQm9vdHN0cmFwM1BsYWNlaG9sZGVyID0gUmVhY3QuY3JlYXRlQ2xhc3Moe1xuICByZW5kZXIgKCkge1xuICAgIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJhdHRhY2hlLWZpbGUtcHJldmlld1wiPlxuICAgICAgPGltZyBzcmM9e3RoaXMucHJvcHMuc3JjfSAvPlxuICAgIDwvZGl2PlxuICAgIClcbiAgfVxufSlcblxuZXhwb3J0IHZhciBCb290c3RyYXAzSGVhZGVyID0gUmVhY3QuY3JlYXRlQ2xhc3Moe1xuICByZW5kZXIgKCkge1xuICAgIHJldHVybiAoXG4gICAgPG5vc2NyaXB0IC8+XG4gICAgKVxuICB9XG59KVxuIiwiLypnbG9iYWwgJCovXG4vKmdsb2JhbCBhbGVydCovXG4vKmdsb2JhbCBYTUxIdHRwUmVxdWVzdCovXG4vKmdsb2JhbCBYRG9tYWluUmVxdWVzdCovXG5cbnZhciBjb3VudGVyID0gMFxuXG5leHBvcnQgY2xhc3MgQ09SU1VwbG9hZCB7XG4gIGNvbnN0cnVjdG9yIChvcHRpb25zKSB7XG4gICAgaWYgKG9wdGlvbnMgPT0gbnVsbCkgb3B0aW9ucyA9IHt9XG4gICAgdmFyIG9wdGlvblxuICAgIGZvciAob3B0aW9uIGluIG9wdGlvbnMpIHtcbiAgICAgIHRoaXNbb3B0aW9uXSA9IG9wdGlvbnNbb3B0aW9uXVxuICAgIH1cbiAgfVxuXG4gIC8vIGZvciBvdmVyd3JpdGluZ1xuICBjcmVhdGVMb2NhbFRodW1ibmFpbCAoKSB7IH1cbiAgb25Db21wbGV0ZSAodWlkLCBqc29uKSB7IH1cbiAgb25Qcm9ncmVzcyAodWlkLCBqc29uKSB7IH1cbiAgb25FcnJvciAodWlkLCBzdGF0dXMpIHsgYWxlcnQoc3RhdHVzKSB9XG5cbiAgaGFuZGxlRmlsZVNlbGVjdCAoKSB7XG4gICAgdmFyIGYsIF9pLCBfbGVuLCBfcmVzdWx0cywgdXJsLCAkZWxlLCBwcmVmaXhcbiAgICAkZWxlID0gJCh0aGlzLmZpbGVfZWxlbWVudClcbiAgICB1cmwgPSAkZWxlLmRhdGEoJ3VwbG9hZHVybCcpXG4gICAgaWYgKCRlbGUuZGF0YSgnaG1hYycpKSB7XG4gICAgICB1cmwgPSB1cmwgK1xuICAgICAgICAnP2htYWM9JyArIGVuY29kZVVSSUNvbXBvbmVudCgkZWxlLmRhdGEoJ2htYWMnKSkgK1xuICAgICAgICAnJnV1aWQ9JyArIGVuY29kZVVSSUNvbXBvbmVudCgkZWxlLmRhdGEoJ3V1aWQnKSkgK1xuICAgICAgICAnJmV4cGlyYXRpb249JyArIGVuY29kZVVSSUNvbXBvbmVudCgkZWxlLmRhdGEoJ2V4cGlyYXRpb24nKSkgK1xuICAgICAgICAnJ1xuICAgIH1cblxuICAgIHByZWZpeCA9IERhdGUubm93KCkgKyAnXydcbiAgICBfcmVzdWx0cyA9IFtdXG4gICAgZm9yIChfaSA9IDAsIF9sZW4gPSB0aGlzLmZpbGVzLmxlbmd0aDsgX2kgPCBfbGVuOyBfaSsrKSB7XG4gICAgICBmID0gdGhpcy5maWxlc1tfaV1cbiAgICAgIHRoaXMuY3JlYXRlTG9jYWxUaHVtYm5haWwoZikgLy8gaWYgYW55XG4gICAgICBmLnVpZCA9IHByZWZpeCArIChjb3VudGVyKyspXG4gICAgICB0aGlzLm9uUHJvZ3Jlc3MoZi51aWQsIHsgc3JjOiBmLnNyYywgZmlsZW5hbWU6IGYubmFtZSwgcGVyY2VudExvYWRlZDogMCwgYnl0ZXNMb2FkZWQ6IDAsIGJ5dGVzVG90YWw6IGYuc2l6ZSB9KVxuICAgICAgX3Jlc3VsdHMucHVzaCh0aGlzLnBlcmZvcm1VcGxvYWQoZiwgdXJsKSlcbiAgICB9XG4gICAgcmV0dXJuIF9yZXN1bHRzXG4gIH1cblxuICBjcmVhdGVDT1JTUmVxdWVzdCAobWV0aG9kLCB1cmwpIHtcbiAgICB2YXIgeGhyXG4gICAgeGhyID0gbmV3IFhNTEh0dHBSZXF1ZXN0KClcbiAgICBpZiAoeGhyLndpdGhDcmVkZW50aWFscyAhPSBudWxsKSB7XG4gICAgICB4aHIub3BlbihtZXRob2QsIHVybCwgdHJ1ZSlcbiAgICB9IGVsc2UgaWYgKHR5cGVvZiBYRG9tYWluUmVxdWVzdCAhPT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgIHhociA9IG5ldyBYRG9tYWluUmVxdWVzdCgpXG4gICAgICB4aHIub3BlbihtZXRob2QsIHVybClcbiAgICB9IGVsc2Uge1xuICAgICAgeGhyID0gbnVsbFxuICAgIH1cbiAgICByZXR1cm4geGhyXG4gIH1cblxuICBwZXJmb3JtVXBsb2FkIChmaWxlLCB1cmwpIHtcbiAgICB2YXIgdGhpc19zM3VwbG9hZCwgeGhyXG4gICAgdGhpc19zM3VwbG9hZCA9IHRoaXNcbiAgICB1cmwgPSB1cmwgKyAodXJsLmluZGV4T2YoJz8nKSA9PT0gLTEgPyAnPycgOiAnJicpICsgJ2ZpbGU9JyArIGVuY29kZVVSSUNvbXBvbmVudChmaWxlLm5hbWUpXG4gICAgeGhyID0gdGhpcy5jcmVhdGVDT1JTUmVxdWVzdCgnUFVUJywgdXJsKVxuICAgIGlmICgheGhyKSB7XG4gICAgICB0aGlzLm9uRXJyb3IoZmlsZS51aWQsICdDT1JTIG5vdCBzdXBwb3J0ZWQnKVxuICAgIH0gZWxzZSB7XG4gICAgICB4aHIub25sb2FkID0gZnVuY3Rpb24gKGUpIHtcbiAgICAgICAgaWYgKHhoci5zdGF0dXMgPT09IDIwMCkge1xuICAgICAgICAgIHRoaXNfczN1cGxvYWQub25Db21wbGV0ZShmaWxlLnVpZCwgSlNPTi5wYXJzZShlLnRhcmdldC5yZXNwb25zZVRleHQpKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiB0aGlzX3MzdXBsb2FkLm9uRXJyb3IoZmlsZS51aWQsIHhoci5zdGF0dXMgKyAnICcgKyB4aHIuc3RhdHVzVGV4dClcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgeGhyLm9uZXJyb3IgPSBmdW5jdGlvbiAoKSB7XG4gICAgICAgIHJldHVybiB0aGlzX3MzdXBsb2FkLm9uRXJyb3IoZmlsZS51aWQsICdVbmFibGUgdG8gcmVhY2ggc2VydmVyJylcbiAgICAgIH1cbiAgICAgIHhoci51cGxvYWQub25wcm9ncmVzcyA9IGZ1bmN0aW9uIChlKSB7XG4gICAgICAgIHZhciBwZXJjZW50TG9hZGVkXG4gICAgICAgIGlmIChlLmxlbmd0aENvbXB1dGFibGUpIHtcbiAgICAgICAgICBwZXJjZW50TG9hZGVkID0gTWF0aC5yb3VuZCgoZS5sb2FkZWQgLyBlLnRvdGFsKSAqIDEwMClcbiAgICAgICAgICByZXR1cm4gdGhpc19zM3VwbG9hZC5vblByb2dyZXNzKGZpbGUudWlkLCB7IHNyYzogZmlsZS5zcmMsIGZpbGVuYW1lOiBmaWxlLm5hbWUsIHBlcmNlbnRMb2FkZWQ6IHBlcmNlbnRMb2FkZWQsIGJ5dGVzTG9hZGVkOiBlLmxvYWRlZCwgYnl0ZXNUb3RhbDogZS50b3RhbCB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB4aHIuc2VuZChmaWxlKVxuICB9XG59XG4iLCIvKmdsb2JhbCAkKi9cbi8qZ2xvYmFsIHdpbmRvdyovXG4vKmdsb2JhbCBSZWFjdCovXG4vKmdsb2JhbCBSZWFjdERPTSovXG5cbmltcG9ydCB7IENPUlNVcGxvYWQgfSBmcm9tICcuL2NvcnNfdXBsb2FkJ1xuaW1wb3J0IHsgQm9vdHN0cmFwM0hlYWRlciwgQm9vdHN0cmFwM0ZpbGVQcmV2aWV3LCBCb290c3RyYXAzUGxhY2Vob2xkZXIgfSBmcm9tICcuL2Jvb3RzdHJhcDMnXG5cbmV4cG9ydCB2YXIgQXR0YWNoZUZpbGVJbnB1dCA9IFJlYWN0LmNyZWF0ZUNsYXNzKHtcbiAgZ2V0SW5pdGlhbFN0YXRlICgpIHtcbiAgICB2YXIgZmlsZXMgPSB7fVxuICAgIGlmICh0aGlzLnByb3BzWydkYXRhLXZhbHVlJ10pIHtcbiAgICAgICQuZWFjaChKU09OLnBhcnNlKHRoaXMucHJvcHNbJ2RhdGEtdmFsdWUnXSksIGZ1bmN0aW9uICh1aWQsIGpzb24pIHtcbiAgICAgICAgaWYgKGpzb24pIGZpbGVzW3VpZF0gPSBqc29uXG4gICAgICB9KVxuICAgIH1cbiAgICByZXR1cm4geyBmaWxlczogZmlsZXMsIGF0dGFjaGVzX2Rpc2NhcmRlZDogW10sIHVwbG9hZGluZzogMCB9XG4gIH0sXG5cbiAgb25SZW1vdmUgKHVpZCwgZSkge1xuICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgIGUuc3RvcFByb3BhZ2F0aW9uKClcblxuICAgIHZhciBmaWVsZG5hbWUgPSBSZWFjdERPTS5maW5kRE9NTm9kZSh0aGlzKS5maXJzdENoaWxkLm5hbWUgLy8gd2hlbiAgICd1c2VyW2F2YXRhcl0nXG4gICAgdmFyIG5ld2ZpZWxkID0gZmllbGRuYW1lLnJlcGxhY2UoL1xcdytcXF0oXFxbXFxdfCkkLywgJ2F0dGFjaGVzX2Rpc2NhcmRlZF1bXScpIC8vIGJlY29tZSAndXNlclthdHRhY2hlc19kaXNjYXJkZWRdW10nXG5cbiAgICB0aGlzLnN0YXRlLmF0dGFjaGVzX2Rpc2NhcmRlZC5wdXNoKHsgZmllbGRuYW1lOiBuZXdmaWVsZCwgcGF0aDogdGhpcy5zdGF0ZS5maWxlc1t1aWRdLnBhdGggfSlcbiAgICBkZWxldGUgdGhpcy5zdGF0ZS5maWxlc1t1aWRdXG5cbiAgICB0aGlzLnNldFN0YXRlKHRoaXMuc3RhdGUpXG4gIH0sXG5cbiAgcGVyZm9ybVVwbG9hZCAoZmlsZV9lbGVtZW50LCBmaWxlcykge1xuICAgIC8vIHVzZXIgY2FuY2VsbGVkIGZpbGUgY2hvb3NlciBkaWFsb2cuIGlnbm9yZVxuICAgIGlmICghZmlsZXMgfHwgZmlsZXMubGVuZ3RoID09PSAwKSByZXR1cm5cbiAgICBpZiAoIXRoaXMucHJvcHMubXVsdGlwbGUpIHtcbiAgICAgIHRoaXMuc3RhdGUuZmlsZXMgPSB7fVxuICAgICAgZmlsZXMgPSBbZmlsZXNbMF1dIC8vIGFycmF5IG9mIDEgZWxlbWVudFxuICAgIH1cblxuICAgIHRoaXMuc2V0U3RhdGUodGhpcy5zdGF0ZSlcbiAgICAvLyB1cGxvYWQgdGhlIGZpbGUgdmlhIENPUlNcbiAgICB2YXIgdGhhdCA9IHRoaXNcblxuICAgIHRoYXQuc3RhdGUudXBsb2FkaW5nID0gdGhhdC5zdGF0ZS51cGxvYWRpbmcgKyBmaWxlcy5sZW5ndGhcbiAgICBpZiAoIXRoYXQuc3RhdGUuc3VibWl0X2J1dHRvbnMpIHRoYXQuc3RhdGUuc3VibWl0X2J1dHRvbnMgPSAkKFwiYnV0dG9uLGlucHV0W3R5cGU9J3N1Ym1pdCddXCIsICQoZmlsZV9lbGVtZW50KS5wYXJlbnRzKCdmb3JtJylbMF0pLmZpbHRlcignOm5vdCg6ZGlzYWJsZWQpJylcblxuICAgIHZhciB1cGxvYWQgPSBuZXcgQ09SU1VwbG9hZCh7XG4gICAgICBmaWxlX2VsZW1lbnQ6IGZpbGVfZWxlbWVudCxcbiAgICAgIGZpbGVzOiBmaWxlcyxcbiAgICAgIG9uUHJvZ3Jlc3M6IHRoaXMuc2V0RmlsZVZhbHVlLFxuICAgICAgb25Db21wbGV0ZSAoKSB7XG4gICAgICAgIHRoYXQuc3RhdGUudXBsb2FkaW5nLS1cbiAgICAgICAgdGhhdC5zZXRGaWxlVmFsdWUuYXBwbHkodGhpcywgYXJndW1lbnRzKVxuICAgICAgfSxcbiAgICAgIG9uRXJyb3IgKHVpZCwgc3RhdHVzKSB7XG4gICAgICAgIHRoYXQuc3RhdGUudXBsb2FkaW5nLS1cbiAgICAgICAgdGhhdC5zZXRGaWxlVmFsdWUodWlkLCB7IHBjdFN0cmluZzogJzkwJScsIHBjdERlc2M6IHN0YXR1cywgY2xhc3NOYW1lOiAncHJvZ3Jlc3MtYmFyIHByb2dyZXNzLWJhci1kYW5nZXInIH0pXG4gICAgICB9XG4gICAgfSlcbiAgICB1cGxvYWQuaGFuZGxlRmlsZVNlbGVjdCgpXG5cbiAgICAvLyB3ZSBkb24ndCB3YW50IHRoZSBmaWxlIGJpbmFyeSB0byBiZSB1cGxvYWRlZCBpbiB0aGUgbWFpbiBmb3JtXG4gICAgLy8gc28gdGhlIGFjdHVhbCBmaWxlIGlucHV0IGlzIG5ldXRlcmVkXG4gICAgZmlsZV9lbGVtZW50LnZhbHVlID0gJydcbiAgfSxcblxuICBvbkNoYW5nZSAoKSB7XG4gICAgdmFyIGZpbGVfZWxlbWVudCA9IFJlYWN0RE9NLmZpbmRET01Ob2RlKHRoaXMpLmZpcnN0Q2hpbGRcbiAgICB0aGlzLnBlcmZvcm1VcGxvYWQoZmlsZV9lbGVtZW50LCBmaWxlX2VsZW1lbnQgJiYgZmlsZV9lbGVtZW50LmZpbGVzKVxuICB9LFxuXG4gIG9uRHJhZ092ZXIgKGUpIHtcbiAgICBlLnN0b3BQcm9wYWdhdGlvbigpXG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgJChSZWFjdERPTS5maW5kRE9NTm9kZSh0aGlzKSkuYWRkQ2xhc3MoJ2F0dGFjaGUtZHJhZ292ZXInKVxuICB9LFxuXG4gIG9uRHJhZ0xlYXZlIChlKSB7XG4gICAgZS5zdG9wUHJvcGFnYXRpb24oKVxuICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICQoUmVhY3RET00uZmluZERPTU5vZGUodGhpcykpLnJlbW92ZUNsYXNzKCdhdHRhY2hlLWRyYWdvdmVyJylcbiAgfSxcblxuICBvbkRyb3AgKGUpIHtcbiAgICBlLnN0b3BQcm9wYWdhdGlvbigpXG4gICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgdmFyIGZpbGVfZWxlbWVudCA9IFJlYWN0RE9NLmZpbmRET01Ob2RlKHRoaXMpLmZpcnN0Q2hpbGRcbiAgICB0aGlzLnBlcmZvcm1VcGxvYWQoZmlsZV9lbGVtZW50LCBlLnRhcmdldC5maWxlcyB8fCBlLmRhdGFUcmFuc2Zlci5maWxlcylcbiAgICAkKFJlYWN0RE9NLmZpbmRET01Ob2RlKHRoaXMpKS5yZW1vdmVDbGFzcygnYXR0YWNoZS1kcmFnb3ZlcicpXG4gIH0sXG5cbiAgc2V0RmlsZVZhbHVlIChrZXksIHZhbHVlKSB7XG4gICAgdGhpcy5zdGF0ZS5maWxlc1trZXldID0gdmFsdWVcbiAgICB0aGlzLnNldFN0YXRlKHRoaXMuc3RhdGUpXG4gIH0sXG5cbiAgcmVuZGVyICgpIHtcbiAgICB2YXIgdGhhdCA9IHRoaXNcbiAgICB2YXIgSGVhZGVyID0gd2luZG93LkF0dGFjaGVIZWFkZXIgfHwgQm9vdHN0cmFwM0hlYWRlclxuICAgIHZhciBGaWxlUHJldmlldyA9IHdpbmRvdy5BdHRhY2hlRmlsZVByZXZpZXcgfHwgQm9vdHN0cmFwM0ZpbGVQcmV2aWV3XG4gICAgdmFyIFBsYWNlaG9sZGVyID0gd2luZG93LkF0dGFjaGVQbGFjZWhvbGRlciB8fCBCb290c3RyYXAzUGxhY2Vob2xkZXJcblxuICAgIGlmICh0aGF0LnN0YXRlLnVwbG9hZGluZyA+IDApIHtcbiAgICAgIHRoYXQuc3RhdGUuc3VibWl0X2J1dHRvbnMuYXR0cignZGlzYWJsZWQnLCB0cnVlKVxuICAgIH0gZWxzZSBpZiAodGhhdC5zdGF0ZS5zdWJtaXRfYnV0dG9ucykge1xuICAgICAgdGhhdC5zdGF0ZS5zdWJtaXRfYnV0dG9ucy5hdHRyKCdkaXNhYmxlZCcsIG51bGwpXG4gICAgfVxuXG4gICAgdmFyIHByZXZpZXdzID0gW11cbiAgICAkLmVhY2godGhhdC5zdGF0ZS5maWxlcywgZnVuY3Rpb24gKGtleSwgcmVzdWx0KSB7XG4gICAgICAvLyBqc29uIGlzIGlucHV0W3ZhbHVlXSwgZHJvcCBub24gZXNzZW50aWFsIHZhbHVlc1xuICAgICAgdmFyIGNvcHkgPSBKU09OLnBhcnNlKEpTT04uc3RyaW5naWZ5KHJlc3VsdCkpXG4gICAgICBkZWxldGUgY29weS5zcmNcbiAgICAgIGRlbGV0ZSBjb3B5LmZpbGVuYW1lXG4gICAgICB2YXIganNvbiA9IEpTT04uc3RyaW5naWZ5KGNvcHkpXG4gICAgICAvL1xuICAgICAgcmVzdWx0Lm11bHRpcGxlID0gdGhhdC5wcm9wcy5tdWx0aXBsZVxuICAgICAgaWYgKHJlc3VsdC5wYXRoKSB7XG4gICAgICAgIHZhciBwYXJ0cyA9IHJlc3VsdC5wYXRoLnNwbGl0KCcvJylcbiAgICAgICAgcmVzdWx0LmZpbGVuYW1lID0gcGFydHMucG9wKCkuc3BsaXQoL1sjP10vKS5zaGlmdCgpXG4gICAgICAgIHBhcnRzLnB1c2goZW5jb2RlVVJJQ29tcG9uZW50KHRoYXQucHJvcHNbJ2RhdGEtZ2VvbWV0cnknXSB8fCAnMTI4eDEyOCMnKSlcbiAgICAgICAgcGFydHMucHVzaChlbmNvZGVVUklDb21wb25lbnQocmVzdWx0LmZpbGVuYW1lKSlcbiAgICAgICAgcmVzdWx0LnNyYyA9IHRoYXQucHJvcHNbJ2RhdGEtZG93bmxvYWR1cmwnXSArICcvJyArIHBhcnRzLmpvaW4oJy8nKVxuICAgICAgfVxuICAgICAgdmFyIHByZXZpZXdLZXkgPSAncHJldmlldycgKyBrZXlcbiAgICAgIHByZXZpZXdzLnB1c2goXG4gICAgICAgIDxkaXYga2V5PXtwcmV2aWV3S2V5fSBjbGFzc05hbWU9XCJhdHRhY2hlLWZpbGUtaW5wdXRcIj5cbiAgICAgICAgICA8aW5wdXRcbiAgICAgICAgICAgIHR5cGU9XCJoaWRkZW5cIlxuICAgICAgICAgICAgbmFtZT17dGhhdC5wcm9wcy5uYW1lfVxuICAgICAgICAgICAgdmFsdWU9e2pzb259XG4gICAgICAgICAgICByZWFkT25seT1cInRydWVcIiAvPlxuICAgICAgICAgIDxGaWxlUHJldmlldyB7Li4ucmVzdWx0fSBrZXk9e2tleX0gb25SZW1vdmU9e3RoYXQub25SZW1vdmUuYmluZCh0aGF0LCBrZXkpfSAvPlxuICAgICAgICA8L2Rpdj5cbiAgICAgIClcbiAgICB9KVxuXG4gICAgdmFyIHBsYWNlaG9sZGVycyA9IFtdXG4gICAgaWYgKHByZXZpZXdzLmxlbmd0aCA9PT0gMCAmJiB0aGF0LnByb3BzWydkYXRhLXBsYWNlaG9sZGVyJ10pIHtcbiAgICAgICQuZWFjaChKU09OLnBhcnNlKHRoYXQucHJvcHNbJ2RhdGEtcGxhY2Vob2xkZXInXSksIGZ1bmN0aW9uICh1aWQsIHNyYykge1xuICAgICAgICBwbGFjZWhvbGRlcnMucHVzaChcbiAgICAgICAgICA8UGxhY2Vob2xkZXIga2V5PVwicGxhY2Vob2xkZXJcIiB7Li4udGhhdC5wcm9wc30gc3JjPXtzcmN9IC8+XG4gICAgICAgIClcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgdmFyIGRpc2NhcmRzID0gW11cbiAgICAkLmVhY2godGhhdC5zdGF0ZS5hdHRhY2hlc19kaXNjYXJkZWQsIGZ1bmN0aW9uIChpbmRleCwgZGlzY2FyZCkge1xuICAgICAgdmFyIGRpc2NhcmRLZXkgPSAnZGlzY2FyZCcgKyBkaXNjYXJkLnBhdGhcbiAgICAgIGRpc2NhcmRzLnB1c2goXG4gICAgICAgIDxpbnB1dFxuICAgICAgICAgIGtleT17ZGlzY2FyZEtleX1cbiAgICAgICAgICB0eXBlPVwiaGlkZGVuXCJcbiAgICAgICAgICBuYW1lPXtkaXNjYXJkLmZpZWxkbmFtZX1cbiAgICAgICAgICB2YWx1ZT17ZGlzY2FyZC5wYXRofSAvPlxuICAgICAgKVxuICAgIH0pXG5cbiAgICB2YXIgY2xhc3NOYW1lID0gWydhdHRhY2hlLWZpbGUtc2VsZWN0b3InLCAnYXR0YWNoZS1wbGFjZWhvbGRlcnMtY291bnQtJyArIHBsYWNlaG9sZGVycy5sZW5ndGgsICdhdHRhY2hlLXByZXZpZXdzLWNvdW50LScgKyBwcmV2aWV3cy5sZW5ndGgsIHRoaXMucHJvcHNbJ2RhdGEtY2xhc3NuYW1lJ11dLmpvaW4oJyAnKS50cmltKClcbiAgICByZXR1cm4gKFxuICAgIDxsYWJlbFxuICAgICAgaHRtbEZvcj17dGhhdC5wcm9wcy5pZH1cbiAgICAgIGNsYXNzTmFtZT17Y2xhc3NOYW1lfVxuICAgICAgb25EcmFnT3Zlcj17dGhpcy5vbkRyYWdPdmVyfVxuICAgICAgb25EcmFnTGVhdmU9e3RoaXMub25EcmFnTGVhdmV9XG4gICAgICBvbkRyb3A9e3RoaXMub25Ecm9wfT5cbiAgICAgIDxpbnB1dCB0eXBlPVwiZmlsZVwiIHsuLi50aGF0LnByb3BzfSBvbkNoYW5nZT17dGhpcy5vbkNoYW5nZX0gLz5cbiAgICAgIDxpbnB1dCB0eXBlPVwiaGlkZGVuXCIgbmFtZT17dGhhdC5wcm9wcy5uYW1lfSB2YWx1ZT1cIlwiIC8+XG4gICAgICA8SGVhZGVyIHsuLi50aGF0LnByb3BzfSAvPlxuICAgICAge3ByZXZpZXdzfVxuICAgICAge3BsYWNlaG9sZGVyc31cbiAgICAgIHtkaXNjYXJkc31cbiAgICA8L2xhYmVsPlxuICAgIClcbiAgfVxufSlcbiJdfQ==
data/app/index.html ADDED
@@ -0,0 +1,39 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <div class="row marketing">
12
+ <div class="col-sm-12">
13
+ <div class="page-header">
14
+ <h1>Test Form</h1>
15
+ </div>
16
+ <div class="panel">
17
+ <form class="form-horizontal">
18
+ <div class="form-group">
19
+ <label for="inputFile" class="col-sm-2 control-label">File</label>
20
+ <div class="col-sm-10">
21
+ <input type="file" class="form-control enable-attache" id="inputFile" placeholder="File">
22
+ </div>
23
+ </div>
24
+ <div class="form-group">
25
+ <div class="col-sm-offset-2 col-sm-10">
26
+ <button type="submit" class="btn btn-default">Sign in</button>
27
+ </div>
28
+ </div>
29
+ </form>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-alpha1/jquery.js"></script>
35
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react.js"></script>
36
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.3/react-dom.js"></script>
37
+ <script src="assets/javascripts/attache.js"></script>
38
+ </body>
39
+ </html>
@@ -14,9 +14,13 @@ module Attache
14
14
 
15
15
  # `discard` management
16
16
  base.class_eval do
17
+ attr_accessor :attaches_to_backup
17
18
  attr_accessor :attaches_discarded
18
- after_commit if: :attaches_discarded do |instance|
19
- instance.attaches_discard!(instance.attaches_discarded)
19
+ after_commit do |instance|
20
+ instance.attaches_discard!(instance.attaches_discarded) if instance.attaches_discarded.present?
21
+ instance.attaches_discarded = []
22
+ instance.attaches_backup!(instance.attaches_to_backup) if instance.attaches_to_backup.present?
23
+ instance.attaches_to_backup = []
20
24
  end
21
25
  end
22
26
  end
@@ -36,8 +40,8 @@ module Attache
36
40
  define_method "#{name}_url", -> (geometry) { attache_field_urls(self.send(name), geometry).try(:first) }
37
41
  define_method "#{name}_attributes", -> (geometry) { attache_field_attributes(self.send(name), geometry).try(:first) }
38
42
  define_method "#{name}=", -> (value) { super(attache_field_set(Array.wrap(value)).try(:first)) }
39
- after_update -> { self.attaches_discarded ||= []; attache_mark_for_discarding(self.send("#{name}_was"), self.send("#{name}"), self.attaches_discarded) }
40
- after_destroy -> { self.attaches_discarded ||= []; attache_mark_for_discarding(self.send("#{name}_was"), [], self.attaches_discarded) }
43
+ after_save -> { attache_update_pending_diffs(self.send("#{name}_was"), self.send("#{name}"), self.attaches_to_backup ||= [], self.attaches_discarded ||= []) }
44
+ after_destroy -> { attache_update_pending_diffs(self.send("#{name}_was"), [], self.attaches_to_backup ||= [], self.attaches_discarded ||= []) }
41
45
  end
42
46
 
43
47
  def has_many_attaches(name)
@@ -46,8 +50,8 @@ module Attache
46
50
  define_method "#{name}_urls", -> (geometry) { attache_field_urls(self.send(name), geometry) }
47
51
  define_method "#{name}_attributes", -> (geometry) { attache_field_attributes(self.send(name), geometry) }
48
52
  define_method "#{name}=", -> (value) { super(attache_field_set(Array.wrap(value))) }
49
- after_update -> { self.attaches_discarded ||= []; attache_mark_for_discarding(self.send("#{name}_was"), self.send("#{name}"), self.attaches_discarded) }
50
- after_destroy -> { self.attaches_discarded ||= []; attache_mark_for_discarding(self.send("#{name}_was"), [], self.attaches_discarded) }
53
+ after_save -> { attache_update_pending_diffs(self.send("#{name}_was"), self.send("#{name}"), self.attaches_to_backup ||= [], self.attaches_discarded ||= []) }
54
+ after_destroy -> { attache_update_pending_diffs(self.send("#{name}_was"), [], self.attaches_to_backup ||= [], self.attaches_discarded ||= []) }
51
55
  end
52
56
  end
53
57
  end
@@ -1,5 +1,5 @@
1
1
  module Attache
2
2
  module Rails
3
- VERSION = "0.4.1"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  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.4.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - choonkeat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-14 00:00:00.000000000 Z
11
+ date: 2015-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.0
33
+ version: 1.0.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.2.0
40
+ version: 1.0.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: httpclient
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -119,12 +119,7 @@ files:
119
119
  - README.md
120
120
  - Rakefile
121
121
  - app/assets/javascripts/attache.js
122
- - app/assets/javascripts/attache/bootstrap3.js
123
- - app/assets/javascripts/attache/bootstrap3.js.jsx
124
- - app/assets/javascripts/attache/cors_upload.js
125
- - app/assets/javascripts/attache/file_input.js
126
- - app/assets/javascripts/attache/file_input.js.jsx
127
- - app/assets/javascripts/attache/ujs.js
122
+ - app/index.html
128
123
  - lib/attache/rails.rb
129
124
  - lib/attache/rails/engine.rb
130
125
  - lib/attache/rails/model.rb
@@ -1,77 +0,0 @@
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
- var previewClassName = "attache-file-preview";
15
-
16
- // progressbar
17
- if (this.state.srcWas != this.props.src) {
18
- previewClassName = previewClassName + " attache-loading";
19
- var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
20
- var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
21
- var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
22
- var pctStyle = { width: pctString, minWidth: '3em' };
23
- var progress = (
24
- React.createElement("div", {className: "progress"},
25
- React.createElement("div", {className: className, role: "progressbar", "aria-valuenow": this.props.percentLoaded, "aria-valuemin": "0", "aria-valuemax": "100", style: pctStyle},
26
- pctDesc
27
- )
28
- )
29
- );
30
- }
31
-
32
- // img tag
33
- if (this.props.src) {
34
- var img = React.createElement("img", {src: this.props.src, onLoad: this.onSrcLoaded});
35
- }
36
-
37
- // combined
38
- return (
39
- React.createElement("div", {className: previewClassName},
40
- progress,
41
- img,
42
- React.createElement("div", {className: "clearfix"},
43
- React.createElement("div", {className: "pull-left"}, this.props.filename),
44
- React.createElement("a", {href: "#remove", className: "pull-right", onClick: this.props.onRemove, title: "Click to remove"}, "×")
45
- )
46
- )
47
- );
48
- }
49
- });
50
-
51
- }
52
-
53
- if (typeof AttachePlaceholder === 'undefined') {
54
-
55
- var AttachePlaceholder = React.createClass({displayName: "AttachePlaceholder",
56
- render: function() {
57
- return (
58
- React.createElement("div", {className: "attache-file-preview"},
59
- React.createElement("img", {src: this.props.src})
60
- )
61
- );
62
- }
63
- });
64
-
65
- }
66
-
67
- if (typeof AttacheHeader === 'undefined') {
68
-
69
- var AttacheHeader = React.createClass({displayName: "AttacheHeader",
70
- render: function() {
71
- return (
72
- React.createElement("noscript", null)
73
- );
74
- }
75
- });
76
-
77
- }
@@ -1,77 +0,0 @@
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
- var previewClassName = "attache-file-preview";
15
-
16
- // progressbar
17
- if (this.state.srcWas != this.props.src) {
18
- previewClassName = previewClassName + " attache-loading";
19
- var className = this.props.className || "progress-bar progress-bar-striped active" + (this.props.src ? " progress-bar-success" : "");
20
- var pctString = this.props.pctString || (this.props.src ? 100 : this.props.percentLoaded) + "%";
21
- var pctDesc = this.props.pctDesc || (this.props.src ? 'Loading...' : pctString);
22
- var pctStyle = { width: pctString, minWidth: '3em' };
23
- var progress = (
24
- <div className="progress">
25
- <div className={className} role="progressbar" aria-valuenow={this.props.percentLoaded} aria-valuemin="0" aria-valuemax="100" style={pctStyle}>
26
- {pctDesc}
27
- </div>
28
- </div>
29
- );
30
- }
31
-
32
- // img tag
33
- if (this.props.src) {
34
- var img = <img src={this.props.src} onLoad={this.onSrcLoaded} />;
35
- }
36
-
37
- // combined
38
- return (
39
- <div className={previewClassName}>
40
- {progress}
41
- {img}
42
- <div className="clearfix">
43
- <div className="pull-left">{this.props.filename}</div>
44
- <a href="#remove" className="pull-right" onClick={this.props.onRemove} title="Click to remove">&times;</a>
45
- </div>
46
- </div>
47
- );
48
- }
49
- });
50
-
51
- }
52
-
53
- if (typeof AttachePlaceholder === 'undefined') {
54
-
55
- var AttachePlaceholder = React.createClass({
56
- render: function() {
57
- return (
58
- <div className="attache-file-preview">
59
- <img src={this.props.src} />
60
- </div>
61
- );
62
- }
63
- });
64
-
65
- }
66
-
67
- if (typeof AttacheHeader === 'undefined') {
68
-
69
- var AttacheHeader = React.createClass({
70
- render: function() {
71
- return (
72
- <noscript />
73
- );
74
- }
75
- });
76
-
77
- }
@@ -1,87 +0,0 @@
1
- var AttacheCORSUpload = (function() {
2
- var counter = 0;
3
-
4
- AttacheCORSUpload.prototype.onComplete = function(uid, json) { };
5
- AttacheCORSUpload.prototype.onProgress = function(uid, json) { };
6
- AttacheCORSUpload.prototype.onError = function(uid, status) { alert(status); };
7
- AttacheCORSUpload.prototype.createLocalThumbnail = function() { }; // for overwriting
8
-
9
- function AttacheCORSUpload(options) {
10
- if (options == null) options = {};
11
- for (option in options) {
12
- this[option] = options[option];
13
- }
14
- this.handleFileSelect(options.file_element, options.files);
15
- }
16
-
17
- AttacheCORSUpload.prototype.handleFileSelect = function(file_element, files) {
18
- var f, output, _i, _len, _results, url, $ele, prefix;
19
- $ele = $(file_element);
20
- url = $ele.data('uploadurl');
21
- if ($ele.data('hmac')) {
22
- url = url +
23
- "?hmac=" + encodeURIComponent($ele.data('hmac')) +
24
- "&uuid=" + encodeURIComponent($ele.data('uuid')) +
25
- "&expiration=" + encodeURIComponent($ele.data('expiration')) +
26
- ""
27
- }
28
-
29
- prefix = Date.now() + "_";
30
- output = [];
31
- _results = [];
32
- for (_i = 0, _len = files.length; _i < _len; _i++) {
33
- f = files[_i];
34
- this.createLocalThumbnail(f); // if any
35
- f.uid = prefix + (counter++);
36
- this.onProgress(f.uid, { src: f.src, filename: f.name, percentLoaded: 0, bytesLoaded: 0, bytesTotal: f.size });
37
- _results.push(this.performUpload(f, url));
38
- }
39
- return _results;
40
- };
41
-
42
- AttacheCORSUpload.prototype.createCORSRequest = function(method, url) {
43
- var xhr;
44
- xhr = new XMLHttpRequest();
45
- if (xhr.withCredentials != null) {
46
- xhr.open(method, url, true);
47
- } else if (typeof XDomainRequest !== "undefined") {
48
- xhr = new XDomainRequest();
49
- xhr.open(method, url);
50
- } else {
51
- xhr = null;
52
- }
53
- return xhr;
54
- };
55
-
56
- AttacheCORSUpload.prototype.performUpload = function(file, url) {
57
- var this_s3upload, xhr;
58
- this_s3upload = this;
59
- url = url + (url.indexOf('?') == -1 ? '?' : '&') + 'file=' + encodeURIComponent(file.name);
60
- xhr = this.createCORSRequest('PUT', url);
61
- if (!xhr) {
62
- this.onError(file.uid, 'CORS not supported');
63
- } else {
64
- xhr.onload = function(e) {
65
- if (xhr.status === 200) {
66
- this_s3upload.onComplete(file.uid, JSON.parse(e.target.responseText));
67
- } else {
68
- return this_s3upload.onError(file.uid, xhr.status + ' ' + xhr.statusText);
69
- }
70
- };
71
- xhr.onerror = function() {
72
- return this_s3upload.onError(file.uid, 'Unable to reach server');
73
- };
74
- xhr.upload.onprogress = function(e) {
75
- var percentLoaded;
76
- if (e.lengthComputable) {
77
- percentLoaded = Math.round((e.loaded / e.total) * 100);
78
- return this_s3upload.onProgress(file.uid, { src: file.src, filename: file.name, percentLoaded: percentLoaded, bytesLoaded: e.loaded, bytesTotal: e.total });
79
- }
80
- };
81
- }
82
- return xhr.send(file);
83
- };
84
-
85
- return AttacheCORSUpload;
86
-
87
- })();
@@ -1,153 +0,0 @@
1
- var AttacheFileInput = React.createClass({displayName: "AttacheFileInput",
2
-
3
- getInitialState: function() {
4
- var files = {};
5
- if (this.props['data-value']) $.each(JSON.parse(this.props['data-value']), function(uid, json) {
6
- if (json) files[uid] = json;
7
- });
8
- return {files: files, attaches_discarded: [], uploading: 0 };
9
- },
10
-
11
- onRemove: function(uid, e) {
12
- e.preventDefault();
13
- e.stopPropagation();
14
-
15
- var fieldname = this.getDOMNode().firstChild.name; // when 'user[avatar]'
16
- var newfield = fieldname.replace(/\w+\](\[\]|)$/, 'attaches_discarded][]'); // become 'user[attaches_discarded][]'
17
-
18
- this.state.attaches_discarded.push({ fieldname: newfield, path: this.state.files[uid].path });
19
- delete this.state.files[uid];
20
-
21
- this.setState(this.state);
22
- },
23
-
24
- performUpload: function(file_element, files) {
25
- // user cancelled file chooser dialog. ignore
26
- if (! files || files.length == 0) return;
27
- if (! this.props.multiple) {
28
- this.state.files = {};
29
- files = [files[0]]; // array of 1 element
30
- }
31
-
32
- this.setState(this.state);
33
- // upload the file via CORS
34
- var that = this;
35
-
36
- that.state.uploading = that.state.uploading + files.length;
37
- if (! that.state.submit_buttons) that.state.submit_buttons = $("button,input[type='submit']", $(file_element).parents('form')[0]).filter(':not(:disabled)');
38
-
39
- new AttacheCORSUpload({
40
- file_element: file_element,
41
- files: files,
42
- onComplete: function() {
43
- that.state.uploading--;
44
- that.setFileValue.apply(this, arguments);
45
- },
46
- onProgress: this.setFileValue,
47
- onError: function(uid, status) {
48
- that.state.uploading--;
49
- that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' });
50
- }
51
- });
52
-
53
- // we don't want the file binary to be uploaded in the main form
54
- // so the actual file input is neutered
55
- file_element.value = '';
56
- },
57
-
58
- onChange: function() {
59
- var file_element = this.getDOMNode().firstChild;
60
- this.performUpload(file_element, file_element && file_element.files);
61
- },
62
-
63
- onDragOver: function(e) {
64
- e.stopPropagation();
65
- e.preventDefault();
66
- $(this.getDOMNode()).addClass('attache-dragover');
67
- },
68
-
69
- onDragLeave: function(e) {
70
- e.stopPropagation();
71
- e.preventDefault();
72
- $(this.getDOMNode()).removeClass('attache-dragover');
73
- },
74
-
75
- onDrop: function(e) {
76
- e.stopPropagation();
77
- e.preventDefault();
78
- var file_element = this.getDOMNode().firstChild;
79
- this.performUpload(file_element, e.target.files || e.dataTransfer.files);
80
- $(this.getDOMNode()).removeClass('attache-dragover');
81
- },
82
-
83
- setFileValue: function(key, value) {
84
- this.state.files[key] = value;
85
- this.setState(this.state);
86
- },
87
-
88
- render: function() {
89
- var that = this;
90
-
91
- if (that.state.uploading > 0) {
92
- that.state.submit_buttons.attr('disabled', true);
93
- } else if (that.state.submit_buttons) {
94
- that.state.submit_buttons.attr('disabled', null);
95
- }
96
-
97
- var Header = eval(this.props['data-header-component'] || 'AttacheHeader');
98
- var Preview = eval(this.props['data-preview-component'] || 'AttacheFilePreview');
99
- var Placeholder = eval(this.props['data-placeholder-component'] || 'AttachePlaceholder');
100
-
101
- var previews = [];
102
- $.each(that.state.files, function(key, result) {
103
- // json is input[value], drop non essential values
104
- var copy = JSON.parse(JSON.stringify(result));
105
- delete copy.src;
106
- delete copy.filename;
107
- var json = JSON.stringify(copy);
108
- //
109
- result.multiple = that.props.multiple;
110
- if (result.path) {
111
- var parts = result.path.split('/');
112
- result.filename = parts.pop().split(/[#?]/).shift();
113
- parts.push(encodeURIComponent(that.props['data-geometry'] || '128x128#'));
114
- parts.push(encodeURIComponent(result.filename));
115
- result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
116
- }
117
- var previewKey = "preview" + key;
118
- previews.push(
119
- React.createElement("div", {key: previewKey, className: "attache-file-input"},
120
- React.createElement("input", {type: "hidden", name: that.props.name, value: json, readOnly: "true"}),
121
- React.createElement(Preview, React.__spread({}, result, {key: key, onRemove: that.onRemove.bind(that, key)}))
122
- )
123
- );
124
- });
125
-
126
- var placeholders = [];
127
- if (previews.length == 0 && that.props['data-placeholder']) $.each(JSON.parse(that.props['data-placeholder']), function(uid, src) {
128
- placeholders.push(
129
- React.createElement(Placeholder, React.__spread({key: "placeholder"}, that.props, {src: src}))
130
- );
131
- });
132
-
133
- var discards = [];
134
- $.each(that.state.attaches_discarded, function(index, discard) {
135
- var discardKey = "discard" + discard.path;
136
- discards.push(
137
- React.createElement("input", {key: discardKey, type: "hidden", name: discard.fieldname, value: discard.path})
138
- );
139
- });
140
-
141
- var className = ["attache-file-selector", "attache-placeholders-count-" + placeholders.length, "attache-previews-count-" + previews.length, this.props['data-classname']].join(' ').trim();
142
- return (
143
- React.createElement("label", {htmlFor: that.props.id, className: className, onDragOver: this.onDragOver, onDragLeave: this.onDragLeave, onDrop: this.onDrop},
144
- React.createElement("input", React.__spread({type: "file"}, that.props, {onChange: this.onChange})),
145
- React.createElement("input", {type: "hidden", name: that.props.name, value: ""}),
146
- React.createElement(Header, React.__spread({}, that.props)),
147
- previews,
148
- placeholders,
149
- discards
150
- )
151
- );
152
- }
153
- });
@@ -1,153 +0,0 @@
1
- var AttacheFileInput = React.createClass({
2
-
3
- getInitialState: function() {
4
- var files = {};
5
- if (this.props['data-value']) $.each(JSON.parse(this.props['data-value']), function(uid, json) {
6
- if (json) files[uid] = json;
7
- });
8
- return {files: files, attaches_discarded: [], uploading: 0 };
9
- },
10
-
11
- onRemove: function(uid, e) {
12
- e.preventDefault();
13
- e.stopPropagation();
14
-
15
- var fieldname = this.getDOMNode().firstChild.name; // when 'user[avatar]'
16
- var newfield = fieldname.replace(/\w+\](\[\]|)$/, 'attaches_discarded][]'); // become 'user[attaches_discarded][]'
17
-
18
- this.state.attaches_discarded.push({ fieldname: newfield, path: this.state.files[uid].path });
19
- delete this.state.files[uid];
20
-
21
- this.setState(this.state);
22
- },
23
-
24
- performUpload: function(file_element, files) {
25
- // user cancelled file chooser dialog. ignore
26
- if (! files || files.length == 0) return;
27
- if (! this.props.multiple) {
28
- this.state.files = {};
29
- files = [files[0]]; // array of 1 element
30
- }
31
-
32
- this.setState(this.state);
33
- // upload the file via CORS
34
- var that = this;
35
-
36
- that.state.uploading = that.state.uploading + files.length;
37
- if (! that.state.submit_buttons) that.state.submit_buttons = $("button,input[type='submit']", $(file_element).parents('form')[0]).filter(':not(:disabled)');
38
-
39
- new AttacheCORSUpload({
40
- file_element: file_element,
41
- files: files,
42
- onComplete: function() {
43
- that.state.uploading--;
44
- that.setFileValue.apply(this, arguments);
45
- },
46
- onProgress: this.setFileValue,
47
- onError: function(uid, status) {
48
- that.state.uploading--;
49
- that.setFileValue(uid, { pctString: '90%', pctDesc: status, className: 'progress-bar progress-bar-danger' });
50
- }
51
- });
52
-
53
- // we don't want the file binary to be uploaded in the main form
54
- // so the actual file input is neutered
55
- file_element.value = '';
56
- },
57
-
58
- onChange: function() {
59
- var file_element = this.getDOMNode().firstChild;
60
- this.performUpload(file_element, file_element && file_element.files);
61
- },
62
-
63
- onDragOver: function(e) {
64
- e.stopPropagation();
65
- e.preventDefault();
66
- $(this.getDOMNode()).addClass('attache-dragover');
67
- },
68
-
69
- onDragLeave: function(e) {
70
- e.stopPropagation();
71
- e.preventDefault();
72
- $(this.getDOMNode()).removeClass('attache-dragover');
73
- },
74
-
75
- onDrop: function(e) {
76
- e.stopPropagation();
77
- e.preventDefault();
78
- var file_element = this.getDOMNode().firstChild;
79
- this.performUpload(file_element, e.target.files || e.dataTransfer.files);
80
- $(this.getDOMNode()).removeClass('attache-dragover');
81
- },
82
-
83
- setFileValue: function(key, value) {
84
- this.state.files[key] = value;
85
- this.setState(this.state);
86
- },
87
-
88
- render: function() {
89
- var that = this;
90
-
91
- if (that.state.uploading > 0) {
92
- that.state.submit_buttons.attr('disabled', true);
93
- } else if (that.state.submit_buttons) {
94
- that.state.submit_buttons.attr('disabled', null);
95
- }
96
-
97
- var Header = eval(this.props['data-header-component'] || 'AttacheHeader');
98
- var Preview = eval(this.props['data-preview-component'] || 'AttacheFilePreview');
99
- var Placeholder = eval(this.props['data-placeholder-component'] || 'AttachePlaceholder');
100
-
101
- var previews = [];
102
- $.each(that.state.files, function(key, result) {
103
- // json is input[value], drop non essential values
104
- var copy = JSON.parse(JSON.stringify(result));
105
- delete copy.src;
106
- delete copy.filename;
107
- var json = JSON.stringify(copy);
108
- //
109
- result.multiple = that.props.multiple;
110
- if (result.path) {
111
- var parts = result.path.split('/');
112
- result.filename = parts.pop().split(/[#?]/).shift();
113
- parts.push(encodeURIComponent(that.props['data-geometry'] || '128x128#'));
114
- parts.push(encodeURIComponent(result.filename));
115
- result.src = that.props['data-downloadurl'] + '/' + parts.join('/');
116
- }
117
- var previewKey = "preview" + key;
118
- previews.push(
119
- <div key={previewKey} className="attache-file-input">
120
- <input type="hidden" name={that.props.name} value={json} readOnly="true" />
121
- <Preview {...result} key={key} onRemove={that.onRemove.bind(that, key)}/>
122
- </div>
123
- );
124
- });
125
-
126
- var placeholders = [];
127
- if (previews.length == 0 && that.props['data-placeholder']) $.each(JSON.parse(that.props['data-placeholder']), function(uid, src) {
128
- placeholders.push(
129
- <Placeholder key="placeholder" {...that.props} src={src} />
130
- );
131
- });
132
-
133
- var discards = [];
134
- $.each(that.state.attaches_discarded, function(index, discard) {
135
- var discardKey = "discard" + discard.path;
136
- discards.push(
137
- <input key={discardKey} type="hidden" name={discard.fieldname} value={discard.path} />
138
- );
139
- });
140
-
141
- var className = ["attache-file-selector", "attache-placeholders-count-" + placeholders.length, "attache-previews-count-" + previews.length, this.props['data-classname']].join(' ').trim();
142
- return (
143
- <label htmlFor={that.props.id} className={className} onDragOver={this.onDragOver} onDragLeave={this.onDragLeave} onDrop={this.onDrop}>
144
- <input type="file" {...that.props} onChange={this.onChange}/>
145
- <input type="hidden" name={that.props.name} value="" />
146
- <Header {...that.props} />
147
- {previews}
148
- {placeholders}
149
- {discards}
150
- </label>
151
- );
152
- }
153
- });
@@ -1,23 +0,0 @@
1
- window.AttacheRails = {
2
- upgrade_fileinputs: function() {
3
- var safeWords = { 'class': 'className', 'for': 'htmlFor' };
4
- var sel = document.getElementsByClassName('enable-attache');
5
- var ele, attrs, name, value;
6
- for (var i = sel.length-1; i >= 0; i--) {
7
- ele = sel[i];
8
- attrs = {};
9
- for (var j = 0; j < ele.attributes.length; j++) {
10
- name = ele.attributes[j].name;
11
- value = ele.attributes[j].value;
12
- if (name === 'class') value = value.replace('enable-attache', 'attache-enabled');
13
- name = safeWords[name] || name;
14
- attrs[name] = value;
15
- }
16
- var wrap = document.createElement('div');
17
- ele.parentNode.replaceChild(wrap, ele); // ele.parentNode.insertBefore(wrap, ele.nextSibling);
18
- React.render(React.createElement(AttacheFileInput, React.__spread({}, attrs)), wrap);
19
- }
20
- }
21
- };
22
-
23
- $(document).on('page:change', AttacheRails.upgrade_fileinputs);