-
-
Save leostera/93e8f58d7186bb132df1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import DS from 'ember-data'` | |
# https://aws.amazon.com/articles/1434 | |
Policy = DS.Model.extend | |
key: DS.attr('string') | |
bucket: DS.attr('string') | |
acl: DS.attr('string') | |
'AWSAccessKeyId': DS.attr('string') | |
'Content-Type': DS.attr('string') | |
policy: DS.attr('string') | |
signature: DS.attr('string') | |
`export default Policy` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import DS from 'ember-data'` | |
S3Image = DS.Model.extend | |
key: DS.attr('string') | |
policy: DS.belongsTo('policy') | |
blob: DS.attr('raw') | |
fileName: DS.attr('string') | |
baseName: Ember.computed.alias 'id' | |
extension: '' | |
blobTypeDidChange: (-> | |
return unless blob = @get('blob') | |
blobType = blob.type | |
extension = switch blobType | |
when 'image/png' then '.png' | |
when 'image/jpg', 'image/jpeg' then '.jpg' | |
else throw new Ember.Error("Unexpected S3Image blob type: " + blobType) | |
@set('extension', extension) | |
).observes('blob') | |
_fileName: (-> | |
return unless baseName = @get('baseName') | |
return unless extension = @get('extension') | |
[baseName, extension].join('') | |
).property('baseName', 'extension') | |
_key: (-> | |
return unless policyKey = @get('policy.key') | |
return unless fileName = @get('fileName') | |
policyKey.replace('${filename}', fileName) | |
).property('policy.key', 'fileName') | |
# HACK: Using observers to replicate behaviour of computed properties | |
# since we need key and fileName (which are DS.attrs) to change when we set | |
# a key or filename | |
keyShouldChange: (-> | |
@set('key', @get('_key')) | |
).observes('policy.key', 'fileName') | |
fileNameShouldChange: (-> | |
@set 'fileName', @get('_fileName') | |
).observes('baseName', 'extension', 'blob') | |
# HACK: Set by adapter for controllers' convenience | |
currentSave: null | |
`export default S3Image` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import DS from 'ember-data'` | |
Attachment = DS.Model.extend | |
caption: DS.attr('string') | |
imageKey: DS.attr('string') | |
imageProcessedAt: DS.attr('moment', { readOnly: true }) | |
imageUrl: DS.attr('string', { readOnly: true }) | |
imageThumbnailUrl: DS.attr('string', { readOnly: true }) | |
createdAt: DS.attr('moment', { readOnly: true }) | |
imageBlob: null | |
`export default Attachment` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import DS from 'ember-data'` | |
S3ImageAdapter = DS.RESTAdapter.extend Ember.Evented, | |
# http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript | |
generateIdForRecord: -> | |
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace /[xy]/g, (c) -> | |
r = Math.random() * 16 | 0 | |
v = (if c is "x" then r else r & 0x3 | 0x8) | |
v.toString 16 | |
updateRecord: -> | |
throw new Ember.Error("Cannot update an s3Image") | |
deleteRecord: -> | |
throw new Ember.Error("Cannot delete an s3Image") | |
buildURL: (type, record) -> | |
['https://', record.get('policy.bucket'), '.s3-us-west-2.amazonaws.com'].join('') | |
createRecord: (store, type, record) -> | |
data = {} | |
serializer = store.serializerFor(type.typeKey) | |
serializer.serializeIntoHash(data, type, record, { includeId: true }) | |
promise = @ajax(this.buildURL(type.typeKey, record), 'POST', { data: data }) | |
# HACK: Allows consuming controller to gracefully abort an upload | |
# since the promise-spec doesn't accomodate for that | |
record.set('currentSave', promise) | |
promise.finally -> | |
record.set('currentSave', null) | |
promise | |
# Populate a FormData instead of an ajax options hash | |
ajaxOptions: (url, type, hash) -> | |
hash = hash || {} | |
hash.url = url | |
hash.type = type | |
data = hash.data.s3_image | |
formData = new FormData | |
for key of data | |
args = [].concat(data[key]) | |
args.unshift(key) | |
formData.append.apply(formData, args) | |
hash.data = formData | |
hash | |
ajax: (url, type, hash) -> | |
adapter = @ | |
hash = @ajaxOptions(url, type, hash) | |
xhr = null | |
proxy = null | |
promise = new Ember.RSVP.Promise (resolve, reject) -> | |
xhr = new XMLHttpRequest() | |
xhr.upload.addEventListener 'progress', (e) -> | |
proxy.trigger 'progress', e.loaded / e.total if e.lengthComputable | |
, false | |
xhr.upload.addEventListener 'abort', (e) -> | |
Ember.run null, reject, adapter.ajaxError(xhr) | |
, false | |
xhr.upload.addEventListener 'error', (e) -> | |
Ember.run null, reject, adapter.ajaxError(xhr) | |
, false | |
xhr.addEventListener 'load', (e) -> | |
status = xhr.status | |
if status >= 200 && status < 300 || status == 304 | |
Ember.run null, resolve, null | |
else if status == 400 | |
error = xhr.responseXML.getElementsByTagName('Error')[0] | |
message = error.getElementsByTagName('Message')[0].textContent | |
Ember.run null, reject, new DS.InvalidError { blob: [message] } | |
else | |
Ember.run null, reject, adapter.ajaxError(xhr) | |
, false | |
xhr.open(hash.type, hash.url, true) | |
xhr.send(hash.data) | |
# Use a proxy that we can register a progress handler and abort method on | |
proxy = Ember.Object.createWithMixins Ember.PromiseProxyMixin, Ember.Evented, | |
promise: promise | |
abort: -> | |
xhr.abort() unless @get('isSettled') | |
ajaxError: (xhr) -> | |
xhr | |
`export default S3ImageAdapter` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import Ember from 'ember'` | |
ImageEditView = Ember.View.extend | |
classNames: ['incident incident-modal'] | |
# Remind IE8 users to upgrade... | |
cannotUpload: (-> | |
!window.Blob || !window.FormData | |
).property().volatile() | |
change: (e) -> | |
$input = @$(e.target) | |
# Only listen for change events to file inputs | |
return unless $input.attr('type') == 'file' | |
files = $input.get(0).files | |
@controller.send('filesWereAdded', files) if files | |
# Just replace to fully clear the input | |
$input.replaceWith($input.clone()) | |
`export default ImageEditView` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
`import Ember from 'ember'` | |
`import DS from 'ember-data'` | |
UsageController = Ember.ObjectController.extend | |
needs: ['company'] | |
company: Ember.computed.alias 'controllers.company' | |
policyBinding: 'controllers.company.policy' | |
addFiles: (files) -> | |
for i in [0..files.length - 1] by 1 | |
@addFile(files[i]) | |
addFile: (file) -> | |
store = @store | |
policy = @get('policy') | |
# Pass along the file blob | |
s3Image = store.createRecord 's3Image', | |
blob: file | |
policy: policy | |
# Use an 'attachment' record to wrap an s3Image (and have a caption, comments, etc.) | |
attachment = @get('attachments').createRecord | |
s3Image: s3Image # For progress bars | |
imageKey: s3Image.get('key') | |
imageBlob: s3Image.get('blob') | |
s3Image.save() # Send off to s3 | |
# HACK - adapter sets `currentSave` xhr that we can ask for progress events on or call abort on | |
Ember.run.once -> | |
if save = s3Image.get('currentSave') | |
save.on 'progress', (pct) -> | |
# Use for a progress bar somewhere in a template | |
actions: | |
filesWereAdded: (files) -> | |
@addFiles(files) | |
`export default UsageController` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment