para 0.6.3 → 0.6.7

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.
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))