cas-cms 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +43 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/config/cas_manifest.js +2 -0
  6. data/app/assets/images/cas/black-star.svg +4 -0
  7. data/app/assets/images/cas/check.svg +4 -0
  8. data/app/assets/images/cas/dropzone-in.svg +4 -0
  9. data/app/assets/images/cas/loading.gif +0 -0
  10. data/app/assets/images/cas/white-star.svg +4 -0
  11. data/app/assets/javascripts/cas/application.js +51 -0
  12. data/app/assets/javascripts/cas/fileupload_manifest.js +269 -0
  13. data/app/assets/javascripts/cas/plugins/cas_image_gallery.js +353 -0
  14. data/app/assets/javascripts/cas/vendor/file_upload/canvas-to-blob.min.js +2 -0
  15. data/app/assets/javascripts/cas/vendor/file_upload/jquery.fileupload-image.js +326 -0
  16. data/app/assets/javascripts/cas/vendor/file_upload/jquery.fileupload-process.js +178 -0
  17. data/app/assets/javascripts/cas/vendor/file_upload/jquery.fileupload.js +1482 -0
  18. data/app/assets/javascripts/cas/vendor/file_upload/jquery.iframe-transport.js +224 -0
  19. data/app/assets/javascripts/cas/vendor/file_upload/jquery.ui.widget.js +572 -0
  20. data/app/assets/javascripts/cas/vendor/file_upload/load-image.all.min.js +2 -0
  21. data/app/assets/javascripts/cas/vendor/jquery.ui.touch-punch.min.js +11 -0
  22. data/app/assets/javascripts/cas/vendor/selectize.min.js +3 -0
  23. data/app/assets/stylesheets/cas/application.sass +17 -0
  24. data/app/assets/stylesheets/cas/colors.sass +6 -0
  25. data/app/assets/stylesheets/cas/form.sass +99 -0
  26. data/app/assets/stylesheets/cas/layout.sass +68 -0
  27. data/app/assets/stylesheets/cas/mixins.sass +34 -0
  28. data/app/assets/stylesheets/cas/plugins/attachments_form.sass +16 -0
  29. data/app/assets/stylesheets/cas/plugins/image_gallery.sass +128 -0
  30. data/app/assets/stylesheets/cas/tables.sass +13 -0
  31. data/app/assets/stylesheets/cas/typography.sass +27 -0
  32. data/app/assets/stylesheets/cas/vendors/selectize.default.css +394 -0
  33. data/app/assets/stylesheets/cas/vendors/simplegrid.css +298 -0
  34. data/app/controllers/cas/api/files_controller.rb +88 -0
  35. data/app/controllers/cas/application_controller.rb +17 -0
  36. data/app/controllers/cas/devise/sessions_controller.rb +9 -0
  37. data/app/controllers/cas/file_uploads_controller.rb +14 -0
  38. data/app/controllers/cas/sections/application_controller.rb +23 -0
  39. data/app/controllers/cas/sections/categories_controller.rb +47 -0
  40. data/app/controllers/cas/sections/contents_controller.rb +144 -0
  41. data/app/controllers/cas/sections_controller.rb +7 -0
  42. data/app/controllers/cas/users_controller.rb +61 -0
  43. data/app/helpers/cas/application_helper.rb +4 -0
  44. data/app/helpers/cas/form_helper.rb +11 -0
  45. data/app/jobs/cas/application_job.rb +4 -0
  46. data/app/jobs/cas/images/delete_job.rb +11 -0
  47. data/app/jobs/cas/images/promote_job.rb +11 -0
  48. data/app/jobs/cas/increment_pageviews_job.rb +11 -0
  49. data/app/models/cas/application_record.rb +5 -0
  50. data/app/models/cas/category.rb +6 -0
  51. data/app/models/cas/content.rb +59 -0
  52. data/app/models/cas/media_file.rb +77 -0
  53. data/app/models/cas/section.rb +10 -0
  54. data/app/models/cas/site.rb +5 -0
  55. data/app/models/cas/user.rb +39 -0
  56. data/app/uploaders/file_uploader.rb +26 -0
  57. data/app/views/cas/devise/sessions/new.html.erb +24 -0
  58. data/app/views/cas/sections/categories/_form.html.erb +19 -0
  59. data/app/views/cas/sections/categories/edit.html.erb +5 -0
  60. data/app/views/cas/sections/categories/index.html.erb +28 -0
  61. data/app/views/cas/sections/categories/new.html.erb +5 -0
  62. data/app/views/cas/sections/contents/_form_attachments.html.erb +42 -0
  63. data/app/views/cas/sections/contents/_form_attachments_template.html.erb +11 -0
  64. data/app/views/cas/sections/contents/_form_for_content.html.erb +85 -0
  65. data/app/views/cas/sections/contents/_form_for_survey.html.erb +36 -0
  66. data/app/views/cas/sections/contents/_form_images.html.erb +86 -0
  67. data/app/views/cas/sections/contents/edit.html.erb +6 -0
  68. data/app/views/cas/sections/contents/index.html.erb +71 -0
  69. data/app/views/cas/sections/contents/new.html.erb +5 -0
  70. data/app/views/cas/sections/index.html.erb +21 -0
  71. data/app/views/cas/shared/_error_messages.html.erb +12 -0
  72. data/app/views/cas/users/_form.html.erb +11 -0
  73. data/app/views/cas/users/edit.html.erb +2 -0
  74. data/app/views/cas/users/index.html.erb +34 -0
  75. data/app/views/cas/users/new.html.erb +2 -0
  76. data/app/views/layouts/cas/application.html.erb +71 -0
  77. data/config/initializers/acts_as_taggable.rb +1 -0
  78. data/config/initializers/devise.rb +277 -0
  79. data/config/initializers/shrine.rb +50 -0
  80. data/config/initializers/simple_form.rb +169 -0
  81. data/config/locales/devise.en.yml +64 -0
  82. data/config/locales/pt-BR.yml +242 -0
  83. data/config/locales/simple_form.en.yml +31 -0
  84. data/config/routes.rb +31 -0
  85. data/db/migrate/20170129212144_create_cas_sites.rb +15 -0
  86. data/db/migrate/20170219172958_create_cas_sections.rb +12 -0
  87. data/db/migrate/20170219175007_create_cas_contents.rb +17 -0
  88. data/db/migrate/20170612204500_create_cas_users.rb +14 -0
  89. data/db/migrate/20170613174724_create_cas_media_files.rb +20 -0
  90. data/db/migrate/20170613175912_create_cas_categories.rb +14 -0
  91. data/db/migrate/20170614171928_add_columns_to_cas_contents.rb +12 -0
  92. data/db/migrate/20170614172904_add_column_to_cas_sections.rb +6 -0
  93. data/db/migrate/20170615235532_add_devise_to_cas_users.rb +49 -0
  94. data/db/migrate/20170616011202_remove_password_from_cas_users.rb +5 -0
  95. data/db/migrate/20170618014204_v3_migration_fields.rb +30 -0
  96. data/db/migrate/20170623182702_add_column_category_id_to_cas_content.rb +6 -0
  97. data/db/migrate/20170624024648_rename_url_to_path_in_files.rb +7 -0
  98. data/db/migrate/20170625192119_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb +36 -0
  99. data/db/migrate/20170625192120_add_missing_unique_indices.acts_as_taggable_on_engine.rb +26 -0
  100. data/db/migrate/20170625192121_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb +20 -0
  101. data/db/migrate/20170625192122_add_missing_taggable_index.acts_as_taggable_on_engine.rb +15 -0
  102. data/db/migrate/20170625192123_change_collation_for_tag_names.acts_as_taggable_on_engine.rb +15 -0
  103. data/db/migrate/20170625192124_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb +23 -0
  104. data/db/migrate/20170713210101_add_location_to_cas_content.rb +5 -0
  105. data/db/migrate/20170714043036_add_url_to_content.rb +5 -0
  106. data/db/migrate/20170718201535_add_embedded_to_content.rb +5 -0
  107. data/db/migrate/20170801171952_categories_with_jsonb_metadata.rb +6 -0
  108. data/db/migrate/20170801173256_add_published_at_to_cas_contents.rb +6 -0
  109. data/db/migrate/20170801175407_add_description_to_cas_categories.rb +5 -0
  110. data/db/migrate/20170830000000_add_data_search_extensions.rb +17 -0
  111. data/db/migrate/20170830000001_add_search_index_to_cas_contents.rb +20 -0
  112. data/db/migrate/20170830000002_use_uuid_with_acts_as_taggable_references.rb +17 -0
  113. data/db/migrate/20170919181809_pageviews_default_to_zero.rb +9 -0
  114. data/db/migrate/20171201191059_add_domains_to_cas_site.rb +6 -0
  115. data/lib/cas.rb +22 -0
  116. data/lib/cas/config.rb +42 -0
  117. data/lib/cas/engine.rb +33 -0
  118. data/lib/cas/form_field.rb +54 -0
  119. data/lib/cas/remote_callbacks.rb +36 -0
  120. data/lib/cas/section_config.rb +84 -0
  121. data/lib/cas/setup.rb +43 -0
  122. data/lib/cas/version.rb +3 -0
  123. data/lib/devise/custom_failure.rb +16 -0
  124. data/lib/tasks/cas_tasks.rake +4 -0
  125. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  126. metadata +560 -0
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Guess what, Javascript doesn't have .sort() by integer out-of-the-box.
3
+ */
4
+ function sortNumber(a, b) {
5
+ return parseInt(a) - parseInt(b);
6
+ }
7
+
8
+ function CasImage(element, gallery) {
9
+ var image = this;
10
+ image.element = element;
11
+ image.gallery = gallery;
12
+
13
+ image.id = image.element.attr("data-id");
14
+
15
+ image.isSelected = function() {
16
+ return image.element.attr("data-selected") == "true";
17
+ };
18
+
19
+ image.markAsSelected = function() {
20
+ image.element.attr("data-selected", "true");
21
+ };
22
+
23
+ image.unselect = function() {
24
+ image.element.find(".selection").hide();
25
+ image.element.attr("data-selected", "false");
26
+ image.element.find(".unselected-but-selectable").show();
27
+ };
28
+
29
+ image.toggleSelection = function() {
30
+ if (image.isSelected()) {
31
+ image.unselect();
32
+ } else {
33
+ image.markAsSelected();
34
+ image.element.find(".unselected-but-selectable").hide();
35
+ image.element.find(".selection").show();
36
+ }
37
+ };
38
+
39
+ image.showEditOptions = function() {
40
+ gallery.element.find(".image .image-options").hide();
41
+ image.element.find(".image .image-options").show();
42
+ image.markAsSelected();
43
+ };
44
+
45
+ return {
46
+ click: function() {
47
+ if (image.gallery.isSelectionEnabled) {
48
+ image.toggleSelection();
49
+ } else {
50
+ image.showEditOptions();
51
+ }
52
+ },
53
+ unselect: function() {
54
+ if (image.isSelected()) {
55
+ image.unselect();
56
+ }
57
+ },
58
+ recordId: function() {
59
+ return image.element.data("id");
60
+ },
61
+ addOrderedInput: function(order) {
62
+ if (!image.element.find("input.js-image-input").length) {
63
+ image.element.append("<input type='hidden' name='images[files]["+order+"][id]' class='js-image-input' value='"+image.id+"' />");
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ function CasImageGallery(options) {
70
+ var gallery = this;
71
+ gallery.element = options.element;
72
+
73
+ gallery.isSelectionEnabled = options.selectionEnabled || false;
74
+ gallery.preloadedImagesSelector = options.preloadedImagesElements || gallery.element.find(".cas-gallery-preloaded");
75
+
76
+ gallery.allItems = function() {
77
+ return gallery.element.find(".image-container");
78
+ };
79
+
80
+ gallery.unselectAll = function() {
81
+ gallery.allItems().each(function(index) {
82
+ var image = new CasImage($(this), gallery);
83
+ image.unselect();
84
+ });
85
+ };
86
+
87
+ gallery.getSelectedItems = function() {
88
+ var result = [];
89
+ return gallery.element.find(".image-container[data-selected=true]").map(function(index, value) {
90
+ return $(value)
91
+ });
92
+ };
93
+
94
+ gallery.toggleSelection = function() {
95
+ gallery.isSelectionEnabled = !gallery.isSelectionEnabled;
96
+
97
+ if (!gallery.isSelectionEnabled) {
98
+ gallery.unselectAll();
99
+ } else {
100
+ gallery.unselectAll();
101
+ gallery.element.find(".unselected-but-selectable").show();
102
+ }
103
+
104
+ gallery.updateUi();
105
+ };
106
+
107
+ gallery.markAsCover = function(id) {
108
+ gallery.element.find(".image-container").attr("data-is-cover", "false");
109
+ gallery.element.find(".image-container[data-id="+id+"]").attr("data-is-cover", "true");
110
+ gallery.element.find("input.js-image-cover-input").remove();
111
+ gallery.element.append("<input type='hidden' name='images[cover_id]' class='js-image-cover-input' value='"+id+"' />");
112
+ };
113
+
114
+ gallery.deleteImages = function() {
115
+ var ids = [];
116
+ gallery.getSelectedItems().each(function(index, imageElement) {
117
+ var image = new CasImage(imageElement, gallery);
118
+ ids.push( String(image.recordId()) );
119
+ });
120
+ var response = options.onDelete(ids);
121
+
122
+ if (response) {
123
+ response.done(function() {
124
+ var imageSelectors = [];
125
+ $.each(ids, function(index, value) {
126
+ imageSelectors.push(".image-container[data-id="+value+"]");
127
+ });
128
+ gallery.element.find(imageSelectors.join(", ")).remove();
129
+ gallery.unselectAll();
130
+ if (gallery.isSelectionEnabled) {
131
+ gallery.toggleSelection();
132
+ }
133
+ }).fail(function() {
134
+ alert("Error!");
135
+ });
136
+ }
137
+ };
138
+
139
+ gallery.imageClicked = function(element) {
140
+ if (!gallery.isSelectionEnabled) {
141
+ gallery.unselectAll();
142
+ }
143
+
144
+ var image = new CasImage($(element), gallery);
145
+ image.click();
146
+ gallery.updateUi();
147
+ };
148
+
149
+ gallery.setupEvents = function() {
150
+ options.element.on("click", ".image-container", function() {
151
+ gallery.imageClicked($(this));
152
+ });
153
+
154
+ options.element.on("click", ".js-toggle-selection", function() {
155
+ gallery.toggleSelection();
156
+ });
157
+
158
+ options.element.on("click", ".js-delete-image", function() {
159
+ gallery.deleteImages();
160
+ });
161
+
162
+ options.element.on("click", ".js-mark-as-cover", function() {
163
+ var id = $(this).parents(".image-container").data("id");
164
+ gallery.markAsCover(id);
165
+ });
166
+
167
+ /*
168
+ * There's a bug with dragleave when over child elements, flickering. This
169
+ * fixes it.
170
+ */
171
+ var dropzoneLastEnter;
172
+ $(document)
173
+ .on('dragover dragenter', ".dropzone", function(e) {
174
+ dropzoneLastEnter = event.target;
175
+ e.preventDefault();
176
+
177
+ var dataTransfer = e.originalEvent.dataTransfer;
178
+ if (dataTransfer.types && (dataTransfer.types.indexOf ? dataTransfer.types.indexOf('Files') != -1 : dataTransfer.types.contains('Files'))) {
179
+ $(this).addClass('in');
180
+ }
181
+ })
182
+ .on('dragleave drop', ".dropzone", function(e) {
183
+ if (dropzoneLastEnter === event.target) {
184
+ $(this).removeClass('in');
185
+ }
186
+ });
187
+ };
188
+
189
+ gallery.preloadImages = function() {
190
+ var images = gallery.preloadedImagesSelector;
191
+
192
+ $.each(images, function(index, value) {
193
+ var image = $(value);
194
+ var url = image.data("src");
195
+ var id = image.data("id");
196
+ var filename = image.data("filename");
197
+ var originalOrder = image.data("original-order");
198
+ var isCover = image.data("is-cover");
199
+ gallery.addImage({
200
+ url: url,
201
+ id: id,
202
+ filename: filename,
203
+ orderBy: "order",
204
+ originalOrder: originalOrder,
205
+ isCover: isCover
206
+ });
207
+ });
208
+ };
209
+
210
+ gallery.imageTemplate = $("#cas-gallery-image-template").html();
211
+ gallery.addImage = function(options) {
212
+ var url = options.url,
213
+ id = options.id,
214
+ filename = options.filename,
215
+ orderBy = options.orderBy,
216
+ // order that comes from the server
217
+ originalOrder = options.originalOrder,
218
+ isCover = options.isCover;
219
+
220
+ /**
221
+ * The new image information/HTML code
222
+ */
223
+ var finalHtml = $(gallery.imageTemplate);
224
+ finalHtml.attr("data-id", id);
225
+ finalHtml.attr("data-filename", filename);
226
+ finalHtml.attr("data-original-order", originalOrder);
227
+ finalHtml.find(".image").css("backgroundImage", "url('"+url+"')");
228
+
229
+ /**
230
+ * We need to add images in order by filename. The sorting algorithm
231
+ * compares current image with the new image, and when the new image is
232
+ * going to be the last one, the algorithm in the loop below doesn't
233
+ * add it to the list of images.
234
+ *
235
+ * So we create `addedImage` to false. In case it's still false after the
236
+ * loop, we append the file to the end of the list.
237
+ */
238
+ var addedImage = false;
239
+ gallery.allItems().each(function(index) {
240
+ if (addedImage === true) {
241
+ return false;
242
+ }
243
+ var ordering;
244
+
245
+ if (orderBy == "filename") {
246
+ var listedItemFilename = $(this).data("filename");
247
+ ordering = [listedItemFilename, filename].sort();
248
+
249
+ /**
250
+ * We compare the crrent file name and new file name. We sort them
251
+ * both.
252
+ *
253
+ * Say current images are 3.jpg and 6.jpg and new image is 5.jpg.
254
+ * We compare ["3.jpg", "5.jpg"].sort() and see that element zero is not
255
+ * 5.jpg. We discard it.
256
+ *
257
+ * Then ["6.jpg", "5.jpg"].sort() and "5.jpg" is the first element,
258
+ * therefore we surpassed the position 5.jpg should be in. At this
259
+ * point we take 5.jpg and `insertBefore()` 6.jpg.
260
+ */
261
+ if (ordering[0] === filename) {
262
+ finalHtml.insertBefore($(this));
263
+ addedImage = true;
264
+ return;
265
+ }
266
+ } else if (orderBy == "order") {
267
+ var listedItemOrder = $(this).data("original-order");
268
+ ordering = [listedItemOrder, originalOrder].sort(sortNumber);
269
+
270
+ /**
271
+ * We compare the crrent file name and new file name. We sort them
272
+ * both.
273
+ *
274
+ * Say current images are 3.jpg and 6.jpg and new image is 5.jpg.
275
+ * We compare ["3.jpg", "5.jpg"].sort() and see that element zero is not
276
+ * 5.jpg. We discard it.
277
+ *
278
+ * Then ["6.jpg", "5.jpg"].sort() and "5.jpg" is the first element,
279
+ * therefore we surpassed the position 5.jpg should be in. At this
280
+ * point we take 5.jpg and `insertBefore()` 6.jpg.
281
+ */
282
+ if (ordering[0] === originalOrder) {
283
+ finalHtml.insertBefore($(this));
284
+ addedImage = true;
285
+ return;
286
+ }
287
+ }
288
+ });
289
+
290
+ if (addedImage === false) {
291
+ gallery.element.find(".images").append(finalHtml);
292
+ }
293
+
294
+ gallery.refreshImagesFormInputs();
295
+ gallery.unselectAll();
296
+
297
+ if (isCover) {
298
+ gallery.markAsCover(id);
299
+ }
300
+
301
+ gallery.updateUi();
302
+ };
303
+
304
+ gallery.refreshImagesFormInputs = function() {
305
+ $(".js-image-input").remove();
306
+ gallery.allItems().each(function(index) {
307
+ var image = new CasImage($(this), gallery);
308
+ image.addOrderedInput(index);
309
+ });
310
+ };
311
+
312
+ gallery.updateUi = function() {
313
+ if (gallery.isSelectionEnabled) {
314
+ gallery.element.find(".when-selection-enabled").show();
315
+ gallery.element.find(".when-selection-disabled").hide();
316
+ gallery.element.find(".image .image-options").hide();
317
+ } else {
318
+ gallery.element.find(".unselected-but-selectable").hide();
319
+ gallery.element.find(".when-selection-enabled").hide();
320
+ gallery.element.find(".when-selection-disabled").show();
321
+ }
322
+ };
323
+
324
+ return {
325
+ init: function() {
326
+ gallery.preloadImages();
327
+ gallery.setupEvents();
328
+ gallery.updateUi();
329
+ },
330
+
331
+ addImage: function(options) {
332
+ gallery.addImage({
333
+ url: options.url,
334
+ id: options.id,
335
+ filename: options.filename,
336
+ orderBy: options.orderBy,
337
+ isCover: options.isCover
338
+ });
339
+ },
340
+
341
+ resetImagesOrder: function() {
342
+ gallery.element.find("input.js-image-input").remove();
343
+ gallery.refreshImagesFormInputs();
344
+ },
345
+
346
+ attachable: function() {
347
+ return {
348
+ type: gallery.element.data("attachable-type"),
349
+ id: gallery.element.data("attachable-id")
350
+ };
351
+ }
352
+ }
353
+ }
@@ -0,0 +1,2 @@
1
+ !function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,o=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),n=o&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,a=/^data:((.*?)(;charset=.*?)?)(;base64)?,/,i=(o||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,l,u,b,c,d,B,f;if(e=t.match(a),!e)throw new Error("invalid data URI");for(i=e[2]?e[1]:"text/plain"+(e[3]||";charset=US-ASCII"),l=!!e[4],u=t.slice(e[0].length),b=l?atob(u):decodeURIComponent(u),c=new ArrayBuffer(b.length),d=new Uint8Array(c),B=0;B<b.length;B+=1)d[B]=b.charCodeAt(B);return o?new Blob([n?d:c],{type:i}):(f=new r,f.append(c),f.getBlob(i))};t.HTMLCanvasElement&&!e.toBlob&&(e.mozGetAsFile?e.toBlob=function(t,o,n){t(n&&e.toDataURL&&i?i(this.toDataURL(o,n)):this.mozGetAsFile("blob",o))}:e.toDataURL&&i&&(e.toBlob=function(t,e,o){t(i(this.toDataURL(e,o)))})),"function"==typeof define&&define.amd?define(function(){return i}):"object"==typeof module&&module.exports?module.exports=i:t.dataURLtoBlob=i}(window);
2
+ //# sourceMappingURL=canvas-to-blob.min.js.map
@@ -0,0 +1,326 @@
1
+ /*
2
+ * jQuery File Upload Image Preview & Resize Plugin
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2013, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * https://opensource.org/licenses/MIT
10
+ */
11
+
12
+ /* jshint nomen:false */
13
+ /* global define, require, window, Blob */
14
+
15
+ ;(function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'load-image',
22
+ 'load-image-meta',
23
+ 'load-image-scale',
24
+ 'load-image-exif',
25
+ 'canvas-to-blob',
26
+ './jquery.fileupload-process'
27
+ ], factory);
28
+ } else if (typeof exports === 'object') {
29
+ // Node/CommonJS:
30
+ factory(
31
+ require('jquery'),
32
+ require('blueimp-load-image/js/load-image'),
33
+ require('blueimp-load-image/js/load-image-meta'),
34
+ require('blueimp-load-image/js/load-image-scale'),
35
+ require('blueimp-load-image/js/load-image-exif'),
36
+ require('blueimp-canvas-to-blob'),
37
+ require('./jquery.fileupload-process')
38
+ );
39
+ } else {
40
+ // Browser globals:
41
+ factory(
42
+ window.jQuery,
43
+ window.loadImage
44
+ );
45
+ }
46
+ }(function ($, loadImage) {
47
+ 'use strict';
48
+
49
+ // Prepend to the default processQueue:
50
+ $.blueimp.fileupload.prototype.options.processQueue.unshift(
51
+ {
52
+ action: 'loadImageMetaData',
53
+ disableImageHead: '@',
54
+ disableExif: '@',
55
+ disableExifThumbnail: '@',
56
+ disableExifSub: '@',
57
+ disableExifGps: '@',
58
+ disabled: '@disableImageMetaDataLoad'
59
+ },
60
+ {
61
+ action: 'loadImage',
62
+ // Use the action as prefix for the "@" options:
63
+ prefix: true,
64
+ fileTypes: '@',
65
+ maxFileSize: '@',
66
+ noRevoke: '@',
67
+ disabled: '@disableImageLoad'
68
+ },
69
+ {
70
+ action: 'resizeImage',
71
+ // Use "image" as prefix for the "@" options:
72
+ prefix: 'image',
73
+ maxWidth: '@',
74
+ maxHeight: '@',
75
+ minWidth: '@',
76
+ minHeight: '@',
77
+ crop: '@',
78
+ orientation: '@',
79
+ forceResize: '@',
80
+ disabled: '@disableImageResize'
81
+ },
82
+ {
83
+ action: 'saveImage',
84
+ quality: '@imageQuality',
85
+ type: '@imageType',
86
+ disabled: '@disableImageResize'
87
+ },
88
+ {
89
+ action: 'saveImageMetaData',
90
+ disabled: '@disableImageMetaDataSave'
91
+ },
92
+ {
93
+ action: 'resizeImage',
94
+ // Use "preview" as prefix for the "@" options:
95
+ prefix: 'preview',
96
+ maxWidth: '@',
97
+ maxHeight: '@',
98
+ minWidth: '@',
99
+ minHeight: '@',
100
+ crop: '@',
101
+ orientation: '@',
102
+ thumbnail: '@',
103
+ canvas: '@',
104
+ disabled: '@disableImagePreview'
105
+ },
106
+ {
107
+ action: 'setImage',
108
+ name: '@imagePreviewName',
109
+ disabled: '@disableImagePreview'
110
+ },
111
+ {
112
+ action: 'deleteImageReferences',
113
+ disabled: '@disableImageReferencesDeletion'
114
+ }
115
+ );
116
+
117
+ // The File Upload Resize plugin extends the fileupload widget
118
+ // with image resize functionality:
119
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
120
+
121
+ options: {
122
+ // The regular expression for the types of images to load:
123
+ // matched against the file type:
124
+ loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/,
125
+ // The maximum file size of images to load:
126
+ loadImageMaxFileSize: 10000000, // 10MB
127
+ // The maximum width of resized images:
128
+ imageMaxWidth: 1920,
129
+ // The maximum height of resized images:
130
+ imageMaxHeight: 1080,
131
+ // Defines the image orientation (1-8) or takes the orientation
132
+ // value from Exif data if set to true:
133
+ imageOrientation: false,
134
+ // Define if resized images should be cropped or only scaled:
135
+ imageCrop: false,
136
+ // Disable the resize image functionality by default:
137
+ disableImageResize: true,
138
+ // The maximum width of the preview images:
139
+ previewMaxWidth: 80,
140
+ // The maximum height of the preview images:
141
+ previewMaxHeight: 80,
142
+ // Defines the preview orientation (1-8) or takes the orientation
143
+ // value from Exif data if set to true:
144
+ previewOrientation: true,
145
+ // Create the preview using the Exif data thumbnail:
146
+ previewThumbnail: true,
147
+ // Define if preview images should be cropped or only scaled:
148
+ previewCrop: false,
149
+ // Define if preview images should be resized as canvas elements:
150
+ previewCanvas: true
151
+ },
152
+
153
+ processActions: {
154
+
155
+ // Loads the image given via data.files and data.index
156
+ // as img element, if the browser supports the File API.
157
+ // Accepts the options fileTypes (regular expression)
158
+ // and maxFileSize (integer) to limit the files to load:
159
+ loadImage: function (data, options) {
160
+ if (options.disabled) {
161
+ return data;
162
+ }
163
+ var that = this,
164
+ file = data.files[data.index],
165
+ dfd = $.Deferred();
166
+ if (($.type(options.maxFileSize) === 'number' &&
167
+ file.size > options.maxFileSize) ||
168
+ (options.fileTypes &&
169
+ !options.fileTypes.test(file.type)) ||
170
+ !loadImage(
171
+ file,
172
+ function (img) {
173
+ if (img.src) {
174
+ data.img = img;
175
+ }
176
+ dfd.resolveWith(that, [data]);
177
+ },
178
+ options
179
+ )) {
180
+ return data;
181
+ }
182
+ return dfd.promise();
183
+ },
184
+
185
+ // Resizes the image given as data.canvas or data.img
186
+ // and updates data.canvas or data.img with the resized image.
187
+ // Also stores the resized image as preview property.
188
+ // Accepts the options maxWidth, maxHeight, minWidth,
189
+ // minHeight, canvas and crop:
190
+ resizeImage: function (data, options) {
191
+ if (options.disabled || !(data.canvas || data.img)) {
192
+ return data;
193
+ }
194
+ options = $.extend({canvas: true}, options);
195
+ var that = this,
196
+ dfd = $.Deferred(),
197
+ img = (options.canvas && data.canvas) || data.img,
198
+ resolve = function (newImg) {
199
+ if (newImg && (newImg.width !== img.width ||
200
+ newImg.height !== img.height ||
201
+ options.forceResize)) {
202
+ data[newImg.getContext ? 'canvas' : 'img'] = newImg;
203
+ }
204
+ data.preview = newImg;
205
+ dfd.resolveWith(that, [data]);
206
+ },
207
+ thumbnail;
208
+ if (data.exif) {
209
+ if (options.orientation === true) {
210
+ options.orientation = data.exif.get('Orientation');
211
+ }
212
+ if (options.thumbnail) {
213
+ thumbnail = data.exif.get('Thumbnail');
214
+ if (thumbnail) {
215
+ loadImage(thumbnail, resolve, options);
216
+ return dfd.promise();
217
+ }
218
+ }
219
+ // Prevent orienting the same image twice:
220
+ if (data.orientation) {
221
+ delete options.orientation;
222
+ } else {
223
+ data.orientation = options.orientation;
224
+ }
225
+ }
226
+ if (img) {
227
+ resolve(loadImage.scale(img, options));
228
+ return dfd.promise();
229
+ }
230
+ return data;
231
+ },
232
+
233
+ // Saves the processed image given as data.canvas
234
+ // inplace at data.index of data.files:
235
+ saveImage: function (data, options) {
236
+ if (!data.canvas || options.disabled) {
237
+ return data;
238
+ }
239
+ var that = this,
240
+ file = data.files[data.index],
241
+ dfd = $.Deferred();
242
+ if (data.canvas.toBlob) {
243
+ data.canvas.toBlob(
244
+ function (blob) {
245
+ if (!blob.name) {
246
+ if (file.type === blob.type) {
247
+ blob.name = file.name;
248
+ } else if (file.name) {
249
+ blob.name = file.name.replace(
250
+ /\.\w+$/,
251
+ '.' + blob.type.substr(6)
252
+ );
253
+ }
254
+ }
255
+ // Don't restore invalid meta data:
256
+ if (file.type !== blob.type) {
257
+ delete data.imageHead;
258
+ }
259
+ // Store the created blob at the position
260
+ // of the original file in the files list:
261
+ data.files[data.index] = blob;
262
+ dfd.resolveWith(that, [data]);
263
+ },
264
+ options.type || file.type,
265
+ options.quality
266
+ );
267
+ } else {
268
+ return data;
269
+ }
270
+ return dfd.promise();
271
+ },
272
+
273
+ loadImageMetaData: function (data, options) {
274
+ if (options.disabled) {
275
+ return data;
276
+ }
277
+ var that = this,
278
+ dfd = $.Deferred();
279
+ loadImage.parseMetaData(data.files[data.index], function (result) {
280
+ $.extend(data, result);
281
+ dfd.resolveWith(that, [data]);
282
+ }, options);
283
+ return dfd.promise();
284
+ },
285
+
286
+ saveImageMetaData: function (data, options) {
287
+ if (!(data.imageHead && data.canvas &&
288
+ data.canvas.toBlob && !options.disabled)) {
289
+ return data;
290
+ }
291
+ var file = data.files[data.index],
292
+ blob = new Blob([
293
+ data.imageHead,
294
+ // Resized images always have a head size of 20 bytes,
295
+ // including the JPEG marker and a minimal JFIF header:
296
+ this._blobSlice.call(file, 20)
297
+ ], {type: file.type});
298
+ blob.name = file.name;
299
+ data.files[data.index] = blob;
300
+ return data;
301
+ },
302
+
303
+ // Sets the resized version of the image as a property of the
304
+ // file object, must be called after "saveImage":
305
+ setImage: function (data, options) {
306
+ if (data.preview && !options.disabled) {
307
+ data.files[data.index][options.name || 'preview'] = data.preview;
308
+ }
309
+ return data;
310
+ },
311
+
312
+ deleteImageReferences: function (data, options) {
313
+ if (!options.disabled) {
314
+ delete data.img;
315
+ delete data.canvas;
316
+ delete data.preview;
317
+ delete data.imageHead;
318
+ }
319
+ return data;
320
+ }
321
+
322
+ }
323
+
324
+ });
325
+
326
+ }));