para 0.6.3 → 0.6.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/para/admin/job-tracker.coffee +1 -1
  3. data/app/assets/javascripts/para/admin/tabs.coffee +14 -8
  4. data/app/assets/javascripts/para/admin.js +20 -0
  5. data/app/assets/javascripts/para/plugins-includes.js.erb +1 -0
  6. data/app/assets/stylesheets/para/admin.sass +1 -0
  7. data/app/assets/stylesheets/para/plugins-includes.sass.erb +1 -0
  8. data/app/controllers/concerns/para/admin/resource_controller_concerns.rb +8 -2
  9. data/app/controllers/para/admin/crud_resources_controller.rb +1 -1
  10. data/app/controllers/para/admin/exports_controller.rb +1 -1
  11. data/app/controllers/para/admin/resources_controller.rb +7 -2
  12. data/app/decorators/para/component/base_decorator.rb +2 -2
  13. data/app/helpers/para/admin/pagination_helper.rb +31 -0
  14. data/app/helpers/para/model_helper.rb +14 -4
  15. data/app/models/application_record.rb +3 -0
  16. data/app/models/para/cache/item.rb +27 -0
  17. data/app/models/para/page/section.rb +0 -6
  18. data/app/views/para/admin/resources/_exports_menu.html.haml +2 -2
  19. data/app/views/para/admin/resources/_list.html.haml +1 -1
  20. data/app/views/para/admin/resources/_per_page_select.html.haml +10 -0
  21. data/app/views/para/inputs/_nested_many.html.haml +1 -1
  22. data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +1 -1
  23. data/config/locales/fr.yml +1 -0
  24. data/db/migrate/20161006105728_create_para_cache_items.rb +12 -0
  25. data/lib/generators/para/admin_user/admin_user_generator.rb +2 -1
  26. data/lib/generators/para/exporter/exporter_generator.rb +1 -5
  27. data/lib/generators/para/exporter/templates/base_exporter.rb +18 -12
  28. data/lib/generators/para/exporter/templates/csv_exporter.rb +35 -11
  29. data/lib/generators/para/exporter/templates/xls_exporter.rb +48 -0
  30. data/lib/generators/para/orderable/orderable_generator.rb +1 -1
  31. data/lib/generators/para/page/section/section_generator.rb +1 -1
  32. data/lib/para/attribute_field/base.rb +4 -0
  33. data/lib/para/attribute_field/datetime.rb +1 -1
  34. data/lib/para/attribute_field/file.rb +4 -0
  35. data/lib/para/attribute_field/image.rb +4 -0
  36. data/lib/para/cache/database_store.rb +71 -0
  37. data/lib/para/cache.rb +11 -0
  38. data/lib/para/config.rb +28 -2
  39. data/lib/para/engine.rb +9 -14
  40. data/lib/para/exporter/base.rb +26 -0
  41. data/lib/para/exporter/csv.rb +6 -26
  42. data/lib/para/exporter/table.rb +23 -0
  43. data/lib/para/exporter/xls.rb +59 -0
  44. data/lib/para/exporter.rb +6 -3
  45. data/lib/para/ext.rb +0 -1
  46. data/lib/para/form_builder/containers.rb +14 -1
  47. data/lib/para/form_builder.rb +1 -0
  48. data/lib/para/importer/base.rb +2 -28
  49. data/lib/para/inputs/nested_many_input.rb +9 -4
  50. data/lib/para/job/base.rb +23 -4
  51. data/lib/para/logging/active_job_log_subscriber.rb +2 -0
  52. data/lib/para/orderable.rb +2 -2
  53. data/lib/para/plugins/set.rb +34 -0
  54. data/lib/para/plugins.rb +3 -2
  55. data/lib/para/routing/component_controller_constraint.rb +1 -1
  56. data/lib/para/routing/component_name_constraint.rb +22 -0
  57. data/lib/para/routing.rb +1 -0
  58. data/lib/para/sti/root_model.rb +4 -54
  59. data/lib/para/version.rb +1 -1
  60. data/lib/para.rb +4 -1
  61. data/lib/rails/routing_mapper.rb +67 -34
  62. data/vendor/assets/javascripts/html5-sortable.js +142 -0
  63. data/vendor/assets/javascripts/jasny.bootstrap.min.js +7 -0
  64. data/vendor/assets/javascripts/jquery.remote-modal-form.coffee +67 -0
  65. data/vendor/assets/javascripts/jquery.scrollto.js +187 -0
  66. data/vendor/assets/stylesheets/animate.min.css +6 -0
  67. data/vendor/assets/stylesheets/jasny.bootstrap.min.css +7 -0
  68. metadata +56 -9
  69. data/app/assets/javascripts/para/admin.coffee +0 -19
  70. data/lib/para/ext/simple_form_extension.rb +0 -27
@@ -4,60 +4,10 @@ module Para
4
4
  extend ActiveSupport::Concern
5
5
 
6
6
  included do
7
- class << self
8
- def descendants_with_eager_loaded_subclasses
9
- eager_load!
10
- descendants_without_eager_loaded_subclasses
11
- end
12
-
13
- alias_method_chain :descendants, :eager_loaded_subclasses
14
- end
15
- end
16
-
17
- module ClassMethods
18
- def eager_load!
19
- return if RequestStore.store[descendants_eager_load_key]
20
- RequestStore.store[descendants_eager_load_key] = true
21
-
22
- models_dir = Rails.root.join('app', 'models')
23
-
24
- Dir[models_dir.join(subclasses_dir, '*.rb')].each do |file_path|
25
- file_name = File.basename(file_path, '.rb')
26
-
27
- # Avoid Circular dependecy errors in development, when the first
28
- # loaded class is not the base class. In this case, the base class
29
- # loading is triggered by the child, so if we try to load that child
30
- # again, Rails issues a CircularDependency error
31
- file_load_path = File.join(File.dirname(file_path), file_name)
32
- next if ActiveSupport::Dependencies.loading.include?(file_load_path)
33
-
34
- # Autoload the subclass
35
- require file_load_path
36
- end
37
- end
38
-
39
- private
40
-
41
- def descendants_eager_load_key
42
- @descendants_eager_load_key ||= [
43
- 'para', 'root_model', 'descendants_eager_loaded', name
44
- ].join(':')
45
- end
46
-
47
- # Allows the including class to define `.subclasses_namespace` class
48
- # method to override the namespace and directory used to eager load the
49
- # subclasses.
50
- #
51
- # Note : No error is raised if target subclasses directory does not
52
- # exist.
53
- #
54
- def subclasses_namespace
55
- @subclasses_namespace ||= name.deconstantize
56
- end
57
-
58
- def subclasses_dir
59
- @subclasses_dir ||= subclasses_namespace.underscore
60
- end
7
+ Rails.logger.warn '[Warning] Para::Sti::RootModel is deprecated and ' \
8
+ 'has no effect at all on your model. Please use the :nested_many ' \
9
+ 'input :subclasses option to provided a list of available ' \
10
+ 'subclasses to create.'
61
11
  end
62
12
  end
63
13
  end
data/lib/para/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Para
2
- VERSION = '0.6.3'
2
+ VERSION = '0.6.7'
3
3
  end
data/lib/para.rb CHANGED
@@ -9,7 +9,9 @@ require 'truncate_html'
9
9
  require 'cocoon'
10
10
  require 'cancan'
11
11
  require 'request_store'
12
+ require 'spreadsheet'
12
13
  require 'roo'
14
+ require 'roo-xls'
13
15
  require 'activejob-status'
14
16
  require 'deep_cloneable'
15
17
 
@@ -28,6 +30,7 @@ require 'para/postgres_extensions_checker'
28
30
 
29
31
  require 'para/ext'
30
32
  require 'para/errors'
33
+ require 'para/plugins'
31
34
  require 'para/config'
32
35
  require 'para/model_field_parsers'
33
36
  require 'para/attribute_field'
@@ -55,8 +58,8 @@ module Para
55
58
  autoload :Generators
56
59
  autoload :Routing
57
60
  autoload :Routes
58
- autoload :Plugins
59
61
  autoload :Breadcrumbs
62
+ autoload :Cache
60
63
  autoload :Logging
61
64
  end
62
65
 
@@ -15,18 +15,34 @@ module ActionDispatch
15
15
  end
16
16
 
17
17
  def component(component_name, options = {}, &block)
18
- as, controller, endpoint = component_informations_from(
18
+ as, controller, component, endpoint = component_informations_from(
19
19
  component_name, options
20
20
  )
21
21
 
22
22
  actions = options.fetch(:actions, [:show])
23
23
 
24
- namespace :admin do
25
- resource endpoint, controller: controller, as: as
24
+ imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
25
+ exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
26
+
27
+
28
+ constraints Para::Routing::ComponentNameConstraint.new(component) do
29
+ namespace :admin do
30
+ defaults(component: component) do
31
+ resource endpoint, controller: controller, as: as
32
+ end
33
+
34
+ scope(endpoint, as: component_name, defaults: { component: component }) do
35
+ instance_eval(&block) if block
36
+ add_extensions_for(:component)
37
+
38
+ scope ':importer' do
39
+ resources :imports, controller: imports_controller
40
+ end
26
41
 
27
- scope(endpoint, as: component_name) do
28
- instance_eval(&block) if block
29
- add_extensions_for(:component)
42
+ scope ':exporter' do
43
+ resources :exports, controller: exports_controller
44
+ end
45
+ end
30
46
  end
31
47
  end
32
48
  end
@@ -38,7 +54,7 @@ module ActionDispatch
38
54
  options[:scope] ||= ':model'
39
55
  end
40
56
 
41
- as, controller, endpoint = component_informations_from(
57
+ as, controller, component, endpoint = component_informations_from(
42
58
  component_name, options
43
59
  )
44
60
 
@@ -49,30 +65,32 @@ module ActionDispatch
49
65
  imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
50
66
  exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
51
67
 
52
- namespace :admin do
68
+ constraints Para::Routing::ComponentNameConstraint.new(component) do
53
69
  constraints Para::Routing::ComponentControllerConstraint.new(controller) do
54
- scope endpoint, as: as do
55
- scope options[:scope] do
56
- resources :resources, controller: controller do
57
- collection do
58
- patch :order
59
- patch :tree
70
+ namespace :admin do
71
+ scope(endpoint, as: as, defaults: { component: component }) do
72
+ scope options[:scope] do
73
+ resources :resources, controller: controller do
74
+ collection do
75
+ patch :order
76
+ patch :tree
77
+ end
78
+
79
+ member do
80
+ post :clone
81
+ end
82
+
83
+ instance_eval(&block) if block
84
+ add_extensions_for(:crud_component)
60
85
  end
61
86
 
62
- member do
63
- post :clone
87
+ scope ':importer' do
88
+ resources :imports, controller: imports_controller
64
89
  end
65
90
 
66
- instance_eval(&block) if block
67
- add_extensions_for(:crud_component)
68
- end
69
-
70
- scope ':importer' do
71
- resources :imports, controller: imports_controller
72
- end
73
-
74
- scope ':exporter' do
75
- resources :exports, controller: exports_controller
91
+ scope ':exporter' do
92
+ resources :exports, controller: exports_controller
93
+ end
76
94
  end
77
95
  end
78
96
  end
@@ -87,17 +105,29 @@ module ActionDispatch
87
105
  options[:scope] ||= ':model'
88
106
  end
89
107
 
90
- as, _, endpoint = component_informations_from(
108
+ as, _, component, endpoint = component_informations_from(
91
109
  component_name, options
92
110
  )
93
111
 
94
112
  controller = options.fetch(:controller, '/para/admin/form_resources')
113
+ imports_controller = options.fetch(:imports_controller, '/para/admin/imports')
114
+ exports_controller = options.fetch(:exports_controller, '/para/admin/exports')
95
115
 
96
- namespace :admin do
116
+ constraints Para::Routing::ComponentNameConstraint.new(component) do
97
117
  constraints Para::Routing::ComponentControllerConstraint.new(controller) do
98
- scope endpoint, as: as do
99
- resource :resource, controller: controller, only: [:show, :create, :update] do
100
- add_extensions_for(:form_component)
118
+ namespace :admin do
119
+ scope(endpoint, as: as, defaults: { component: component }) do
120
+ resource :resource, controller: controller, only: [:show, :create, :update] do
121
+ add_extensions_for(:form_component)
122
+ end
123
+
124
+ scope ':importer' do
125
+ resources :imports, controller: imports_controller
126
+ end
127
+
128
+ scope ':exporter' do
129
+ resources :exports, controller: exports_controller
130
+ end
101
131
  end
102
132
  end
103
133
  end
@@ -108,20 +138,23 @@ module ActionDispatch
108
138
 
109
139
  def draw_plugin_routes(identifier)
110
140
  routes = [
141
+ '',
111
142
  Para::Plugins.module_name_for(identifier),
112
143
  'Routes'
113
144
  ].join('::').constantize
114
145
 
115
146
  routes.new(self).draw
147
+ # If the plugin does not define a Routes module, continue
148
+ rescue NameError
116
149
  end
117
150
 
118
151
  def component_informations_from(component_name, options)
119
- path = options.fetch(:path, component_name.to_s)
152
+ component = options.fetch(:component, component_name.to_s)
120
153
  as = options.fetch(:as, component_name)
121
154
  controller = options.fetch(:controller, "#{ component_name }_component")
122
- endpoint = "#{ path }/:component_id"
155
+ endpoint = ":component/:component_id"
123
156
 
124
- [as, controller, endpoint]
157
+ [as, controller, component, endpoint]
125
158
  end
126
159
 
127
160
  def add_extensions_for(type)
@@ -0,0 +1,142 @@
1
+ /*
2
+ * HTML5 Sortable jQuery Plugin
3
+ * https://github.com/voidberg/html5sortable
4
+ *
5
+ * Original code copyright 2012 Ali Farhadi.
6
+ * This version is mantained by Alexandru Badiu <andu@ctrlz.ro>
7
+ *
8
+ * Thanks to the following contributors: andyburke, bistoco, daemianmack, drskullster, flying-sheep, OscarGodson, Parikshit N. Samant, rodolfospalenza, ssafejava
9
+ *
10
+ * Released under the MIT license.
11
+ */
12
+ 'use strict';
13
+
14
+ (function ($) {
15
+ var dragging, draggingHeight, placeholders = $();
16
+ $.fn.sortable = function (options) {
17
+ var method = String(options);
18
+
19
+ options = $.extend({
20
+ connectWith: false,
21
+ placeholder: null,
22
+ dragImage: null
23
+ }, options);
24
+
25
+ return this.each(function () {
26
+
27
+ var index, items = $(this).children(options.items), handles = options.handle ? items.find(options.handle) : items;
28
+
29
+ if (method === 'reload') {
30
+ $(this).children(options.items).off('dragstart.h5s dragend.h5s selectstart.h5s dragover.h5s dragenter.h5s drop.h5s');
31
+ }
32
+ if (/^enable|disable|destroy$/.test(method)) {
33
+ var citems = $(this).children($(this).data('items')).attr('draggable', method === 'enable');
34
+ if (method === 'destroy') {
35
+ $(this).off('sortupdate');
36
+ $(this).removeData('opts');
37
+ citems.add(this).removeData('connectWith items')
38
+ .off('dragstart.h5s dragend.h5s dragover.h5s dragenter.h5s drop.h5s').off('sortupdate');
39
+ handles.off('selectstart.h5s');
40
+ }
41
+ return;
42
+ }
43
+
44
+ var soptions = $(this).data('opts');
45
+
46
+ if (typeof soptions === 'undefined') {
47
+ $(this).data('opts', options);
48
+ }
49
+ else {
50
+ options = soptions;
51
+ }
52
+
53
+ var startParent, newParent;
54
+ var placeholder = ( options.placeholder === null ) ? $('<' + (/^ul|ol$/i.test(this.tagName) ? 'li' : 'div') + ' class="sortable-placeholder"/>') : $(options.placeholder).addClass('sortable-placeholder');
55
+
56
+ $(this).data('items', options.items);
57
+ placeholders = placeholders.add(placeholder);
58
+ if (options.connectWith) {
59
+ $(options.connectWith).add(this).data('connectWith', options.connectWith);
60
+ }
61
+
62
+ items.attr('role', 'option');
63
+ items.attr('aria-grabbed', 'false');
64
+
65
+ // Setup drag handles
66
+ handles.attr('draggable', 'true').not('a[href], img').on('selectstart.h5s', function() {
67
+ if (this.dragDrop) {
68
+ this.dragDrop();
69
+ }
70
+ return false;
71
+ }).end();
72
+
73
+ // Handle drag events on draggable items
74
+ items.on('dragstart.h5s', function(e) {
75
+ e.stopPropagation();
76
+
77
+ var dt = e.originalEvent.dataTransfer;
78
+ dt.effectAllowed = 'move';
79
+ dt.setData('text', '');
80
+
81
+ if (options.dragImage && dt.setDragImage) {
82
+ dt.setDragImage(options.dragImage, 0, 0);
83
+ }
84
+
85
+ index = (dragging = $(this)).addClass('sortable-dragging').attr('aria-grabbed', 'true').index();
86
+ draggingHeight = dragging.outerHeight();
87
+ startParent = $(this).parent();
88
+ dragging.parent().triggerHandler('sortstart', {item: dragging, startparent: startParent});
89
+ }).on('dragend.h5s',function () {
90
+ if (!dragging) {
91
+ return;
92
+ }
93
+ dragging.removeClass('sortable-dragging').attr('aria-grabbed', 'false').show();
94
+ placeholders.detach();
95
+ newParent = $(this).parent();
96
+ if (index !== dragging.index() || startParent.get(0) !== newParent.get(0)) {
97
+ dragging.parent().triggerHandler('sortupdate', {item: dragging, oldindex: index, startparent: startParent, endparent: newParent});
98
+ }
99
+ dragging = null;
100
+ draggingHeight = null;
101
+ }).add([this, placeholder]).on('dragover.h5s dragenter.h5s drop.h5s', function(e) {
102
+ if (!items.is(dragging) && options.connectWith !== $(dragging).parent().data('connectWith')) {
103
+ return true;
104
+ }
105
+ if (e.type === 'drop') {
106
+ e.stopPropagation();
107
+ placeholders.filter(':visible').after(dragging);
108
+ dragging.trigger('dragend.h5s');
109
+ return false;
110
+ }
111
+ e.preventDefault();
112
+ e.originalEvent.dataTransfer.dropEffect = 'move';
113
+ if (items.is(this)) {
114
+ var thisHeight = $(this).outerHeight();
115
+ if (options.forcePlaceholderSize) {
116
+ placeholder.height(draggingHeight);
117
+ }
118
+
119
+ // Check if $(this) is bigger than the draggable. If it is, we have to define a dead zone to prevent flickering
120
+ if (thisHeight > draggingHeight) {
121
+ // Dead zone?
122
+ var deadZone = thisHeight - draggingHeight, offsetTop = $(this).offset().top;
123
+ if (placeholder.index() < $(this).index() && e.originalEvent.pageY < offsetTop + deadZone) {
124
+ return false;
125
+ }
126
+ else if (placeholder.index() > $(this).index() && e.originalEvent.pageY > offsetTop + thisHeight - deadZone) {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ dragging.hide();
132
+ $(this)[placeholder.index() < $(this).index() ? 'after' : 'before'](placeholder);
133
+ placeholders.not(placeholder).detach();
134
+ } else if (!placeholders.is(this) && !$(this).children(options.items).length) {
135
+ placeholders.detach();
136
+ $(this).append(placeholder);
137
+ }
138
+ return false;
139
+ });
140
+ });
141
+ };
142
+ })(jQuery);
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * Jasny Bootstrap v3.1.0 (http://jasny.github.com/bootstrap)
3
+ * Copyright 2011-2014 Arnold Daniels.
4
+ * Licensed under Apache-2.0 (https://github.com/jasny/bootstrap/blob/master/LICENSE)
5
+ */
6
+
7
+ +function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.$element.on("click.bs.rowlink","td:not(.rowlink-skip)",a.proxy(this.click,this))};b.DEFAULTS={target:"a"},b.prototype.click=function(b){var c=a(b.currentTarget).closest("tr").find(this.options.target)[0];if(a(b.target)[0]===c)return;b.preventDefault();if(c.click)c.click();else if(document.createEvent){var d=document.createEvent("MouseEvents");d.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),c.dispatchEvent(d)}};var c=a.fn.rowlink;a.fn.rowlink=function(c){return this.each(function(){var d=a(this),e=d.data("bs.rowlink");e||d.data("bs.rowlink",e=new b(this,c))})},a.fn.rowlink.Constructor=b,a.fn.rowlink.noConflict=function(){return a.fn.rowlink=c,this},a(document).on("click.bs.rowlink.data-api",'[data-link="row"]',function(b){if(a(b.target).closest(".rowlink-skip").length!==0)return;var c=a(this);if(c.data("bs.rowlink"))return;c.rowlink(c.data()),a(b.target).trigger("click.bs.rowlink")})}(window.jQuery),+function(a){"use strict";var b=window.orientation!==undefined,c=navigator.userAgent.toLowerCase().indexOf("android")>-1,d=window.navigator.appName=="Microsoft Internet Explorer",e=function(b,d){if(c)return;this.$element=a(b),this.options=a.extend({},e.DEFAULTS,d),this.mask=String(this.options.mask),this.init(),this.listen(),this.checkVal()};e.DEFAULTS={mask:"",placeholder:"_",definitions:{9:"[0-9]",a:"[A-Za-z]",w:"[A-Za-z0-9]","*":"."}},e.prototype.init=function(){var b=this.options.definitions,c=this.mask.length;this.tests=[],this.partialPosition=this.mask.length,this.firstNonMaskPos=null,a.each(this.mask.split(""),a.proxy(function(a,d){d=="?"?(c--,this.partialPosition=a):b[d]?(this.tests.push(new RegExp(b[d])),this.firstNonMaskPos===null&&(this.firstNonMaskPos=this.tests.length-1)):this.tests.push(null)},this)),this.buffer=a.map(this.mask.split(""),a.proxy(function(a,c){if(a!="?")return b[a]?this.options.placeholder:a},this)),this.focusText=this.$element.val(),this.$element.data("rawMaskFn",a.proxy(function(){return a.map(this.buffer,function(a,b){return this.tests[b]&&a!=this.options.placeholder?a:null}).join("")},this))},e.prototype.listen=function(){if(this.$element.attr("readonly"))return;var b=(d?"paste":"input")+".mask";this.$element.on("unmask.bs.inputmask",a.proxy(this.unmask,this)).on("focus.bs.inputmask",a.proxy(this.focusEvent,this)).on("blur.bs.inputmask",a.proxy(this.blurEvent,this)).on("keydown.bs.inputmask",a.proxy(this.keydownEvent,this)).on("keypress.bs.inputmask",a.proxy(this.keypressEvent,this)).on(b,a.proxy(this.pasteEvent,this))},e.prototype.caret=function(a,b){if(this.$element.length===0)return;if(typeof a=="number")return b=typeof b=="number"?b:a,this.$element.each(function(){if(this.setSelectionRange)this.setSelectionRange(a,b);else if(this.createTextRange){var c=this.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",a),c.select()}});if(this.$element[0].setSelectionRange)a=this.$element[0].selectionStart,b=this.$element[0].selectionEnd;else if(document.selection&&document.selection.createRange){var c=document.selection.createRange();a=0-c.duplicate().moveStart("character",-1e5),b=a+c.text.length}return{begin:a,end:b}},e.prototype.seekNext=function(a){var b=this.mask.length;while(++a<=b&&!this.tests[a]);return a},e.prototype.seekPrev=function(a){while(--a>=0&&!this.tests[a]);return a},e.prototype.shiftL=function(a,b){var c=this.mask.length;if(a<0)return;for(var d=a,e=this.seekNext(b);d<c;d++)if(this.tests[d]){if(e<c&&this.tests[d].test(this.buffer[e]))this.buffer[d]=this.buffer[e],this.buffer[e]=this.options.placeholder;else break;e=this.seekNext(e)}this.writeBuffer(),this.caret(Math.max(this.firstNonMaskPos,a))},e.prototype.shiftR=function(a){var b=this.mask.length;for(var c=a,d=this.options.placeholder;c<b;c++)if(this.tests[c]){var e=this.seekNext(c),f=this.buffer[c];this.buffer[c]=d;if(e<b&&this.tests[e].test(f))d=f;else break}},e.prototype.unmask=function(){this.$element.unbind(".mask").removeData("inputmask")},e.prototype.focusEvent=function(){this.focusText=this.$element.val();var a=this.mask.length,b=this.checkVal();this.writeBuffer();var c=this,d=function(){b==a?c.caret(0,b):c.caret(b)};d(),setTimeout(d,50)},e.prototype.blurEvent=function(){this.checkVal(),this.$element.val()!==this.focusText&&this.$element.trigger("change")},e.prototype.keydownEvent=function(a){var c=a.which;if(c==8||c==46||b&&c==127){var d=this.caret(),e=d.begin,f=d.end;return f-e===0&&(e=c!=46?this.seekPrev(e):f=this.seekNext(e-1),f=c==46?this.seekNext(f):f),this.clearBuffer(e,f),this.shiftL(e,f-1),!1}if(c==27)return this.$element.val(this.focusText),this.caret(0,this.checkVal()),!1},e.prototype.keypressEvent=function(a){var b=this.mask.length,c=a.which,d=this.caret();if(a.ctrlKey||a.altKey||a.metaKey||c<32)return!0;if(c){d.end-d.begin!==0&&(this.clearBuffer(d.begin,d.end),this.shiftL(d.begin,d.end-1));var e=this.seekNext(d.begin-1);if(e<b){var f=String.fromCharCode(c);if(this.tests[e].test(f)){this.shiftR(e),this.buffer[e]=f,this.writeBuffer();var g=this.seekNext(e);this.caret(g)}}return!1}},e.prototype.pasteEvent=function(){var a=this;setTimeout(function(){a.caret(a.checkVal(!0))},0)},e.prototype.clearBuffer=function(a,b){var c=this.mask.length;for(var d=a;d<b&&d<c;d++)this.tests[d]&&(this.buffer[d]=this.options.placeholder)},e.prototype.writeBuffer=function(){return this.$element.val(this.buffer.join("")).val()},e.prototype.checkVal=function(a){var b=this.mask.length,c=this.$element.val(),d=-1;for(var e=0,f=0;e<b;e++)if(this.tests[e]){this.buffer[e]=this.options.placeholder;while(f++<c.length){var g=c.charAt(f-1);if(this.tests[e].test(g)){this.buffer[e]=g,d=e;break}}if(f>c.length)break}else this.buffer[e]==c.charAt(f)&&e!=this.partialPosition&&(f++,d=e);if(!a&&d+1<this.partialPosition)this.$element.val(""),this.clearBuffer(0,b);else if(a||d+1>=this.partialPosition)this.writeBuffer(),a||this.$element.val(this.$element.val().substring(0,d+1));return this.partialPosition?e:this.firstNonMaskPos};var f=a.fn.inputmask;a.fn.inputmask=function(b){return this.each(function(){var c=a(this),d=c.data("bs.inputmask");d||c.data("bs.inputmask",d=new e(this,b))})},a.fn.inputmask.Constructor=e,a.fn.inputmask.noConflict=function(){return a.fn.inputmask=f,this},a(document).on("focus.bs.inputmask.data-api","[data-mask]",function(b){var c=a(this);if(c.data("bs.inputmask"))return;c.inputmask(c.data())})}(window.jQuery),+function(a){"use strict";var b=window.navigator.appName=="Microsoft Internet Explorer",c=function(b,c){this.$element=a(b),this.$input=this.$element.find(":file");if(this.$input.length===0)return;this.name=this.$input.attr("name")||c.name,this.$hidden=this.$element.find('input[type=hidden][name="'+this.name+'"]'),this.$hidden.length===0&&(this.$hidden=a('<input type="hidden">').insertBefore(this.$input)),this.$preview=this.$element.find(".fileinput-preview");var d=this.$preview.css("height");this.$preview.css("display")!=="inline"&&d!=="0px"&&d!=="none"&&this.$preview.css("line-height",d),this.original={exists:this.$element.hasClass("fileinput-exists"),preview:this.$preview.html(),hiddenVal:this.$hidden.val()},this.listen()};c.prototype.listen=function(){this.$input.on("change.bs.fileinput",a.proxy(this.change,this)),a(this.$input[0].form).on("reset.bs.fileinput",a.proxy(this.reset,this)),this.$element.find('[data-trigger="fileinput"]').on("click.bs.fileinput",a.proxy(this.trigger,this)),this.$element.find('[data-dismiss="fileinput"]').on("click.bs.fileinput",a.proxy(this.clear,this))},c.prototype.change=function(b){var c=b.target.files===undefined?b.target&&b.target.value?[{name:b.target.value.replace(/^.+\\/,"")}]:[]:b.target.files;b.stopPropagation();if(c.length===0){this.clear();return}this.$hidden.val(""),this.$hidden.attr("name",""),this.$input.attr("name",this.name);var d=c[0];if(this.$preview.length>0&&(typeof d.type!="undefined"?d.type.match(/^image\/(gif|png|jpeg)$/):d.name.match(/\.(gif|png|jpe?g)$/i))&&typeof FileReader!="undefined"){var e=new FileReader,f=this.$preview,g=this.$element;e.onload=function(b){var e=a("<img>");e[0].src=b.target.result,c[0].result=b.target.result,g.find(".fileinput-filename").text(d.name),f.css("max-height")!="none"&&e.css("max-height",parseInt(f.css("max-height"),10)-parseInt(f.css("padding-top"),10)-parseInt(f.css("padding-bottom"),10)-parseInt(f.css("border-top"),10)-parseInt(f.css("border-bottom"),10)),f.html(e),g.addClass("fileinput-exists").removeClass("fileinput-new"),g.trigger("change.bs.fileinput",c)},e.readAsDataURL(d)}else this.$element.find(".fileinput-filename").text(d.name),this.$preview.text(d.name),this.$element.addClass("fileinput-exists").removeClass("fileinput-new"),this.$element.trigger("change.bs.fileinput")},c.prototype.clear=function(a){a&&a.preventDefault(),this.$hidden.val(""),this.$hidden.attr("name",this.name),this.$input.attr("name","");if(b){var c=this.$input.clone(!0);this.$input.after(c),this.$input.remove(),this.$input=c}else this.$input.val("");this.$preview.html(""),this.$element.find(".fileinput-filename").text(""),this.$element.addClass("fileinput-new").removeClass("fileinput-exists"),a!==undefined&&(this.$input.trigger("change"),this.$element.trigger("clear.bs.fileinput"))},c.prototype.reset=function(){this.clear(),this.$hidden.val(this.original.hiddenVal),this.$preview.html(this.original.preview),this.$element.find(".fileinput-filename").text(""),this.original.exists?this.$element.addClass("fileinput-exists").removeClass("fileinput-new"):this.$element.addClass("fileinput-new").removeClass("fileinput-exists"),this.$element.trigger("reset.bs.fileinput")},c.prototype.trigger=function(a){this.$input.trigger("click"),a.preventDefault()};var d=a.fn.fileinput;a.fn.fileinput=function(b){return this.each(function(){var d=a(this),e=d.data("bs.fileinput");e||d.data("bs.fileinput",e=new c(this,b)),typeof b=="string"&&e[b]()})},a.fn.fileinput.Constructor=c,a.fn.fileinput.noConflict=function(){return a.fn.fileinput=d,this},a(document).on("click.fileinput.data-api",'[data-provides="fileinput"]',function(b){var c=a(this);if(c.data("bs.fileinput"))return;c.fileinput(c.data());var d=a(b.target).closest('[data-dismiss="fileinput"],[data-trigger="fileinput"]');d.length>0&&(b.preventDefault(),d.trigger("click.bs.fileinput"))})}(window.jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.state=null,this.placement=null,this.options.recalc&&(this.calcClone(),a(window).on("resize",a.proxy(this.recalc,this))),this.options.autohide&&a(document).on("click",a.proxy(this.autohide,this)),this.options.toggle&&this.toggle(),this.options.disablescrolling&&(this.options.disableScrolling=this.options.disablescrolling,delete this.options.disablescrolling)};b.DEFAULTS={toggle:!0,placement:"auto",autohide:!0,recalc:!0,disableScrolling:!0},b.prototype.offset=function(){switch(this.placement){case"left":case"right":return this.$element.outerWidth();case"top":case"bottom":return this.$element.outerHeight()}},b.prototype.calcPlacement=function(){function e(a,b){if(d.css(b)==="auto")return a;if(d.css(a)==="auto")return b;var c=parseInt(d.css(a),10),e=parseInt(d.css(b),10);return c>e?b:a}if(this.options.placement!=="auto"){this.placement=this.options.placement;return}this.$element.hasClass("in")||this.$element.css("visiblity","hidden !important").addClass("in");var b=a(window).width()/this.$element.width(),c=a(window).height()/this.$element.height(),d=this.$element;this.placement=b>=c?e("left","right"):e("top","bottom"),this.$element.css("visibility")==="hidden !important"&&this.$element.removeClass("in").css("visiblity","")},b.prototype.opposite=function(a){switch(a){case"top":return"bottom";case"left":return"right";case"bottom":return"top";case"right":return"left"}},b.prototype.getCanvasElements=function(){var b=this.options.canvas?a(this.options.canvas):this.$element,c=b.find("*").filter(function(){return a(this).css("position")==="fixed"}).not(this.options.exclude);return b.add(c)},b.prototype.slide=function(b,c,d){if(!a.support.transition){var e={};return e[this.placement]="+="+c,b.animate(e,350,d)}var f=this.placement,g=this.opposite(f);b.each(function(){a(this).css(f)!=="auto"&&a(this).css(f,(parseInt(a(this).css(f),10)||0)+c),a(this).css(g)!=="auto"&&a(this).css(g,(parseInt(a(this).css(g),10)||0)-c)}),this.$element.one(a.support.transition.end,d).emulateTransitionEnd(350)},b.prototype.disableScrolling=function(){var b=a("body").width(),c="padding-"+this.opposite(this.placement);a("body").data("offcanvas-style")===undefined&&a("body").data("offcanvas-style",a("body").attr("style")||""),a("body").css("overflow","hidden");if(a("body").width()>b){var d=parseInt(a("body").css(c),10)+a("body").width()-b;setTimeout(function(){a("body").css(c,d)},1)}},b.prototype.show=function(){if(this.state)return;var b=a.Event("show.bs.offcanvas");this.$element.trigger(b);if(b.isDefaultPrevented())return;this.state="slide-in",this.calcPlacement();var c=this.getCanvasElements(),d=this.placement,e=this.opposite(d),f=this.offset();c.index(this.$element)!==-1&&(a(this.$element).data("offcanvas-style",a(this.$element).attr("style")||""),this.$element.css(d,-1*f),this.$element.css(d)),c.addClass("canvas-sliding").each(function(){a(this).data("offcanvas-style")===undefined&&a(this).data("offcanvas-style",a(this).attr("style")||""),a(this).css("position")==="static"&&a(this).css("position","relative"),(a(this).css(d)==="auto"||a(this).css(d)==="0px")&&(a(this).css(e)==="auto"||a(this).css(e)==="0px")&&a(this).css(d,0)}),this.options.disableScrolling&&this.disableScrolling();var g=function(){if(this.state!="slide-in")return;this.state="slid",c.removeClass("canvas-sliding").addClass("canvas-slid"),this.$element.trigger("shown.bs.offcanvas")};setTimeout(a.proxy(function(){this.$element.addClass("in"),this.slide(c,f,a.proxy(g,this))},this),1)},b.prototype.hide=function(b){if(this.state!=="slid")return;var c=a.Event("hide.bs.offcanvas");this.$element.trigger(c);if(c.isDefaultPrevented())return;this.state="slide-out";var d=a(".canvas-slid"),e=this.placement,f=-1*this.offset(),g=function(){if(this.state!="slide-out")return;this.state=null,this.placement=null,this.$element.removeClass("in"),d.removeClass("canvas-sliding"),d.add(this.$element).add("body").each(function(){a(this).attr("style",a(this).data("offcanvas-style")).removeData("offcanvas-style")}),this.$element.trigger("hidden.bs.offcanvas")};d.removeClass("canvas-slid").addClass("canvas-sliding"),setTimeout(a.proxy(function(){this.slide(d,f,a.proxy(g,this))},this),1)},b.prototype.toggle=function(){if(this.state==="slide-in"||this.state==="slide-out")return;this[this.state==="slid"?"hide":"show"]()},b.prototype.calcClone=function(){this.$calcClone=this.$element.clone().html("").addClass("offcanvas-clone").removeClass("in").appendTo(a("body"))},b.prototype.recalc=function(){if(this.$calcClone.css("display")==="none"||this.state!=="slid"&&this.state!=="slide-in")return;this.state=null,this.placement=null;var b=this.getCanvasElements();this.$element.removeClass("in"),b.removeClass("canvas-slid"),b.add(this.$element).add("body").each(function(){a(this).attr("style",a(this).data("offcanvas-style")).removeData("offcanvas-style")})},b.prototype.autohide=function(b){a(b.target).closest(this.$element).length===0&&this.hide()};var c=a.fn.offcanvas;a.fn.offcanvas=function(c){return this.each(function(){var d=a(this),e=d.data("bs.offcanvas"),f=a.extend({},b.DEFAULTS,d.data(),typeof c=="object"&&c);e||d.data("bs.offcanvas",e=new b(this,f)),typeof c=="string"&&e[c]()})},a.fn.offcanvas.Constructor=b,a.fn.offcanvas.noConflict=function(){return a.fn.offcanvas=c,this},a(document).on("click.bs.offcanvas.data-api","[data-toggle=offcanvas]",function(b){var c=a(this),d,e=c.attr("data-target")||b.preventDefault()||(d=c.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.offcanvas"),h=g?"toggle":c.data();b.stopPropagation(),g?g.toggle():f.offcanvas(h)})}(window.jQuery),+function(a){function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(a.style[c]!==undefined)return{end:b[c]};return!1}"use strict";if(a.support.transition!==undefined)return;a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(window.jQuery)
@@ -0,0 +1,67 @@
1
+ # Allows to handle a form in a modal with rails' jquery-ujs remote links and
2
+ # forms feature, without having to write any extra javascript
3
+ #
4
+ class @RemoteModalForm extends Vertebra.View
5
+ events:
6
+ 'ajax:success form[data-remote]': 'formSuccess'
7
+ 'ajax:success a[data-remote]': 'pageLoaded'
8
+ 'ajax:error [data-remote]': 'formError'
9
+ 'hidden.bs.modal': 'modalHidden'
10
+ 'hide.bs.modal': 'modalHide'
11
+
12
+ initialize: (options = {}) ->
13
+ { modalMarkup, @$link, refreshOnClose } = options
14
+ @replaceModalWith(modalMarkup)
15
+ @refreshOnClose = if refreshOnClose? then refreshOnClose else @$link?.data('refresh-on-close')
16
+ @trigger('shown')
17
+
18
+ formSuccess: (e, response) ->
19
+ @formDidSuccess = true
20
+ @handleModalResponse(response)
21
+ @trigger('success')
22
+
23
+ formError: (e, jqXHR) ->
24
+ @replaceModalWith(jqXHR.responseText)
25
+ @trigger('error')
26
+
27
+ pageLoaded: (e, response) ->
28
+ @handleModalResponse(response)
29
+
30
+ handleModalResponse: (response) ->
31
+ if response and $.trim(response).length
32
+ @replaceModalWith(response)
33
+ else
34
+ @hideModal()
35
+
36
+ replaceModalWith: (modalMarkup) ->
37
+ @hideModal()
38
+ @setElement($(modalMarkup).appendTo('body').modal())
39
+ @$el.data('remote-modal-form', this)
40
+ @showModal()
41
+
42
+ showModal: ->
43
+ @$el.simpleForm?()
44
+
45
+ hideModal: (options = {}) ->
46
+ @$el?.modal('hide')
47
+
48
+ modalHide: (e) ->
49
+ @refreshPage()
50
+
51
+ modalHidden: (e) ->
52
+ $(e.currentTarget).remove()
53
+ @trigger('hide')
54
+ @trigger('close') if @$el[0] is $(e.currentTarget)[0]
55
+
56
+ refreshPage: ->
57
+ Turbolinks.reloadPage() if @refreshOnClose and @formDidSuccess
58
+
59
+ trigger: (event, args...) ->
60
+ super(event, args...)
61
+ @$el.trigger("remote-modal-form:#{ event }", args)
62
+
63
+ # Lazy initialization when the link target is loaded
64
+ #
65
+ $(document).on 'page:change turbolinks:load', ->
66
+ $('body').on 'ajax:success', '[data-remote-modal-form]', (e, response) ->
67
+ new RemoteModalForm(modalMarkup: response, $link: $(e.currentTarget))