oxen_media 0.0.6 → 0.3.4
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 +4 -4
- data/app/assets/javascripts/components/dropzone.js.jsx.coffee +298 -0
- data/app/assets/javascripts/components/photocard.js.jsx.coffee +77 -0
- data/app/assets/javascripts/components/photos.js.jsx.coffee +68 -0
- data/app/assets/javascripts/oxen_media.js +1 -3
- data/app/assets/stylesheets/oxen_media/drop_zone.css +86 -0
- data/app/assets/stylesheets/oxen_media.css +3 -0
- data/app/controllers/photos_controller.rb +26 -0
- data/app/models/ox_photo.rb +17 -0
- data/app/policies/ox_photo_policy.rb +3 -0
- data/app/uploaders/photo_uploader.rb +53 -0
- data/app/views/photos/_photo.html.haml +11 -0
- data/app/views/photos/create.js.haml +1 -0
- data/app/views/photos/index.json.jbuilder +5 -0
- data/lib/oxen_media/version.rb +1 -1
- data/lib/oxen_media.rb +1 -1
- data/oxen_media.gemspec +4 -4
- metadata +15 -86
- data/app/assets/javascripts/jsrender.min.js +0 -4
- data/app/assets/javascripts/jsrender.min.js.map +0 -463
- data/app/assets/javascripts/oxen_media/carrier_wave_cropper.js.coffee +0 -22
- data/app/assets/javascripts/oxen_media/media.js.coffee +0 -469
- data/app/assets/javascripts/oxen_media/medium_pane.js.coffee +0 -107
- data/app/assets/javascripts/templates/selected_files.html +0 -35
- data/app/assets/stylesheets/media.css +0 -10
- data/app/assets/stylesheets/scaffold.css +0 -56
- data/app/controllers/media_controller.rb +0 -106
- data/app/helpers/media_helper.rb +0 -2
- data/app/models/medium.rb +0 -25
- data/app/policies/oxen_medium_policy.rb +0 -12
- data/app/uploaders/medium_uploader.rb +0 -153
- data/app/views/media/_fields.html.haml +0 -42
- data/app/views/media/_form.html.haml +0 -48
- data/app/views/media/_media.html.haml +0 -18
- data/app/views/media/_medium.html.haml +0 -25
- data/app/views/media/_medium_fields.html.haml +0 -4
- data/app/views/media/create.js.haml +0 -2
- data/app/views/media/crop.html.haml +0 -3
- data/app/views/media/edit.html.haml +0 -2
- data/app/views/media/index.html.haml +0 -29
- data/app/views/media/index.json.jbuilder +0 -4
- data/app/views/media/new.html.erb +0 -5
- data/app/views/media/show.html.haml +0 -21
- data/app/views/media/show.json.jbuilder +0 -1
- data/app/views/media/update_crop.js.haml +0 -1
- /data/app/assets/javascripts/{oxen_media/.keep → components/.gitkeep} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 969f6e62d5ffc5306313af425df4ad912e833635
|
4
|
+
data.tar.gz: 38d1dab4be570218ac019416769bbc0b0bfd6db4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f36442f070349750f997d8579c6900551572c0ca1dbf22b29d81544efad8ef441ff8f337ff11c1d7e645e2204d4f4a2d4a04efe24beb4f2de5915dcda71e6a25
|
7
|
+
data.tar.gz: 97ae113d18282e2d85dd2e9a61f6a1c875c1058c2f2369d1ec252d28f5058151b9d3643ecd7ff7c26527d572362bc035f943d98703d2b93f74c70891955925c2
|
@@ -0,0 +1,298 @@
|
|
1
|
+
{ div, input } = React.DOM
|
2
|
+
|
3
|
+
BLANK_FUNCTION = () ->
|
4
|
+
|
5
|
+
|
6
|
+
class @DropZone extends React.Component
|
7
|
+
constructor: (props) ->
|
8
|
+
super props
|
9
|
+
@state =
|
10
|
+
entity: props.entity
|
11
|
+
key: props.key
|
12
|
+
record: props.record
|
13
|
+
reader: null
|
14
|
+
shared: new App.Shared()
|
15
|
+
url: props.url
|
16
|
+
|
17
|
+
# filesID: {}
|
18
|
+
|
19
|
+
@propTypes =
|
20
|
+
# record: React.PropTypes.node
|
21
|
+
entity: React.PropTypes.string
|
22
|
+
key: React.PropTypes.number
|
23
|
+
id: React.PropTypes.string
|
24
|
+
classes: React.PropTypes.string
|
25
|
+
url: React.PropTypes.string
|
26
|
+
onDragStart: React.PropTypes.func
|
27
|
+
onDragEnter: React.PropTypes.func
|
28
|
+
onDrop: React.PropTypes.func
|
29
|
+
disabled: React.PropTypes.bool
|
30
|
+
|
31
|
+
@defaultProps: ->
|
32
|
+
disabled: true
|
33
|
+
entity: 'stock_item'
|
34
|
+
record: ''
|
35
|
+
classes: ''
|
36
|
+
id: 'dropzone'
|
37
|
+
key: 0
|
38
|
+
onDragStart: BLANK_FUNCTION
|
39
|
+
onDragEnter: BLANK_FUNCTION
|
40
|
+
onDrop: BLANK_FUNCTION
|
41
|
+
|
42
|
+
# reader = () ->
|
43
|
+
#
|
44
|
+
# currentFile: (f) =>
|
45
|
+
# #
|
46
|
+
# # if (i>-1)
|
47
|
+
# # # remove element
|
48
|
+
# # ids = @state.filesID.slice() # copy array
|
49
|
+
# # ids.splice(i, 1) # remove element
|
50
|
+
# # @setState filesID: ids # update state
|
51
|
+
# # else
|
52
|
+
# # @setState filesID:
|
53
|
+
|
54
|
+
progressPercentElement = ''
|
55
|
+
|
56
|
+
abortRead: () =>
|
57
|
+
@reader.abort()
|
58
|
+
|
59
|
+
preventDefaultClick: (e) =>
|
60
|
+
e.preventDefault()
|
61
|
+
e.stopPropagation()
|
62
|
+
|
63
|
+
onDragStart: (e) =>
|
64
|
+
@preventDefaultClick(e)
|
65
|
+
|
66
|
+
onDragOver: (e) =>
|
67
|
+
@preventDefaultClick(e)
|
68
|
+
# if(this.containerAcceptsDropData(e.dataTransfer.types)) { e.preventDefault(); }
|
69
|
+
# var over = parseInt(e.currentTarget.dataset.key);
|
70
|
+
# if(e.clientY - e.currentTarget.offsetTop > e.currentTarget.offsetHeight / 2) { over++; }
|
71
|
+
# if(over !== this.state.hoverOver) { this.setState({ hoverOver: over }); }
|
72
|
+
e.dataTransfer.dropEffect = 'copy';
|
73
|
+
return false;
|
74
|
+
|
75
|
+
onDragEnter: (e) =>
|
76
|
+
@preventDefaultClick(e)
|
77
|
+
$('#drop_zone_upload_bay').addClass('hovering_files')
|
78
|
+
|
79
|
+
onDragLeave: (e) =>
|
80
|
+
@preventDefaultClick(e)
|
81
|
+
$('#drop_zone_upload_bay').removeClass('hovering_files')
|
82
|
+
|
83
|
+
onDrop: (e) =>
|
84
|
+
@preventDefaultClick(e)
|
85
|
+
dt = e.dataTransfer
|
86
|
+
@handleFiles(dt.files)
|
87
|
+
|
88
|
+
clickDropZone: (e) =>
|
89
|
+
$('#file_browser').trigger('click')
|
90
|
+
|
91
|
+
changeDropZone: (e) =>
|
92
|
+
dt = e.target
|
93
|
+
@handleFiles(dt.files)
|
94
|
+
|
95
|
+
|
96
|
+
# .drop_zone.delimiter
|
97
|
+
# .drop_zone.upload_bay.stitched
|
98
|
+
# .drop_zone.file_browser
|
99
|
+
# %input{ type: "file", multiple: true, id: "file_browser" }
|
100
|
+
# .drop_zone.user_notice
|
101
|
+
# %span
|
102
|
+
# træk dine filer her; eller tryk og vælg
|
103
|
+
# .drop_zone.file_hangar
|
104
|
+
#
|
105
|
+
# :coffeescript
|
106
|
+
# $('.drop_zone.user_notice').on 'dragenter', (e) =>
|
107
|
+
# $('.drop_zone.upload_bay').addClass 'hovering_files'
|
108
|
+
# $('.drop_zone.user_notice').on 'dragleave', (e) =>
|
109
|
+
# $('.drop_zone.upload_bay').removeClass 'hovering_files'
|
110
|
+
# $('.drop_zone.user_notice').on 'click', (e) =>
|
111
|
+
# $('#file_browser').trigger 'click'
|
112
|
+
|
113
|
+
render: ->
|
114
|
+
|
115
|
+
photos = React.createElement( Photos, url: @props.url, pollInterval: 2000, id: 'existing_photos', classes: 'drop_zone existing_file_hangar', entity: @props.entity )
|
116
|
+
|
117
|
+
if @props.disabled
|
118
|
+
div
|
119
|
+
className: 'drop_zone file_hangar row'
|
120
|
+
id: 'photos'
|
121
|
+
photos
|
122
|
+
else
|
123
|
+
try
|
124
|
+
|
125
|
+
div
|
126
|
+
className: 'drop_zone delimiter'
|
127
|
+
div
|
128
|
+
className: 'drop_zone upload_bay row stitched'
|
129
|
+
id: 'drop_zone_upload_bay'
|
130
|
+
onClick: @clickDropZone
|
131
|
+
div
|
132
|
+
className: 'drop_zone file_browser'
|
133
|
+
input
|
134
|
+
className: "file_browser"
|
135
|
+
type: "file"
|
136
|
+
id: "file_browser"
|
137
|
+
name: "files"
|
138
|
+
multiple: 'true'
|
139
|
+
onChange: @changeDropZone
|
140
|
+
div
|
141
|
+
className: 'drop_zone user_notice'
|
142
|
+
onDragOver: @onDragOver
|
143
|
+
onDragEnter: @onDragEnter
|
144
|
+
onDragLeave: @onDragLeave
|
145
|
+
# onClick: @clickDropZone
|
146
|
+
onDrop: @onDrop
|
147
|
+
'træk dine billeder herover og slip dem; eller klik her og vælg dem!'
|
148
|
+
div
|
149
|
+
className: "progress"
|
150
|
+
id: "progress_bar"
|
151
|
+
div
|
152
|
+
className: "percent"
|
153
|
+
'0%'
|
154
|
+
div
|
155
|
+
className: 'drop_zone file_hangar row'
|
156
|
+
id: 'photos'
|
157
|
+
photos
|
158
|
+
|
159
|
+
catch
|
160
|
+
div
|
161
|
+
className: ''
|
162
|
+
@state.err
|
163
|
+
|
164
|
+
handleFiles: (files) =>
|
165
|
+
@bindDeleteActions()
|
166
|
+
count = 0
|
167
|
+
for file in files
|
168
|
+
count=count+1
|
169
|
+
# Only process image files.
|
170
|
+
continue if (!file.type.match('image.*'))
|
171
|
+
|
172
|
+
file.id=Date.now() + count
|
173
|
+
|
174
|
+
# place file in the dropzone
|
175
|
+
@placeFileInDOM(file)
|
176
|
+
|
177
|
+
# start uploading it
|
178
|
+
# @uploadFile(file)
|
179
|
+
|
180
|
+
|
181
|
+
bindDeleteActions: =>
|
182
|
+
$(document.body).unbind('click.delete_photo')
|
183
|
+
$(document.body).on 'click.delete_photo', 'i.delete.file_upload', @handleDelete
|
184
|
+
|
185
|
+
handleDelete: (e) =>
|
186
|
+
e.preventDefault()
|
187
|
+
id=$(e.target).closest('a').next('input').val()
|
188
|
+
|
189
|
+
jqxhr = $.ajax
|
190
|
+
method: 'DELETE'
|
191
|
+
url: "/photos/" + id + ".js"
|
192
|
+
dataType: 'html'
|
193
|
+
jqxhr.done (r) =>
|
194
|
+
$(e.target).closest('.card').fadeOut 'slow', () => #animate({ "opacity": "0" }, "slow" )
|
195
|
+
$(e.target).closest('.card').remove()
|
196
|
+
jqxhr.fail (e,msg) =>
|
197
|
+
swal "Fejl!", "Det var ikke muligt at slette billedet - fejlen er:\n" + msg, "error"
|
198
|
+
|
199
|
+
uploadFile: (f) =>
|
200
|
+
|
201
|
+
formData = new FormData()
|
202
|
+
formData.append 'photo[image]', f
|
203
|
+
$('#drop_zone_upload_bay').removeClass('hovering_files').addClass('loading_files')
|
204
|
+
jqxhr = $.ajax
|
205
|
+
url : '/photos.js'
|
206
|
+
type : 'POST'
|
207
|
+
data : formData
|
208
|
+
processData: false
|
209
|
+
contentType: false
|
210
|
+
dataType: 'html'
|
211
|
+
|
212
|
+
.done (r) =>
|
213
|
+
$('#drop_zone_upload_bay').removeClass('loading_files')
|
214
|
+
$('#photos_'+f.id+'_id').val(r.replace(/\s/g, ""))
|
215
|
+
|
216
|
+
.fail (e,msg) =>
|
217
|
+
$('#photos_'+f.id+'_id').closest('.card').remove()
|
218
|
+
$('#drop_zone_upload_bay').removeClass('loading_files')
|
219
|
+
$('#drop_zone_upload_bay').addClass('error_loading_files')
|
220
|
+
setTimeout(@removeLoadError,2000)
|
221
|
+
swal "Fejl!", "Der opstod desværre en fejl - beskrivelsen er:\n" + msg, "error"
|
222
|
+
|
223
|
+
errorHandler: (evt) =>
|
224
|
+
switch evt.target.error.code
|
225
|
+
when evt.target.error.NOT_FOUND_ERR then alert('File Not Found!')
|
226
|
+
when evt.target.error.NOT_READABLE_ERR then alert('File is not readable')
|
227
|
+
when evt.target.error.ABORT_ERR then console.log 'aborted!! line 227 in dropzone.js.jsx.coffee'
|
228
|
+
else alert('An error occurred reading this file.')
|
229
|
+
|
230
|
+
removeLoadError: (e) =>
|
231
|
+
$('#drop_zone_upload_bay').removeClass('error_loading_files')
|
232
|
+
|
233
|
+
showProgress: (p) =>
|
234
|
+
progressPercentElement = document.querySelector('.percent')
|
235
|
+
progressPercentElement.style.width = p + '%'
|
236
|
+
progressPercentElement.textContent = p + '%'
|
237
|
+
if p>99
|
238
|
+
@state.shared.fadeItOut document.getElementById('progress_bar')
|
239
|
+
|
240
|
+
updateProgress: (e) =>
|
241
|
+
# e is an ProgressEvent.
|
242
|
+
percentLoaded = 0
|
243
|
+
if (e.lengthComputable)
|
244
|
+
percentLoaded = Math.round((e.loaded / e.total) * 100)
|
245
|
+
# Increase the progress bar length.
|
246
|
+
@showProgress(percentLoaded) if (percentLoaded < 100)
|
247
|
+
@showProgress(100) if (percentLoaded > 99)
|
248
|
+
|
249
|
+
|
250
|
+
placeFileInDOM: (file) =>
|
251
|
+
# Reset progress indicator on new file selection.
|
252
|
+
# @updateProgress ''
|
253
|
+
|
254
|
+
@reader = new FileReader()
|
255
|
+
# @currentFile(file)
|
256
|
+
|
257
|
+
@reader.onerror = @errorHandler
|
258
|
+
@reader.onprogress = @updateProgress
|
259
|
+
@reader.onabort = (e) =>
|
260
|
+
alert('File read cancelled')
|
261
|
+
|
262
|
+
@reader.onloadstart = (e) =>
|
263
|
+
document.getElementById('progress_bar').className = 'loading'
|
264
|
+
|
265
|
+
@reader.onload = (e) =>
|
266
|
+
|
267
|
+
# Render thumbnail.
|
268
|
+
img = document.createElement('div')
|
269
|
+
img.className = "col sl12 m6 l3"
|
270
|
+
img.innerHTML = [ '<div class="card">',
|
271
|
+
'<div class="card-image waves-effect waves-block waves-light">',
|
272
|
+
'<div class="progress fileupload-progress fade" style="display: none">',
|
273
|
+
'<div class="determinate" style="width: 0%"></div>',
|
274
|
+
'</div>',
|
275
|
+
'<img class="thumb" src="', e.target.result, '" title="', escape(file.name), '"/>',
|
276
|
+
'<div class="row">',
|
277
|
+
'<div class="col s2">',
|
278
|
+
'<a class="delete file-upload" href="#!">',
|
279
|
+
'<i class="material-icons small delete file_upload red-text">',
|
280
|
+
'delete',
|
281
|
+
'</i> </a>',
|
282
|
+
'</div>',
|
283
|
+
'<div class="col s10">',
|
284
|
+
window.build_drop_zone_photo file, @props.entity
|
285
|
+
'</div>',
|
286
|
+
'</div>',
|
287
|
+
'</div>'
|
288
|
+
].join('')
|
289
|
+
|
290
|
+
document.getElementById('photos').appendChild(img, null)
|
291
|
+
@uploadFile file
|
292
|
+
|
293
|
+
# Ensure that the progress bar displays 100% at the end.
|
294
|
+
@showProgress(100)
|
295
|
+
|
296
|
+
|
297
|
+
# Read in the image file as a data URL.
|
298
|
+
@reader.readAsDataURL(file)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
{ div, img, input, a, i, label } = React.DOM
|
2
|
+
|
3
|
+
class @PhotoCard extends React.Component
|
4
|
+
constructor: (props) ->
|
5
|
+
super props
|
6
|
+
@state =
|
7
|
+
key: props.key
|
8
|
+
entity: props.entity
|
9
|
+
record: props.record
|
10
|
+
|
11
|
+
@propTypes =
|
12
|
+
# record: React.PropTypes.node
|
13
|
+
key: React.PropTypes.number
|
14
|
+
entity: React.PropTypes.string
|
15
|
+
|
16
|
+
@defaultProps: ->
|
17
|
+
record: ''
|
18
|
+
key: 0
|
19
|
+
entity: 'stock_item'
|
20
|
+
|
21
|
+
show_on_new: () =>
|
22
|
+
if @props.record.id == 0
|
23
|
+
''
|
24
|
+
else
|
25
|
+
'hide'
|
26
|
+
|
27
|
+
handleDelete: (e) =>
|
28
|
+
e.preventDefault()
|
29
|
+
jqxhr = $.ajax
|
30
|
+
method: 'DELETE'
|
31
|
+
url: "/photos/#{ @state.record.id }"
|
32
|
+
dataType: 'html'
|
33
|
+
jqxhr.done () =>
|
34
|
+
$(e.target).closest('.card').fadeOut 'slow', () => #animate({ "opacity": "0" }, "slow" )
|
35
|
+
@props.deleteRecord @state.record
|
36
|
+
jqxhr.fail (e,msg) =>
|
37
|
+
swal "Fejl!", "Det var ikke muligt at slette billedet - fejlen er:\n" + msg, "error"
|
38
|
+
|
39
|
+
render: ->
|
40
|
+
|
41
|
+
div
|
42
|
+
className: "col s12 m6 l3"
|
43
|
+
div
|
44
|
+
className: "card"
|
45
|
+
div
|
46
|
+
className: "card-image waves-effect waves-block waves-light"
|
47
|
+
div
|
48
|
+
className: "progress fileupload-progress fade"
|
49
|
+
style: display: 'none'
|
50
|
+
div
|
51
|
+
className: "determinate"
|
52
|
+
style: width: '0%'
|
53
|
+
img
|
54
|
+
className: "activator"
|
55
|
+
id: @props.key
|
56
|
+
src: @props.record.image.url
|
57
|
+
div
|
58
|
+
className: "row"
|
59
|
+
div
|
60
|
+
className: "col s2"
|
61
|
+
a
|
62
|
+
className: "delete file-upload"
|
63
|
+
href: "#!"
|
64
|
+
i
|
65
|
+
className: "material-icons small file_upload red-text"
|
66
|
+
onClick: @handleDelete
|
67
|
+
'delete'
|
68
|
+
div
|
69
|
+
className: "col s10"
|
70
|
+
input
|
71
|
+
className: "files_uploaded"
|
72
|
+
id: "photos_#{@props.record.id}_id"
|
73
|
+
type: "hidden"
|
74
|
+
name: "#{@props.entity}[ps][#{@props.record.id}][id]"
|
75
|
+
value: "#{@props.record.id}"
|
76
|
+
@props.record.purpose || ""
|
77
|
+
@props.record.title || ""
|
@@ -0,0 +1,68 @@
|
|
1
|
+
{ div, a, i } = React.DOM
|
2
|
+
|
3
|
+
class @Photos extends React.Component
|
4
|
+
constructor: (props) ->
|
5
|
+
super props
|
6
|
+
@state =
|
7
|
+
url: props.url
|
8
|
+
err: 'Indlæser billeder...'
|
9
|
+
entity: props.entity
|
10
|
+
|
11
|
+
@propTypes =
|
12
|
+
url: React.PropTypes.string
|
13
|
+
id: React.PropTypes.string
|
14
|
+
classes: React.PropTypes.string
|
15
|
+
entity: React.PropTypes.string
|
16
|
+
|
17
|
+
@defaultProps: ->
|
18
|
+
records: []
|
19
|
+
url: ''
|
20
|
+
id: 'oldphotos'
|
21
|
+
classes: 'drop_zone old_files_hangar'
|
22
|
+
entity: 'stock_item'
|
23
|
+
|
24
|
+
componentDidMount: ->
|
25
|
+
@loadPhotosFromServer()
|
26
|
+
# setInterval(@loadPhotosFromServer, @props.pollInterval); # use polling, can be other eg. WebSockets
|
27
|
+
|
28
|
+
addRecord: (record) ->
|
29
|
+
records = React.addons.update(@state.records, { $push: [record] })
|
30
|
+
@setState records: records
|
31
|
+
|
32
|
+
deleteRecord: (record) =>
|
33
|
+
index = @state.records.indexOf record
|
34
|
+
records = React.addons.update(@state.records, { $splice: [[index, 1]] })
|
35
|
+
@setState records: records
|
36
|
+
|
37
|
+
render: ->
|
38
|
+
try
|
39
|
+
cards = []
|
40
|
+
for record in @state.records
|
41
|
+
cards.push React.createElement PhotoCard, { key: record.id, record: record, deleteRecord: @deleteRecord, entity: @props.entity }
|
42
|
+
|
43
|
+
div
|
44
|
+
className: @props.classes
|
45
|
+
id: @props.id
|
46
|
+
div
|
47
|
+
cards
|
48
|
+
|
49
|
+
catch
|
50
|
+
div
|
51
|
+
className: ''
|
52
|
+
@state.err
|
53
|
+
|
54
|
+
loadPhotosFromServer: ->
|
55
|
+
$.ajax
|
56
|
+
url: @state.url,
|
57
|
+
dataType: 'json',
|
58
|
+
cache: false,
|
59
|
+
success: (data) =>
|
60
|
+
@setState {records: data}
|
61
|
+
error: (xhr, status, err) =>
|
62
|
+
@setState { err: err.toString() }
|
63
|
+
|
64
|
+
# $.ajax {
|
65
|
+
# url: @props.url,
|
66
|
+
# dataType: 'json',
|
67
|
+
# type: 'POST',
|
68
|
+
# data: comment,
|
@@ -0,0 +1,86 @@
|
|
1
|
+
/* dropzone styling */
|
2
|
+
.thumb {
|
3
|
+
width: 100%;
|
4
|
+
}
|
5
|
+
|
6
|
+
#progress_bar {
|
7
|
+
margin: 50px 40px 0px 30px;
|
8
|
+
padding: 3px;
|
9
|
+
border: 1px solid grey;
|
10
|
+
font-size: 7px;
|
11
|
+
clear: both;
|
12
|
+
opacity: 0;
|
13
|
+
-moz-transition: opacity 1s linear;
|
14
|
+
-o-transition: opacity 1s linear;
|
15
|
+
-webkit-transition: opacity 1s linear;
|
16
|
+
}
|
17
|
+
#progress_bar.loading {
|
18
|
+
opacity: 1.0;
|
19
|
+
}
|
20
|
+
#progress_bar .percent {
|
21
|
+
background-color: #99ccff;
|
22
|
+
height: auto;
|
23
|
+
width: 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
.drop_zone.delimiter {
|
27
|
+
height: 150px;
|
28
|
+
width: 100%;
|
29
|
+
padding: 0px;
|
30
|
+
margin: 0px;
|
31
|
+
position: relative
|
32
|
+
}
|
33
|
+
.drop_zone.upload_bay {
|
34
|
+
width: 100%;
|
35
|
+
height: 80px;
|
36
|
+
position: relative;
|
37
|
+
padding: 0px;
|
38
|
+
margin: 0px;
|
39
|
+
background-color: #26a69a;
|
40
|
+
border-radius: 8px;
|
41
|
+
}
|
42
|
+
.drop_zone.user_notice {
|
43
|
+
position: absolute;
|
44
|
+
top: 0px;
|
45
|
+
border: 0px;
|
46
|
+
width: 100%;
|
47
|
+
height: 100%;
|
48
|
+
padding: 30px;
|
49
|
+
}
|
50
|
+
.drop_zone.file_browser {
|
51
|
+
width: 100%;
|
52
|
+
height: 100%;
|
53
|
+
position: absolute;
|
54
|
+
top: 0px;
|
55
|
+
opacity: 0;
|
56
|
+
}
|
57
|
+
.drop_zone.file_hangar {
|
58
|
+
min-height: 100px;
|
59
|
+
height: 100%;
|
60
|
+
background-color: transparent;
|
61
|
+
}
|
62
|
+
.hovering_files {
|
63
|
+
background-color: #0ABFBC !important;
|
64
|
+
}
|
65
|
+
.loading_files {
|
66
|
+
background-color: #f57c00 !important;
|
67
|
+
}
|
68
|
+
.error_loading_files {
|
69
|
+
background-color: red !important;
|
70
|
+
}
|
71
|
+
.stitched {
|
72
|
+
padding: 20px;
|
73
|
+
margin: 10px;
|
74
|
+
color: #fff;
|
75
|
+
font-size: 12px;
|
76
|
+
font-weight: 400;
|
77
|
+
line-height: 1.3em;
|
78
|
+
border: 1px dashed #fff;
|
79
|
+
border-radius: 8px;
|
80
|
+
box-shadow: 0 0 0 4px #26a69a, 2px 2px 6px 4px rgba(1, 1, 1, 0.25);
|
81
|
+
font-weight: normal;
|
82
|
+
}
|
83
|
+
.drop_zone .card .row {
|
84
|
+
margin-bottom: 0px
|
85
|
+
}
|
86
|
+
/* end of dropzone styling */
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class PhotosController < AbstractResourcesController
|
2
|
+
|
3
|
+
private
|
4
|
+
|
5
|
+
def resource_params
|
6
|
+
params.require(:photo).permit( :id, :image )
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# find all resources
|
12
|
+
def find_all_resources options
|
13
|
+
return false unless policy_scope(resource_class)
|
14
|
+
policy_scope(resource_class)
|
15
|
+
end
|
16
|
+
#
|
17
|
+
#
|
18
|
+
# find queried resources collection - implement on each controller to customize
|
19
|
+
def find_resources_queried options={}
|
20
|
+
lot = parent? ? parent.send(resource_name) : resource_class
|
21
|
+
res = case params[:subtype]
|
22
|
+
when nil; Photo.search policy_scope(lot), params[:q]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class OxPhoto < AbstractResource
|
2
|
+
self.table_name = "photos"
|
3
|
+
|
4
|
+
has_paper_trail
|
5
|
+
#photo belongs to album
|
6
|
+
# belongs_to :stock_item
|
7
|
+
# belongs_to :account
|
8
|
+
|
9
|
+
#validations
|
10
|
+
# validates :stock_item, presence: true
|
11
|
+
|
12
|
+
include Rails.application.routes.url_helpers
|
13
|
+
|
14
|
+
# Photo uploader using carrierwave
|
15
|
+
mount_uploader :image, PhotoUploader
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class PhotoUploader < CarrierWave::Uploader::Base
|
4
|
+
|
5
|
+
# Include RMagick or MiniMagick support:
|
6
|
+
# include CarrierWave::RMagick
|
7
|
+
include CarrierWave::MiniMagick
|
8
|
+
|
9
|
+
# Choose what kind of storage to use for this uploader:
|
10
|
+
storage :file
|
11
|
+
#storage :fog
|
12
|
+
# Override the directory where uploaded files will be stored.
|
13
|
+
# This is a sensible default for uploaders that are meant to be mounted:
|
14
|
+
def store_dir
|
15
|
+
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Provide a default URL as a default if there hasn't been a file uploaded:
|
19
|
+
def default_url
|
20
|
+
# For Rails 3.1+ asset pipeline compatibility:
|
21
|
+
# ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
|
22
|
+
|
23
|
+
#{}"/images/fallback/" + [version_name, "default.png"].compact.join('_')
|
24
|
+
'default_photo.png' #rails will look at 'app/assets/images/default_avatar.png'
|
25
|
+
end
|
26
|
+
|
27
|
+
# Process files as they are uploaded:
|
28
|
+
# process :scale => [200, 300]
|
29
|
+
#
|
30
|
+
# def scale(width, height)
|
31
|
+
# # do something
|
32
|
+
# end
|
33
|
+
|
34
|
+
# Create different versions of your uploaded files:
|
35
|
+
version :large_photo do
|
36
|
+
# returns a 150x150 image
|
37
|
+
process :resize_to_fill => [150, 150]
|
38
|
+
end
|
39
|
+
version :medium_photo do
|
40
|
+
# returns a 50x50 image
|
41
|
+
process :resize_to_fill => [50, 50]
|
42
|
+
end
|
43
|
+
version :small_photo do
|
44
|
+
# returns a 35x35 image
|
45
|
+
process :resize_to_fill => [35, 35]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Add a white list of extensions which are allowed to be uploaded.
|
49
|
+
# For images you might use something like this:
|
50
|
+
def extension_white_list
|
51
|
+
%w(jpg jpeg gif png)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
.col.s12.m6.l3
|
2
|
+
.card
|
3
|
+
.card-image
|
4
|
+
= image_tag photo.image_url
|
5
|
+
%span.card-title{ style: "padding: 0px"}
|
6
|
+
%a.start.file-upload{ href: '#!'}
|
7
|
+
%i.material-icons.medium.file_upload.green-text file_upload
|
8
|
+
/%a.cancel.file-upload{ href: '#!'}
|
9
|
+
/ %i.material-icons.cancel.orange-text cancel
|
10
|
+
%a.delete.file-upload{ href: '#!'}
|
11
|
+
%i.material-icons.medium.delete.red-text delete
|
@@ -0,0 +1 @@
|
|
1
|
+
= resource.id
|
data/lib/oxen_media/version.rb
CHANGED