neofiles 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +1 -0
  3. data/README.md +417 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/neofiles/loading.gif +0 -0
  6. data/app/assets/images/neofiles/swf-thumb-100x100.png +0 -0
  7. data/app/assets/images/neofiles/watermark.png +0 -0
  8. data/app/assets/javascripts/neofiles/index.js.coffee +3 -0
  9. data/app/assets/javascripts/neofiles/jquery.fileupload.js +1128 -0
  10. data/app/assets/javascripts/neofiles/jquery.iframe-transport.js +172 -0
  11. data/app/assets/javascripts/neofiles/jquery.neofiles.js.coffee +191 -0
  12. data/app/assets/stylesheets/neofiles/index.css.scss +3 -0
  13. data/app/assets/stylesheets/neofiles/neofiles.css.scss +149 -0
  14. data/app/controllers/concerns/neofiles/not_found.rb +21 -0
  15. data/app/controllers/neofiles/admin_controller.rb +228 -0
  16. data/app/controllers/neofiles/admin_test_controller.rb +5 -0
  17. data/app/controllers/neofiles/files_controller.rb +28 -0
  18. data/app/controllers/neofiles/images_controller.rb +130 -0
  19. data/app/helpers/neofiles/neofiles_helper.rb +188 -0
  20. data/app/models/neofiles/file.rb +319 -0
  21. data/app/models/neofiles/file_chunk.rb +18 -0
  22. data/app/models/neofiles/image.rb +119 -0
  23. data/app/models/neofiles/swf.rb +45 -0
  24. data/app/views/neofiles/admin/_file_compact.html.haml +85 -0
  25. data/app/views/neofiles/admin/file_compact.html.haml +1 -0
  26. data/app/views/neofiles/admin_test/file_compact.erb +7 -0
  27. data/config/locales/ru.yml +21 -0
  28. data/config/routes.rb +1 -0
  29. data/lib/neofiles.rb +94 -0
  30. data/lib/neofiles/engine.rb +33 -0
  31. data/lib/neofiles/version.rb +3 -0
  32. metadata +131 -0
@@ -0,0 +1,172 @@
1
+ /*
2
+ * jQuery Iframe Transport Plugin 1.5
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2011, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint unparam: true, nomen: true */
13
+ /*global define, window, document */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define(['jquery'], factory);
20
+ } else {
21
+ // Browser globals:
22
+ factory(window.jQuery);
23
+ }
24
+ }(function ($) {
25
+ 'use strict';
26
+
27
+ // Helper variable to create unique names for the transport iframes:
28
+ var counter = 0;
29
+
30
+ // The iframe transport accepts three additional options:
31
+ // options.fileInput: a jQuery collection of file input fields
32
+ // options.paramName: the parameter name for the file form data,
33
+ // overrides the name property of the file input field(s),
34
+ // can be a string or an array of strings.
35
+ // options.formData: an array of objects with name and value properties,
36
+ // equivalent to the return data of .serializeArray(), e.g.:
37
+ // [{name: 'a', value: 1}, {name: 'b', value: 2}]
38
+ $.ajaxTransport('iframe', function (options) {
39
+ if (options.async && (options.type === 'POST' || options.type === 'GET')) {
40
+ var form,
41
+ iframe;
42
+ return {
43
+ send: function (_, completeCallback) {
44
+ form = $('<form style="display:none;"></form>');
45
+ form.attr('accept-charset', options.formAcceptCharset);
46
+ // javascript:false as initial iframe src
47
+ // prevents warning popups on HTTPS in IE6.
48
+ // IE versions below IE8 cannot set the name property of
49
+ // elements that have already been added to the DOM,
50
+ // so we set the name along with the iframe HTML markup:
51
+ iframe = $(
52
+ '<iframe src="javascript:false;" name="iframe-transport-' +
53
+ (counter += 1) + '"></iframe>'
54
+ ).bind('load', function () {
55
+ var fileInputClones,
56
+ paramNames = $.isArray(options.paramName) ?
57
+ options.paramName : [options.paramName];
58
+ iframe
59
+ .unbind('load')
60
+ .bind('load', function () {
61
+ var response;
62
+ // Wrap in a try/catch block to catch exceptions thrown
63
+ // when trying to access cross-domain iframe contents:
64
+ try {
65
+ response = iframe.contents();
66
+ // Google Chrome and Firefox do not throw an
67
+ // exception when calling iframe.contents() on
68
+ // cross-domain requests, so we unify the response:
69
+ if (!response.length || !response[0].firstChild) {
70
+ throw new Error();
71
+ }
72
+ } catch (e) {
73
+ response = undefined;
74
+ }
75
+ // The complete callback returns the
76
+ // iframe content document as response object:
77
+ completeCallback(
78
+ 200,
79
+ 'success',
80
+ {'iframe': response}
81
+ );
82
+ // Fix for IE endless progress bar activity bug
83
+ // (happens on form submits to iframe targets):
84
+ $('<iframe src="javascript:false;"></iframe>')
85
+ .appendTo(form);
86
+ form.remove();
87
+ });
88
+ form
89
+ .prop('target', iframe.prop('name'))
90
+ .prop('action', options.url)
91
+ .prop('method', options.type);
92
+ if (options.formData) {
93
+ $.each(options.formData, function (index, field) {
94
+ $('<input type="hidden"/>')
95
+ .prop('name', field.name)
96
+ .val(field.value)
97
+ .appendTo(form);
98
+ });
99
+ }
100
+ if (options.fileInput && options.fileInput.length &&
101
+ options.type === 'POST') {
102
+ fileInputClones = options.fileInput.clone();
103
+ // Insert a clone for each file input field:
104
+ options.fileInput.after(function (index) {
105
+ return fileInputClones[index];
106
+ });
107
+ if (options.paramName) {
108
+ options.fileInput.each(function (index) {
109
+ $(this).prop(
110
+ 'name',
111
+ paramNames[index] || options.paramName
112
+ );
113
+ });
114
+ }
115
+ // Appending the file input fields to the hidden form
116
+ // removes them from their original location:
117
+ form
118
+ .append(options.fileInput)
119
+ .prop('enctype', 'multipart/form-data')
120
+ // enctype must be set as encoding for IE:
121
+ .prop('encoding', 'multipart/form-data');
122
+ }
123
+ form.submit();
124
+ // Insert the file input fields at their original location
125
+ // by replacing the clones with the originals:
126
+ if (fileInputClones && fileInputClones.length) {
127
+ options.fileInput.each(function (index, input) {
128
+ var clone = $(fileInputClones[index]);
129
+ $(input).prop('name', clone.prop('name'));
130
+ clone.replaceWith(input);
131
+ });
132
+ }
133
+ });
134
+ form.append(iframe).appendTo(document.body);
135
+ },
136
+ abort: function () {
137
+ if (iframe) {
138
+ // javascript:false as iframe src aborts the request
139
+ // and prevents warning popups on HTTPS in IE6.
140
+ // concat is used to avoid the "Script URL" JSLint error:
141
+ iframe
142
+ .unbind('load')
143
+ .prop('src', 'javascript'.concat(':false;'));
144
+ }
145
+ if (form) {
146
+ form.remove();
147
+ }
148
+ }
149
+ };
150
+ }
151
+ });
152
+
153
+ // The iframe transport returns the iframe content document as response.
154
+ // The following adds converters from iframe to text, json, html, and script:
155
+ $.ajaxSetup({
156
+ converters: {
157
+ 'iframe text': function (iframe) {
158
+ return $(iframe[0].body).text();
159
+ },
160
+ 'iframe json': function (iframe) {
161
+ return $.parseJSON($(iframe[0].body).text());
162
+ },
163
+ 'iframe html': function (iframe) {
164
+ return $(iframe[0].body).html();
165
+ },
166
+ 'iframe script': function (iframe) {
167
+ return $.globalEval($(iframe[0].body).text());
168
+ }
169
+ }
170
+ });
171
+
172
+ }));
@@ -0,0 +1,191 @@
1
+ $ ->
2
+ $.widget "neofiles.image",
3
+ options: {},
4
+
5
+ _$transferInput: null,
6
+ _$fileInput: null,
7
+ _$removeButton: null,
8
+ _$optionsButton: null,
9
+ _$descriptionHandle: null,
10
+
11
+ _savedNowmState: null,
12
+ _savedDescription: null,
13
+
14
+ _create: ->
15
+
16
+ $form = @element
17
+
18
+ @_$transferInput = $form.find(".neofiles-image-transfer-input")
19
+ @_$fileInput = $form.find(".neofiles-image-compact-file")
20
+ @_$removeButton = $form.find(".neofiles-image-compact-remove")
21
+ @_$optionsButton = $form.find(".neofiles-image-compact-options")
22
+ @_$descriptionHandle = $form.find(".neofiles-image-compact-description-handle")
23
+
24
+ $form.fileupload
25
+ dropZone: $form,
26
+ pasteZone: $form,
27
+ singleFileUploads: false,
28
+ limitMultiFileUploads: if @_$fileInput.attr("multiple") == "multiple" then 999 else 1,
29
+
30
+ formData: ->
31
+ $form.find("input, select").serializeArray()
32
+
33
+ start: =>
34
+ @loading()
35
+
36
+ success: (response, textStatus, jqXhr)=>
37
+ $form.replaceWith(response)
38
+ @destroy()
39
+
40
+ error: =>
41
+ alert("Ошибка при загрузке файла, попробуйте обновить страницу.\nТакже, проверьте тип файла, загружать можно только картинки.")
42
+ @notloading()
43
+ false
44
+
45
+ @_$removeButton.click (e)=>
46
+ e.preventDefault()
47
+ @remove()
48
+
49
+ @_$optionsButton.popover().on "shown.bs.popover", =>
50
+ @hideOtherActivePopovers()
51
+ @restoreSavedNowmState()
52
+
53
+ @_$optionsButton.click (e)=>
54
+ e.preventDefault()
55
+
56
+ @_$descriptionHandle.popover().on "shown.bs.popover", =>
57
+ @hideOtherActivePopovers()
58
+ @restoreSavedDescription()
59
+ @element.find(".neofiles-image-compact-description-input").redactor
60
+ toolbarFixed: false
61
+ minHeight: 250
62
+ buttons: ['bold', 'italic', 'orderedlist', 'unorderedlist', 'alignment']
63
+
64
+ @_$descriptionHandle.click (e)=>
65
+ e.preventDefault()
66
+
67
+ $form.find(".neofiles-image-compact-upload-icon").click =>
68
+ @_$fileInput.click()
69
+
70
+ $form.on "change", ".neofiles-image-compact-nowm", (e)=>
71
+ @saveNowmState($(e.target))
72
+
73
+ $form.on "click", ".neofiles-image-compact-description-save", (e)=>
74
+ @saveDescription()
75
+
76
+ imageId: ->
77
+ @_$transferInput.val()
78
+
79
+ loading: ->
80
+ @element[0].className += " neofiles-image-compact-loading"
81
+
82
+ notloading: ->
83
+ @element[0].className = @element[0].className.replace(/(^|\s+)neofiles-image-compact-loading/, "")
84
+
85
+ remove: ->
86
+ @loading()
87
+
88
+ data = {}
89
+ @element.find("input[type!=file][name^=neofiles], select[name^=neofiles]").serializeArray().forEach (o)->
90
+ data[o.name] = o.value
91
+
92
+ removeUrl = if @_$removeButton.is("a") then @_$removeButton.attr("href") else @_$removeButton.data("url")
93
+ $.ajax removeUrl,
94
+ type: "POST",
95
+ data: data,
96
+ success: (response)=>
97
+ @element.replaceWith(response)
98
+ @destroy()
99
+
100
+ @_$removeButton.trigger("neofiles.click.remove")
101
+
102
+ hideOtherActivePopovers: ->
103
+ self = @_$optionsButton[0]
104
+ $(".neofiles-image-compact .neofiles-image-compact-options").each ->
105
+ if @ != self
106
+ $(@).popover("hide")
107
+
108
+ restoreSavedNowmState: ->
109
+ if @_savedNowmState != null
110
+ @element.find(".neofiles-image-compact-nowm").prop("checked", @_savedNowmState)
111
+
112
+ restoreSavedDescription: ->
113
+ if @_savedDescription != null
114
+ @element.find(".neofiles-image-compact-description-input").val(@_savedDescription)
115
+
116
+ saveNowmState: ($checkbox)->
117
+ $checkbox.prop("disabled", true)
118
+ formData = @element.find("input, select").serializeArray()
119
+ formData.push name: "neofiles[no_wm]", value: if $checkbox.is(":checked") then "1" else "0"
120
+
121
+ $.ajax($checkbox.data("update-url"), type: "post", data: formData)
122
+ .done =>
123
+ @_savedNowmState = $checkbox.is(":checked")
124
+ .fail ->
125
+ $checkbox.prop("checked", !$checkbox.is(":checked"))
126
+ alert("Ошибка при сохранении, попробуйте еще раз позднее")
127
+ .always ->
128
+ # ответ приходит быстро, бывает не успеваешь заметить моргание disabled/enabled
129
+ setTimeout ->
130
+ $checkbox.prop("disabled", false)
131
+ , 300
132
+
133
+ saveDescription: ->
134
+ $textarea = @element.find(".neofiles-image-compact-description-input")
135
+ $button = @element.find(".neofiles-image-compact-description-save")
136
+
137
+ $textarea.prop("disabled", true)
138
+ formData = @element.find("input, select").serializeArray()
139
+ formData.push name: "neofiles[description]", value: $textarea.val()
140
+
141
+ $button.prop("disabled", true)
142
+
143
+ $.ajax($textarea.data("update-url"), type: "post", data: formData)
144
+
145
+ .done =>
146
+ setTimeout =>
147
+ @_savedDescription = $textarea.val()
148
+ text = $.trim(@_savedDescription).substr(0, 15)
149
+ @_$descriptionHandle.text(text || @_$descriptionHandle.data("empty"))
150
+ @_$descriptionHandle[if text then "removeClass" else "addClass"]("neofiles-image-compact-description-empty")
151
+ @_$descriptionHandle.popover("hide")
152
+ , 300
153
+
154
+ .fail ->
155
+ alert("Ошибка при сохранении, попробуйте еще раз позднее")
156
+
157
+ .always ->
158
+ # ответ приходит быстро, бывает не успеваешь заметить моргание disabled/enabled
159
+ setTimeout ->
160
+ $textarea.prop("disabled", false)
161
+ $button.prop("disabled", false)
162
+ , 300
163
+
164
+
165
+
166
+ $.widget "neofiles.album",
167
+ options: {},
168
+ limit: null,
169
+
170
+ _create: ->
171
+ $form = @element
172
+
173
+ # повесим сортировку на файлы, они сохранятся в том порядке, в котором input:hidden придут в контроллер
174
+ neofilesImageContainersSelector = ".neofiles-image-compact:not(.neofiles-image-compact-empty)"
175
+ $form.sortable
176
+ items: neofilesImageContainersSelector
177
+ handle: ".neofiles-image-compact-view"
178
+
179
+ @limit = @options.limit
180
+
181
+ if @limit
182
+ limit = @limit;
183
+ trigger = (e)->
184
+ ourLimit = (e.data && e.data.limit) || limit
185
+ if $(neofilesImageContainersSelector).length >= ourLimit
186
+ $(".neofiles-image-compact-empty").hide()
187
+ else
188
+ $(".neofiles-image-compact-empty").show()
189
+
190
+ $("body").on("neofiles.newimage", trigger)
191
+ $("body").on("neofiles.click.remove", {limit: limit + 1}, trigger)
@@ -0,0 +1,3 @@
1
+ /*
2
+ *= require ./neofiles
3
+ */
@@ -0,0 +1,149 @@
1
+ .neofiles-image-compact {
2
+ box-sizing: border-box;
3
+ display: inline-block;
4
+ position: relative;
5
+ border: 1px solid #ccc;
6
+ background-color: #fff;
7
+ margin-bottom: 10px;
8
+ vertical-align: top;
9
+ padding: 3px;
10
+ line-height: 1em;
11
+
12
+ .popover-content {
13
+ width: 250px;
14
+ }
15
+ }
16
+ .neofiles-image-compact-with-description {
17
+ margin-bottom: 40px;
18
+ min-width: 100px;
19
+ text-align: center;
20
+ }
21
+ .neofiles-image-compact-empty {
22
+ width: 108px;
23
+ height: 108px;
24
+ }
25
+ .neofiles-image-compact-upload-icon {
26
+ position: absolute;
27
+ box-sizing: border-box;
28
+ bottom: -7px;
29
+ left: 50%;
30
+ margin-left: -7px;
31
+ background-color: #fff;
32
+ border-radius: 7px;
33
+ opacity: .1;
34
+ width: 16px;
35
+ height: 16px;
36
+ text-align: center;
37
+ cursor: pointer;
38
+
39
+ &:hover {
40
+ opacity: 1;
41
+ }
42
+
43
+ .glyphicon {
44
+ top: 0;
45
+ }
46
+
47
+ .neofiles-image-compact-empty & {
48
+ position: static;
49
+ display: block;
50
+ width: 100%;
51
+ height: 100px;
52
+ margin: 0;
53
+ text-align: center;
54
+ padding-top: 42px;
55
+ }
56
+ }
57
+ .neofiles-image-compact-remove, .neofiles-image-compact-options {
58
+ display: block;
59
+ position: absolute;
60
+ box-sizing: border-box;
61
+ width: 20px;
62
+ height: 20px;
63
+ padding-top: 2px;
64
+ right: -10px;
65
+ top: -10px;
66
+ border-radius: 10px;
67
+ text-align: center;
68
+ background-color: #ee5f5b;
69
+ box-shadow: 0px 2px 2px rgba(90, 50, 50, .5);
70
+ opacity: .2;
71
+
72
+ .glyphicon {
73
+ top: 1px;
74
+ font-size: 12px;
75
+ line-height: 12px;
76
+ color: #000;
77
+ text-shadow: 0 1px 1px rgba(255, 255, 255, .3);
78
+ }
79
+
80
+ &:hover {
81
+ opacity: 1;
82
+ }
83
+ }
84
+ .neofiles-image-compact-options {
85
+ top: 12px;
86
+ background-color: #fff;
87
+ }
88
+ INPUT.neofiles-image-compact-file {
89
+ display: none !important;
90
+ }
91
+ .neofiles-image-compact-loading {
92
+ opacity: .5;
93
+ background: image-path('neofiles/loading.gif') no-repeat center center;
94
+
95
+ .neofiles-image-compact-upload-icon,
96
+ .neofiles-image-compact-remove,
97
+ .neofiles-image-compact-options,
98
+ .neofiles-image-compact-file {
99
+ visibility: hidden;
100
+ }
101
+ }
102
+ .neofiles-image-compact-description {
103
+ position: absolute;
104
+ bottom: -25px;
105
+ width: 100%;
106
+ font-size: 12px;
107
+ margin-left: -4px;
108
+ line-height: 14px;
109
+ text-align: center;
110
+ }
111
+ .neofiles-image-compact-description-empty {
112
+ color: #ccc !important;
113
+ border-bottom-color: #ddd !important;
114
+ }
115
+ .neofiles-image-compact-description-handle {
116
+ color: #333;
117
+ border-bottom: 1px dotted #777;
118
+ text-decoration: none !important;
119
+
120
+ &:hover {
121
+ border-bottom: 0;
122
+ text-decoration: none !important;
123
+ }
124
+ }
125
+ .popover.neofiles-image-compact-description-popover {
126
+ width: 430px;
127
+ height: 400px;
128
+ max-width: none;
129
+
130
+ .popover-content {
131
+ width: 100%;
132
+ }
133
+ }
134
+ .neofiles-image-compact-description-input {
135
+ width: 400px;
136
+ height: 250px;
137
+ }
138
+
139
+ .neofiles-album-compact {
140
+ margin-bottom: 10px;
141
+ }
142
+ .neofiles-album-compact .neofiles-image-compact {
143
+ margin-right: 20px;
144
+ margin-bottom: 20px;
145
+
146
+ &.neofiles-image-compact-with-description {
147
+ margin-bottom: 50px;
148
+ }
149
+ }