refinerycms-llama_widgets 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/app/assets/images/refinery/icons/lock.png +0 -0
  2. data/app/assets/images/refinery/itoggle.png +0 -0
  3. data/app/assets/javascripts/refinery/engage.itoggle-min.js +17 -0
  4. data/app/assets/javascripts/refinery/widgets.js.coffee.erb +91 -0
  5. data/app/assets/stylesheets/refinery/engage.itoggle.css +94 -0
  6. data/app/assets/stylesheets/refinery/widgets.scss +89 -0
  7. data/app/controllers/refinery/widgets/admin/templates_controller.rb +28 -0
  8. data/app/controllers/refinery/widgets/admin/widget_types_controller.rb +59 -0
  9. data/app/controllers/refinery/widgets/admin/widgets_controller.rb +30 -0
  10. data/app/decorators/controllers/refinery/admin/pages_controller.rb +23 -0
  11. data/app/decorators/controllers/refinery/pages_controller.rb +13 -0
  12. data/app/decorators/models/refinery/page.rb +28 -0
  13. data/app/decorators/models/refinery/page_part.rb +16 -0
  14. data/app/helpers/refinery/widgets/widgets_helper.rb +38 -0
  15. data/app/models/refinery/widgets/content_block_widget.rb +13 -0
  16. data/app/models/refinery/widgets/template.rb +64 -0
  17. data/app/models/refinery/widgets/template_part.rb +37 -0
  18. data/app/models/refinery/widgets/widget.rb +108 -0
  19. data/app/overrides/refinery/admin/pages/_actions.rb +5 -0
  20. data/app/overrides/refinery/admin/pages/_form.rb +4 -0
  21. data/app/overrides/refinery/admin/pages/_form_page_parts.rb +5 -0
  22. data/app/overrides/refinery/admin/pages/_page.rb +5 -0
  23. data/app/views/refinery/admin/_image_picker.html.erb +66 -0
  24. data/app/views/refinery/admin/_nested_image_picker.html.erb +58 -0
  25. data/app/views/refinery/admin/pages/_form_page_parts.html.erb +37 -0
  26. data/app/views/refinery/admin/pages/_page_part.slim +25 -0
  27. data/app/views/refinery/admin/pages/_widget_fields.slim +46 -0
  28. data/app/views/refinery/widgets/_content_block_widget.slim +2 -0
  29. data/app/views/refinery/widgets/_widget.slim +1 -0
  30. data/app/views/refinery/widgets/admin/templates/_actions.html.erb +25 -0
  31. data/app/views/refinery/widgets/admin/templates/_form.html.erb +17 -0
  32. data/app/views/refinery/widgets/admin/templates/_page_part.slim +22 -0
  33. data/app/views/refinery/widgets/admin/templates/_page_parts.html.erb +37 -0
  34. data/app/views/refinery/widgets/admin/templates/_records.html.erb +16 -0
  35. data/app/views/refinery/widgets/admin/templates/_sortable_list.html.erb +5 -0
  36. data/app/views/refinery/widgets/admin/templates/_template.html.erb +18 -0
  37. data/app/views/refinery/widgets/admin/templates/_templates.html.erb +2 -0
  38. data/app/views/refinery/widgets/admin/templates/_widget_fields.slim +26 -0
  39. data/app/views/refinery/widgets/admin/templates/edit.html.erb +1 -0
  40. data/app/views/refinery/widgets/admin/templates/index.html.erb +7 -0
  41. data/app/views/refinery/widgets/admin/templates/list.slim +15 -0
  42. data/app/views/refinery/widgets/admin/templates/new.html.erb +1 -0
  43. data/app/views/refinery/widgets/admin/widget_types/new_for_page.slim +4 -0
  44. data/app/views/refinery/widgets/admin/widget_types/new_for_template.slim +4 -0
  45. data/app/views/refinery/widgets/admin/widget_types/show_for_page.slim +4 -0
  46. data/app/views/refinery/widgets/admin/widget_types/show_for_template.slim +4 -0
  47. data/app/views/refinery/widgets/admin/widgets/_content_block_widget.slim +3 -0
  48. data/app/views/refinery/widgets/admin/widgets/_content_block_widget_preview.slim +3 -0
  49. data/app/views/refinery/widgets/admin/widgets/_widget.slim +2 -0
  50. data/app/views/refinery/widgets/admin/widgets/_widget_preview.slim +2 -0
  51. data/app/views/refinery/widgets/admin/widgets/new.slim +31 -0
  52. data/config/locales/cs.yml +27 -0
  53. data/config/locales/en.yml +34 -0
  54. data/config/locales/es.yml +28 -0
  55. data/config/locales/fr.yml +27 -0
  56. data/config/locales/it.yml +27 -0
  57. data/config/locales/nb.yml +27 -0
  58. data/config/locales/nl.yml +27 -0
  59. data/config/locales/sk.yml +27 -0
  60. data/config/locales/tr.yml +27 -0
  61. data/config/locales/zh-CN.yml +29 -0
  62. data/config/routes.rb +29 -0
  63. data/db/migrate/1_create_widgets_templates.rb +22 -0
  64. data/db/migrate/2_create_widgets_template_parts.rb +20 -0
  65. data/db/migrate/3_create_widgets_widgets.rb +24 -0
  66. data/db/migrate/4_create_widgets_content_block_widgets.rb +16 -0
  67. data/db/migrate/5_add_template_id_to_refinery_pages.rb +13 -0
  68. data/db/migrate/6_add_locked_to_refinery_page_parts.rb +13 -0
  69. data/db/seeds.rb +24 -0
  70. data/lib/generators/refinery/templates/config/initializers/refinery/widgets.rb.erb +7 -0
  71. data/lib/generators/refinery/widgets_generator.rb +27 -0
  72. data/lib/refinery/widgets.rb +24 -0
  73. data/lib/refinery/widgets/acts_as_widget.rb +31 -0
  74. data/lib/refinery/widgets/configuration.rb +15 -0
  75. data/lib/refinery/widgets/engine.rb +39 -0
  76. data/lib/refinery/widgets/widget_type.rb +31 -0
  77. data/lib/refinerycms-widgets.rb +4 -0
  78. data/lib/tasks/refinery/widgets.rake +26 -0
  79. metadata +202 -0
@@ -0,0 +1,13 @@
1
+ Refinery::PagesController.class_eval do
2
+ def find_page(fallback_to_404 = true)
3
+ @page ||= case action_name
4
+ when "home"
5
+ Refinery::Page.where(:link_url => '/').first
6
+ when "show"
7
+ Refinery::Page.find_by_path_or_id(params[:path], params[:id])
8
+ end
9
+ @page || (error_404 if fallback_to_404)
10
+ call_widget_helpers(@page)
11
+ end
12
+ end
13
+
@@ -0,0 +1,28 @@
1
+ Refinery::Page.class_eval do
2
+ attr_accessible :template_id
3
+
4
+ belongs_to :template, class_name: 'Refinery::Widgets::Template'
5
+
6
+ before_validation :validate_widgets
7
+
8
+ def self.find_by_widget_class(widget_class)
9
+ instance = widget_class.first
10
+ return nil if instance.nil?
11
+ container = widget_class.first.widget.container
12
+ return nil unless container.class == Refinery::PagePart
13
+ container.page
14
+ end
15
+
16
+ def widgets
17
+ parts.collect(&:widgets).flatten
18
+ end
19
+
20
+ private
21
+
22
+ def validate_widgets
23
+ widgets.each do |widget|
24
+ widget.valid?
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,16 @@
1
+ Refinery::PagePart.class_eval do
2
+ has_many :widgets, class_name: 'Refinery::Widgets::Widget',
3
+ as: :container, order: 'position'
4
+ accepts_nested_attributes_for :widgets, :allow_destroy => true
5
+
6
+ attr_accessible :widgets_attributes, :locked
7
+
8
+ def add_ref_widget_for!(widget)
9
+ w = widget.dup_by_ref
10
+ w.locked = true
11
+ w.layout = true
12
+ self.widgets << w
13
+ self.save!
14
+ end
15
+
16
+ end
@@ -0,0 +1,38 @@
1
+ module Refinery
2
+ module Widgets
3
+ module WidgetsHelper
4
+
5
+ def widget_type_fields(type, template_part_form_builder)
6
+ widget = Widget.new(sub_type: type.klass)
7
+ widget.send("#{type.association_name}=", type.klass.new)
8
+ fields = template_part_form_builder.fields_for(:widgets, widget) do |builder|
9
+ render("widget_fields", f: builder, type: type)
10
+ end
11
+ content_tag :div, '', data: {id: type.html_id, fields: fields.gsub("\n", "")}
12
+ end
13
+
14
+ def render_page_part(page, part)
15
+ ret = ''
16
+ return ret unless page && page.parts.find_by_title(part)
17
+ page.parts.find_by_title(part).widgets.where(display: true).each do |widget|
18
+ ret += render("refinery/widgets/#{widget.widget_type.front_partial}", :widget => widget)
19
+ end
20
+ raw ret
21
+ end
22
+
23
+ def call_widget_helpers(page)
24
+ if page
25
+ page.widgets.each do |w|
26
+ eval w.ultimate_widget_type.association_name
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+
35
+ ::ApplicationController.send(:helper, Refinery::Widgets::WidgetsHelper)
36
+ ::ApplicationController.send(:include, Refinery::Widgets::WidgetsHelper)
37
+ ::Refinery::AdminController.send(:helper, Refinery::Widgets::WidgetsHelper)
38
+ ::ActionView::Base.send(:include, Refinery::Widgets::WidgetsHelper)
@@ -0,0 +1,13 @@
1
+ module Refinery
2
+ module Widgets
3
+ class ContentBlockWidget < ActiveRecord::Base
4
+
5
+ acts_as_widget
6
+
7
+ attr_accessible :content
8
+
9
+ validates :content, :presence => true
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,64 @@
1
+ module Refinery
2
+ module Widgets
3
+ class Template < Refinery::Core::BaseModel
4
+
5
+ has_many :template_parts, :dependent => :destroy, :class_name => 'Refinery::Widgets::TemplatePart'
6
+ accepts_nested_attributes_for :template_parts, :allow_destroy => true
7
+
8
+ has_many :pages, :class_name => 'Refinery::Page'
9
+
10
+ attr_accessible :name, :position, :template_parts_attributes
11
+
12
+ validates :name, :presence => true, :uniqueness => true
13
+ before_validation :validate_widgets
14
+
15
+ after_save :synchronize_template_parts
16
+
17
+ PartNames = Refinery::Pages.config[:default_parts]
18
+ Parts = PartNames.map(&:underscore)
19
+
20
+ def self.part_names
21
+ PartNames
22
+ end
23
+
24
+ def self.parts
25
+ Parts
26
+ end
27
+
28
+ def synchronize_template_parts
29
+ pages.each do |p|
30
+ template_parts.each do |tp|
31
+ tp.sychronize_widgets!(p)
32
+ end
33
+ end
34
+ end
35
+
36
+ def unlocked_template_parts
37
+ template_parts.reject(&:locked?)
38
+ end
39
+
40
+ def template_part(name)
41
+ if template_parts.find{|tp| tp.name == name }
42
+ template_parts.find{|tp| tp.name == name }
43
+ elsif template_parts.where(name: name).any?
44
+ template_parts.where(name: name).first
45
+ else
46
+ self.template_parts.build(name: name, locked: '0')
47
+ end
48
+ end
49
+
50
+ def widgets
51
+ template_parts.collect(&:widgets).flatten
52
+ end
53
+
54
+ private
55
+
56
+ def validate_widgets
57
+ widgets.each do |widget|
58
+ widget.valid?
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,37 @@
1
+ module Refinery
2
+ module Widgets
3
+ class TemplatePart < Refinery::Core::BaseModel
4
+
5
+ belongs_to :template
6
+ has_many :widgets, class_name: 'Refinery::Widgets::Widget',
7
+ as: :container, order: 'position'
8
+
9
+ accepts_nested_attributes_for :widgets, :allow_destroy => true
10
+
11
+ attr_accessible :name, :display, :locked, :widgets_attributes
12
+
13
+ validates :name, :presence => true, :uniqueness => {:scope => 'template_id'}
14
+
15
+ def sychronize_widgets!(page)
16
+ page_part = page.parts.select{|pp| pp.title == self.name }.first
17
+ if page_part
18
+ page_part_ids = page_part.widgets.select{|w| w.widget_id? && w.layout? }.collect{|x| x.widget_id }
19
+ template_part_ids = self.widgets.collect{|x| x.id }
20
+ missing = template_part_ids - page_part_ids
21
+ missing.each do |w_id|
22
+ page_part.add_ref_widget_for! Widget.find(w_id)
23
+ end
24
+ extras = page_part_ids - template_part_ids
25
+ extras.each do |w_id|
26
+ page_part.widgets.select{|x| x.widget_id == w_id }.first.destroy
27
+ end
28
+ if page_part.locked != self.locked
29
+ page_part.locked = self.locked
30
+ page_part.save!
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,108 @@
1
+ module Refinery
2
+ module Widgets
3
+ class Widget < Refinery::Core::BaseModel
4
+
5
+ belongs_to :container, polymorphic: true
6
+ belongs_to :widget, class_name: 'Refinery::Widgets::Widget'
7
+ accepts_nested_attributes_for :widget
8
+
9
+
10
+ attr_accessor :widget_attributes
11
+ attr_accessible :name, :position, :container_type, :container_id,
12
+ :display, :master_id, :sub_type, :locked, :widget_attributes,
13
+ :widget_id, :layout
14
+
15
+
16
+ validates :name, :presence => true
17
+ validate :validate_widget
18
+
19
+ before_validation :update_widget
20
+
21
+ def self.reusable
22
+ where(widget_id: nil)
23
+ end
24
+
25
+ def self.on_page
26
+ where(container_type: 'Refinery::PagePart')
27
+ end
28
+
29
+ def self.on_template
30
+ where(container_type: 'Refinery::Widgets::TemplatePart')
31
+ end
32
+
33
+ def widget_type
34
+ WidgetType.find(sub_type)
35
+ end
36
+
37
+ def ultimate_widget_type
38
+ widget ? widget.ultimate_widget_type : widget_type
39
+ end
40
+
41
+ def sub_type_instance
42
+ self.send(widget_type.association_name)
43
+ end
44
+
45
+ def deep_dup
46
+ ret = self.dup
47
+ sub_type_copy = self.sub_type_instance.dup
48
+ ret.send("#{self.widget_type.association_name}=", sub_type_copy)
49
+ ret
50
+ end
51
+
52
+ def dup_by_ref
53
+ ret = Widget.new
54
+ ret.widget = self
55
+ ret.sub_type = self.class.to_s
56
+ ret.name = self.name
57
+ ret
58
+ end
59
+
60
+ def on_page?
61
+ container_type == 'Refinery::PagePart'
62
+ end
63
+
64
+ def on_template?
65
+ container_type == 'Refinery::Widgets::TemplatePart'
66
+ end
67
+
68
+ def templates
69
+ ret = []
70
+ ret << self.container.template if self.on_template? && self.container
71
+ Widget.where(widget_id: self.id).each do |w|
72
+ ret << w.container.template if w.on_template? && w.container
73
+ end
74
+ ret
75
+ end
76
+
77
+ def pages
78
+ ret = []
79
+ ret << self.container.page if self.on_page? && self.container
80
+ Widget.where(widget_id: self.id).each do |w|
81
+ ret << w.container.page if w.on_page? && w.container
82
+ end
83
+ ret
84
+ end
85
+
86
+ def times_used
87
+ if widget
88
+ widget.times_used
89
+ elsif new_record?
90
+ 0
91
+ else
92
+ Widget.where(widget_id: id).count + 1
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def update_widget
99
+ widget.attributes = @widget_attributes if widget
100
+ end
101
+
102
+ def validate_widget
103
+ widget.valid? if widget
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(virtual_path: 'refinery/admin/pages/_actions',
2
+ name: 'replace_new_page_link',
3
+ replace: "erb[loud]:contains('refinery.new_admin_page_path')",
4
+ text: "<%= link_to t('.create_new_page'), refinery.list_widgets_admin_templates_path(dialog: true), :class => \"add_icon\", :title => 'Select a template' %>",
5
+ original: "<%= link_to t('.create_new_page'), refinery.new_admin_page_path, :class => \"add_icon\" %>")
@@ -0,0 +1,4 @@
1
+ Deface::Override.new(virtual_path: 'refinery/admin/pages/_form',
2
+ name: 'insert_template_id_field_to_pages_admin_form',
3
+ insert_after: "erb[loud]:contains('form_for')",
4
+ text: "<%= f.hidden_field :template_id -%>")
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(virtual_path: 'refinery/admin/pages/_form_page_parts',
2
+ name: 'replace_page_parts_with_widget_page_parts',
3
+ replace: "erb[loud]:contains('page_part_field')",
4
+ text: "<%= render 'page_part_field', :part => p.object, :part_index => (part_index += 1), :new_part => @page.new_record?, :f => p -%>",
5
+ original: "<%= render 'page_part_field', :part => p.object, :part_index => (part_index += 1), :new_part => @page.new_record? -%>")
@@ -0,0 +1,5 @@
1
+ Deface::Override.new(virtual_path: 'refinery/admin/pages/_page',
2
+ name: 'replace_new_child_page_link',
3
+ replace: "erb[loud]:contains('refinery.new_admin_page_path')",
4
+ text: "<%= link_to refinery_icon_tag('page_add.png'), refinery.list_widgets_admin_templates_path(:dialog => true, :parent_id => page.id), :title => t('new', :scope => 'refinery.admin.pages') %>",
5
+ original: "<%= link_to refinery_icon_tag('page_add.png'), refinery.new_admin_page_path(:parent_id => page.id), :title => t('new', :scope => 'refinery.admin.pages') %>")
@@ -0,0 +1,66 @@
1
+ <% if f.object.new_record? %>
2
+ <%= render 'refinery/admin/nested_image_picker', :f => f, :field => field, :image => image, :toggle_image_display => toggle_image_display %>
3
+ <% else %>
4
+ <%
5
+ description ||= 'image'
6
+ thumbnail ||= 'medium'
7
+ toggle_image_display ||= false
8
+ conditions ||= nil
9
+ randomiser = rand(Time.now.yesterday.to_i * Time.now.to_i)
10
+ current_image_link_href = refinery.insert_admin_images_path(:dialog => true, :callback => "image_picker_#{randomiser}_changed",
11
+ :width => 866, :height => 510, :conditions => conditions)
12
+ current_image_title = t('.change', :what => description.titleize)
13
+ -%>
14
+ <div id='image_picker_container_<%= randomiser %>'>
15
+ <%= link_to t('.show'), '', :class => "current_image_toggler" if toggle_image_display %>
16
+ <%= f.hidden_field field, :class => "current_image_id" -%>
17
+
18
+ <div class='current_image_container' style="margin-top: 10px<%= ";display: none" if toggle_image_display %>">
19
+ <a class='current_image_link dialog' href="<%= current_image_link_href %>" style='border: 0px' title='<%= current_image_title %>' name='<%= current_image_title %>'>
20
+ <% unless image.nil? -%>
21
+ <%= image_fu image, Refinery::Images.user_image_sizes[thumbnail.to_sym],
22
+ :class => "brown_border",
23
+ :class => "current_picked_image" %>
24
+ <% else -%>
25
+ <img class="current_picked_image" src="" alt="" style='display: none' />
26
+ <% end -%>
27
+ <span class='no_picked_image_selected nothing_selected'<%= " style='display: none;'".html_safe if image.present? %>>
28
+ <%= t('.none_selected') %>
29
+ </span>
30
+ </a>
31
+ <br/>
32
+ <%= link_to t('.remove_current'), "#",
33
+ :class => "remove_picked_image",
34
+ :style => "#{"display:none;" if image.nil?}" %>
35
+ </div>
36
+ </div>
37
+
38
+ <% content_for :javascripts do -%>
39
+ <script>
40
+ var image_picker_<%= randomiser %> = {};
41
+ $(document).ready(function(e) {
42
+ $.extend(true, image_picker_<%= randomiser %>, image_picker)
43
+ image_picker_<%= randomiser %> = image_picker_<%= randomiser %>.init({
44
+ thumbnail: '<%= thumbnail.html_safe %>'
45
+ , image_toggler: '<%= toggle_image_display ? ".current_image_toggler" : "null" %>'
46
+ , description: '<%= description %>'
47
+ , field: '.current_image_id'
48
+ , image_display: '.current_picked_image'
49
+ , no_image_message: '.no_picked_image_selected'
50
+ , remove_image_button: '.remove_picked_image'
51
+ , picker_container: '#image_picker_container_<%= (lambda {randomiser}).call %>'
52
+ , image_container: '.current_image_container'
53
+ , image_link: '.current_image_link'
54
+ });
55
+ });
56
+
57
+ image_picker_<%= randomiser %>_changed = function(args){
58
+ $.proxy(image_picker_<%= randomiser %>.changed, {
59
+ container: '#image_picker_container_<%= randomiser %>'
60
+ , picker: image_picker_<%= randomiser %>
61
+ , image: args
62
+ })();
63
+ };
64
+ </script>
65
+ <% end %>
66
+ <% end %>
@@ -0,0 +1,58 @@
1
+ <%
2
+ description ||= 'image'
3
+ thumbnail ||= 'medium'
4
+ toggle_image_display ||= false
5
+ conditions ||= nil
6
+ randomiser = f.object.object_id
7
+ current_image_link_href = refinery.insert_admin_images_path(:dialog => true, :callback => "image_picker_#{randomiser}_changed",
8
+ :width => 866, :height => 510, :conditions => conditions)
9
+ current_image_title = t('.change', :what => description.titleize)
10
+ -%>
11
+ <div id='image_picker_container_<%= randomiser %>'>
12
+ <%= link_to t('.show'), '', :class => "current_image_toggler" if toggle_image_display %>
13
+ <%= f.hidden_field field, :class => "current_image_id" -%>
14
+
15
+ <div class='current_image_container' style="margin-top: 10px<%= ";display: none" if toggle_image_display %>">
16
+ <a class='current_image_link dialog' href="<%= current_image_link_href %>" style='border: 0px' title='<%= current_image_title %>' name='<%= current_image_title %>'>
17
+ <% unless image.nil? -%>
18
+ <%= image_fu image, Refinery::Images.user_image_sizes[thumbnail.to_sym],
19
+ :class => "brown_border",
20
+ :class => "current_picked_image" %>
21
+ <% else -%>
22
+ <img class="current_picked_image" src="" alt="" style='display: none' />
23
+ <% end -%>
24
+ <span class='no_picked_image_selected nothing_selected'<%= " style='display: none;'".html_safe if image.present? %>>
25
+ <%= t('.none_selected') %>
26
+ </span>
27
+ </a>
28
+ <br/>
29
+ <%= link_to t('.remove_current'), "#",
30
+ :class => "remove_picked_image",
31
+ :style => "#{"display:none;" if image.nil?}" %>
32
+ </div>
33
+ </div>
34
+
35
+ <script>
36
+ var image_picker_<%= randomiser %> = {};
37
+ $.extend(true, image_picker_<%= randomiser %>, image_picker);
38
+ image_picker_<%= randomiser %> = image_picker_<%= randomiser %>.init({
39
+ thumbnail: '<%= thumbnail.html_safe %>'
40
+ , image_toggler: '<%= toggle_image_display ? ".current_image_toggler" : "null" %>'
41
+ , description: '<%= description %>'
42
+ , field: '.current_image_id'
43
+ , image_display: '.current_picked_image'
44
+ , no_image_message: '.no_picked_image_selected'
45
+ , remove_image_button: '.remove_picked_image'
46
+ , picker_container: '#image_picker_container_<%= (lambda {randomiser}).call %>'
47
+ , image_container: '.current_image_container'
48
+ , image_link: '.current_image_link'
49
+ });
50
+
51
+ image_picker_<%= randomiser %>_changed = function(args){
52
+ $.proxy(image_picker_<%= randomiser %>.changed, {
53
+ container: '#image_picker_container_<%= randomiser %>'
54
+ , picker: image_picker_<%= randomiser %>
55
+ , image: args
56
+ })();
57
+ };
58
+ </script>