para 0.11.4 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/stylesheets/para/admin/main.scss +31 -0
- data/app/assets/stylesheets/para/admin/src/_affix.scss +7 -0
- data/app/assets/stylesheets/para/admin/src/_alert.scss +35 -0
- data/app/assets/stylesheets/para/admin/src/_base.scss +63 -0
- data/app/assets/stylesheets/para/admin/src/_bootstrap-variables.scss +766 -379
- data/app/assets/stylesheets/para/admin/src/_breadcrumb.scss +37 -0
- data/app/assets/stylesheets/para/admin/src/_buttons.scss +128 -0
- data/app/assets/stylesheets/para/admin/src/_checkable.scss +106 -0
- data/app/assets/stylesheets/para/admin/src/_common.scss +250 -0
- data/app/assets/stylesheets/para/admin/src/_dropdown.scss +52 -0
- data/app/assets/stylesheets/para/admin/src/_form.scss +359 -0
- data/app/assets/stylesheets/para/admin/src/_list.scss +96 -0
- data/app/assets/stylesheets/para/admin/src/_mixins.scss +109 -0
- data/app/assets/stylesheets/para/admin/src/_multi-select.scss +110 -0
- data/app/assets/stylesheets/para/admin/src/_navigation.scss +210 -0
- data/app/assets/stylesheets/para/admin/src/_navtabs.scss +68 -0
- data/app/assets/stylesheets/para/admin/src/_nested-many.scss +87 -0
- data/app/assets/stylesheets/para/admin/src/_nested_one.scss +26 -0
- data/app/assets/stylesheets/para/admin/src/_orderable.scss +48 -0
- data/app/assets/stylesheets/para/admin/src/_page-loading.scss +40 -0
- data/app/assets/stylesheets/para/admin/src/_pagination.scss +39 -0
- data/app/assets/stylesheets/para/admin/src/_panel.scss +61 -0
- data/app/assets/stylesheets/para/admin/src/_responsive.scss +133 -0
- data/app/assets/stylesheets/para/admin/src/_sorting.scss +43 -0
- data/app/assets/stylesheets/para/admin/src/_statcard.scss +46 -0
- data/app/assets/stylesheets/para/admin/src/_table.scss +36 -0
- data/app/assets/stylesheets/para/admin/src/_tree.scss +60 -0
- data/app/assets/stylesheets/para/admin/src/{_variables.sass → _variables.scss} +7 -7
- data/app/assets/stylesheets/para/admin/src/{_well.sass → _well.scss} +5 -4
- data/app/assets/stylesheets/para/admin.scss +15 -0
- data/app/assets/stylesheets/para/index.scss +1 -0
- data/app/assets/stylesheets/para/lib/datetimepicker.scss +15 -0
- data/app/assets/stylesheets/para/lib/fuelux.scss +46 -0
- data/app/assets/stylesheets/para/lib/jasny-bootstrap.scss +100 -0
- data/app/assets/stylesheets/para/lib/selectize.scss +129 -0
- data/app/assets/stylesheets/para/lib/slider.scss +14 -0
- data/app/controllers/para/admin/crud_resources_controller.rb +16 -9
- data/app/controllers/para/admin/imports_controller.rb +9 -9
- data/app/controllers/para/admin/jobs_controller.rb +7 -1
- data/app/controllers/para/admin/resources_controller.rb +17 -18
- data/app/controllers/para/admin/settings_component_controller.rb +2 -2
- data/app/helpers/para/admin/base_helper.rb +36 -25
- data/app/helpers/para/admin/page_helper.rb +8 -9
- data/app/helpers/para/flash_helper.rb +7 -28
- data/app/helpers/para/model_helper.rb +5 -8
- data/app/helpers/para/tree_helper.rb +4 -4
- data/app/javascripts/para/admin/async-progress.js +58 -0
- data/app/javascripts/para/admin/filters-form.js +12 -0
- data/app/javascripts/para/admin/job-tracker.js +33 -0
- data/app/javascripts/para/admin/table.js +58 -0
- data/app/javascripts/para/admin/tabs.js +110 -0
- data/app/javascripts/para/admin/theme_actions.js +39 -0
- data/app/javascripts/para/admin/tree.js +124 -0
- data/app/javascripts/para/application.js +2 -0
- data/app/javascripts/para/controllers/application.js +17 -0
- data/app/javascripts/para/controllers/index.js +10 -0
- data/app/javascripts/para/controllers/para_admin_flash_message_controller.js +48 -0
- data/app/javascripts/para/controllers/para_admin_modal_controller.js +43 -0
- data/app/javascripts/para/controllers/selectize_field_controller.js +142 -0
- data/app/javascripts/para/index.js +31 -0
- data/app/javascripts/para/inputs/material-input.js +7 -0
- data/app/javascripts/para/inputs/multi-select-input.js +343 -0
- data/app/javascripts/para/inputs/nested_many.js +151 -0
- data/app/javascripts/para/lib/fetch.js +41 -0
- data/app/javascripts/para/lib/page-loading.js +42 -0
- data/app/javascripts/para/simple_form_extension/colorpicker.js +30 -0
- data/app/javascripts/para/simple_form_extension/datetimepicker.js +115 -0
- data/app/javascripts/para/simple_form_extension/fileinput.js +57 -0
- data/app/javascripts/para/simple_form_extension/index.js +12 -0
- data/app/javascripts/para/simple_form_extension/selectize.js +160 -0
- data/app/javascripts/para/simple_form_extension/simple_form_extension_initialization.js +62 -0
- data/app/javascripts/para/simple_form_extension/slider.js +43 -0
- data/app/javascripts/para/simple_form_extension/spinbox.js +35 -0
- data/app/javascripts/para/vendor/bootstrap-colorpicker.js +1025 -0
- data/app/javascripts/para/vendor/bootstrap-slider.js +1610 -0
- data/app/javascripts/para/vendor/cocoon.js +150 -0
- data/app/javascripts/para/vendor/jquery.datetimepicker.js +2156 -0
- data/app/javascripts/para/vendor/jquery.js +3 -0
- data/app/javascripts/para/vendor/spinbox.js +438 -0
- data/app/javascripts/para/vendor/vertebra.js +224 -0
- data/app/views/admin/para/exporter/bases/_completed.html.haml +1 -1
- data/app/views/layouts/para/admin.html.haml +4 -2
- data/app/views/para/admin/imports/new.html.haml +12 -11
- data/app/views/para/admin/jobs/_completed.html.haml +1 -1
- data/app/views/para/admin/jobs/_failed.html.haml +1 -1
- data/app/views/para/admin/jobs/_job.html.haml +11 -0
- data/app/views/para/admin/jobs/show.html.haml +1 -10
- data/app/views/para/admin/resources/_add_button.html.haml +1 -1
- data/app/views/para/admin/resources/_exports_menu.html.haml +2 -2
- data/app/views/para/admin/resources/_filters.html.haml +1 -1
- data/app/views/para/admin/resources/_imports_menu.html.haml +2 -2
- data/app/views/para/admin/resources/_list.html.haml +27 -26
- data/app/views/para/admin/resources/_navigation.html.haml +10 -11
- data/app/views/para/admin/resources/_subclassable_add_button.html.haml +1 -1
- data/app/views/para/admin/shared/_flash.html.haml +18 -0
- data/app/views/para/admin/shared/_header.html.haml +1 -1
- data/app/views/para/form/_tabs.html.haml +1 -1
- data/app/views/para/inputs/_nested_many.html.haml +3 -3
- data/app/views/para/inputs/nested_many/_add.html.haml +1 -1
- data/app/views/para/inputs/nested_many/_add_with_subclasses.html.haml +2 -2
- data/app/views/para/inputs/nested_many/_container.html.haml +1 -1
- data/config/locales/fr.yml +1 -0
- data/lib/generators/para/install/install_generator.rb +10 -14
- data/lib/para/attribute_field/base.rb +3 -0
- data/lib/para/attribute_field/wysiwyg_editor.rb +15 -0
- data/lib/para/attribute_field.rb +1 -1
- data/lib/para/components_configuration.rb +1 -1
- data/lib/para/engine.rb +20 -22
- data/lib/para/ext/turbo_stream.rb +13 -0
- data/lib/para/ext.rb +1 -1
- data/lib/para/form_builder/containers.rb +33 -22
- data/lib/para/form_builder/nested_form.rb +6 -5
- data/lib/para/inputs/nested_many_input.rb +5 -0
- data/lib/para/markup/modal.rb +30 -15
- data/lib/para/markup/resources_buttons.rb +15 -11
- data/lib/para/model_field_parsers/wysiwyg_editor.rb +19 -0
- data/lib/para/model_field_parsers.rb +1 -1
- data/lib/para/version.rb +1 -1
- data/lib/para.rb +2 -8
- data/lib/simple_form_extension/components/icons.rb +13 -0
- data/lib/simple_form_extension/components/popovers.rb +46 -0
- data/lib/simple_form_extension/components.rb +8 -0
- data/lib/simple_form_extension/ext/form_builder.rb +20 -0
- data/lib/simple_form_extension/file_concern.rb +74 -0
- data/lib/simple_form_extension/inputs/boolean_input.rb +16 -0
- data/lib/simple_form_extension/inputs/collection_check_boxes_input.rb +15 -0
- data/lib/simple_form_extension/inputs/collection_radio_buttons_input.rb +15 -0
- data/lib/simple_form_extension/inputs/color_input.rb +37 -0
- data/lib/simple_form_extension/inputs/date_time_input.rb +48 -0
- data/lib/simple_form_extension/inputs/file_input.rb +41 -0
- data/lib/simple_form_extension/inputs/image_input.rb +110 -0
- data/lib/simple_form_extension/inputs/numeric_input.rb +27 -0
- data/lib/simple_form_extension/inputs/selectize_input.rb +210 -0
- data/lib/simple_form_extension/inputs/slider_input.rb +30 -0
- data/lib/simple_form_extension/inputs.rb +16 -0
- data/lib/simple_form_extension/railtie.rb +21 -0
- data/lib/simple_form_extension/resource_name_helper.rb +9 -0
- data/lib/simple_form_extension/translations.rb +7 -0
- data/lib/simple_form_extension/version.rb +3 -0
- data/lib/simple_form_extension.rb +30 -0
- data/vendor/assets/images/simple_form_extension/bootstrap-colorpicker/alpha-horizontal.png +0 -0
- data/vendor/assets/images/simple_form_extension/bootstrap-colorpicker/alpha.png +0 -0
- data/vendor/assets/images/simple_form_extension/bootstrap-colorpicker/hue-horizontal.png +0 -0
- data/vendor/assets/images/simple_form_extension/bootstrap-colorpicker/hue.png +0 -0
- data/vendor/assets/images/simple_form_extension/bootstrap-colorpicker/saturation.png +0 -0
- data/vendor/assets/stylesheets/bootstrap-colorpicker.scss +227 -0
- data/vendor/assets/stylesheets/bootstrap-slider.scss +246 -0
- data/vendor/assets/stylesheets/bootstrap.scss +6032 -0
- data/vendor/assets/stylesheets/jquery.datetimepicker.scss +568 -0
- metadata +120 -188
- data/app/assets/javascripts/admin/app.coffee +0 -1
- data/app/assets/javascripts/para/admin/async-progress.coffee +0 -29
- data/app/assets/javascripts/para/admin/filters-form.coffee +0 -11
- data/app/assets/javascripts/para/admin/job-tracker.coffee +0 -39
- data/app/assets/javascripts/para/admin/table.coffee +0 -44
- data/app/assets/javascripts/para/admin/tabs.coffee +0 -63
- data/app/assets/javascripts/para/admin/theme_actions.coffee +0 -25
- data/app/assets/javascripts/para/admin/tree.coffee +0 -105
- data/app/assets/javascripts/para/admin.coffee +0 -22
- data/app/assets/javascripts/para/application.js +0 -13
- data/app/assets/javascripts/para/inputs/material-input.coffee +0 -4
- data/app/assets/javascripts/para/inputs/multi-select-input.coffee +0 -174
- data/app/assets/javascripts/para/inputs/nested_many.coffee +0 -108
- data/app/assets/javascripts/para/lib/ajax.coffee +0 -11
- data/app/assets/javascripts/para/lib/remote-file-forms.coffee +0 -59
- data/app/assets/javascripts/para/lib/turbolinks-forms.coffee +0 -23
- data/app/assets/javascripts/para/lib/turbolinks-loading.coffee +0 -25
- data/app/assets/javascripts/para/lib/turbolinks-reloader.coffee +0 -19
- data/app/assets/stylesheets/para/admin/main.sass +0 -33
- data/app/assets/stylesheets/para/admin/src/_affix.sass +0 -4
- data/app/assets/stylesheets/para/admin/src/_alert.sass +0 -17
- data/app/assets/stylesheets/para/admin/src/_base.sass +0 -54
- data/app/assets/stylesheets/para/admin/src/_breadcrumb.sass +0 -32
- data/app/assets/stylesheets/para/admin/src/_buttons.sass +0 -107
- data/app/assets/stylesheets/para/admin/src/_checkable.sass +0 -94
- data/app/assets/stylesheets/para/admin/src/_common.sass +0 -202
- data/app/assets/stylesheets/para/admin/src/_dropdown.sass +0 -40
- data/app/assets/stylesheets/para/admin/src/_form.sass +0 -299
- data/app/assets/stylesheets/para/admin/src/_list.sass +0 -89
- data/app/assets/stylesheets/para/admin/src/_mixins.sass +0 -113
- data/app/assets/stylesheets/para/admin/src/_multi-select.sass +0 -91
- data/app/assets/stylesheets/para/admin/src/_navigation.sass +0 -171
- data/app/assets/stylesheets/para/admin/src/_navtabs.sass +0 -58
- data/app/assets/stylesheets/para/admin/src/_nested-many.sass +0 -75
- data/app/assets/stylesheets/para/admin/src/_nested_one.sass +0 -22
- data/app/assets/stylesheets/para/admin/src/_orderable.sass +0 -44
- data/app/assets/stylesheets/para/admin/src/_page-loading.sass +0 -39
- data/app/assets/stylesheets/para/admin/src/_pagination.sass +0 -34
- data/app/assets/stylesheets/para/admin/src/_panel.sass +0 -55
- data/app/assets/stylesheets/para/admin/src/_responsive.sass +0 -117
- data/app/assets/stylesheets/para/admin/src/_sorting.sass +0 -36
- data/app/assets/stylesheets/para/admin/src/_statcard.sass +0 -41
- data/app/assets/stylesheets/para/admin/src/_table.sass +0 -33
- data/app/assets/stylesheets/para/admin/src/_tree.sass +0 -52
- data/app/assets/stylesheets/para/admin.sass +0 -12
- data/app/assets/stylesheets/para/lib/compass/_support.scss +0 -447
- data/app/assets/stylesheets/para/lib/compass/css3/_box-shadow.scss +0 -88
- data/app/assets/stylesheets/para/lib/compass/css3/_images.scss +0 -152
- data/app/assets/stylesheets/para/lib/compass/css3/_inline-block.scss +0 -31
- data/app/assets/stylesheets/para/lib/compass/css3/_text-shadow.scss +0 -82
- data/app/assets/stylesheets/para/lib/compass/css3/_transform.scss +0 -590
- data/app/assets/stylesheets/para/lib/compass/css3/_transition.scss +0 -190
- data/app/assets/stylesheets/para/lib/compass/css3/_user-interface.scss +0 -71
- data/app/assets/stylesheets/para/lib/compass/utilities/general/_hacks.scss +0 -65
- data/app/assets/stylesheets/para/lib/datetimepicker.sass +0 -15
- data/app/assets/stylesheets/para/lib/fuelux.sass +0 -40
- data/app/assets/stylesheets/para/lib/jasny-bootstrap.sass +0 -97
- data/app/assets/stylesheets/para/lib/redactor.sass +0 -86
- data/app/assets/stylesheets/para/lib/selectize.sass +0 -111
- data/app/assets/stylesheets/para/lib/slider.sass +0 -12
- data/app/assets/stylesheets/para/plugins-includes.sass.erb +0 -1
- data/lib/para/attribute_field/redactor.rb +0 -22
- data/lib/para/ext/request_iframe_xhr.rb +0 -17
- data/lib/para/iframe_transport/middleware.rb +0 -58
- data/lib/para/iframe_transport.rb +0 -7
- data/lib/para/model_field_parsers/redactor.rb +0 -19
- data/vendor/assets/javascripts/jquery.iframe-transport.js +0 -260
- data/vendor/assets/javascripts/jquery.remote-modal-form.coffee +0 -145
- /data/app/assets/stylesheets/admin/{app.sass → app.scss} +0 -0
- /data/app/{assets/javascripts → javascripts}/para/plugins-includes.js.erb +0 -0
- /data/{vendor/assets/javascripts → app/javascripts/para/vendor}/Sortable.js +0 -0
- /data/{vendor/assets/javascripts → app/javascripts/para/vendor}/jasny-bootstrap.js +0 -0
- /data/{vendor/assets/javascripts → app/javascripts/para/vendor}/jquery.scrollto.js +0 -0
- /data/{vendor/assets/javascripts → app/javascripts/para/vendor}/jquery.sortable.js +0 -0
- /data/vendor/assets/stylesheets/{animate.css → animate.scss} +0 -0
- /data/vendor/assets/stylesheets/{hint.css → hint.scss} +0 -0
- /data/vendor/assets/stylesheets/{jasny-bootstrap.css → jasny-bootstrap.scss} +0 -0
@@ -1,105 +0,0 @@
|
|
1
|
-
class Para.ResourceTree
|
2
|
-
constructor: (@$el) ->
|
3
|
-
@initializeTree()
|
4
|
-
|
5
|
-
initializeTree: ->
|
6
|
-
@orderUrl = @$el.data('url')
|
7
|
-
@maxDepth = parseInt @$el.data('max-depth')
|
8
|
-
|
9
|
-
# Each is needed here as the sortable jQuery plugin doesn't loop over each found node
|
10
|
-
# but initializes the tree on the first found element.
|
11
|
-
$(".tree").each(@initializeSubTree)
|
12
|
-
|
13
|
-
initializeSubTree: (_i, el) =>
|
14
|
-
$(el).sortable(
|
15
|
-
group: "tree"
|
16
|
-
handle: ".handle"
|
17
|
-
draggable: ".node"
|
18
|
-
fallbackOnBody: true
|
19
|
-
swapThreshold: 0.65
|
20
|
-
animation: 150
|
21
|
-
onSort: @handleOrderUpdated
|
22
|
-
onMove: @isMovementValid
|
23
|
-
)
|
24
|
-
|
25
|
-
# Note : This method is called often (many times per second while we're dragging) and
|
26
|
-
# takes quite some processing.
|
27
|
-
isMovementValid: (e) =>
|
28
|
-
$movedNode = $(e.dragged)
|
29
|
-
$target = $(e.related)
|
30
|
-
|
31
|
-
# Calculate the deepness of the moved and target nodes
|
32
|
-
movedNodeDeepness = $movedNode.parents(".node").length - 1
|
33
|
-
# If the target is a node, the moved node root deepness is gonna be the same as the
|
34
|
-
# the target one, else the tree's parent node is counted also
|
35
|
-
targetDeepness = $target.parents(".node").length - 1
|
36
|
-
|
37
|
-
# Find the deepest node in the subtree of the moved node
|
38
|
-
$movedNodeSubtrees = $movedNode.find(".tree")
|
39
|
-
movedNodeTreeDeepness = 0
|
40
|
-
|
41
|
-
# The movedNodeTreeDeepness is the maximum deepness of a child node of the current
|
42
|
-
# moved node, relative to the moved node
|
43
|
-
$movedNodeSubtrees.each (i, el) =>
|
44
|
-
subtreeDeepness = $(el).parents(".node").length - 1
|
45
|
-
subtreeRelativeDeepness = subtreeDeepness - movedNodeDeepness
|
46
|
-
movedNodeTreeDeepness = Math.max(movedNodeTreeDeepness, subtreeRelativeDeepness)
|
47
|
-
|
48
|
-
# Calculate the final subtree deepness once we move the whole moved node subtree to
|
49
|
-
# its target position
|
50
|
-
finalSubtreeDeepnessAfterMove = movedNodeTreeDeepness + targetDeepness
|
51
|
-
|
52
|
-
# We finally validate the move only if the final subtree deepness is lower than the
|
53
|
-
# maximum allowed depth
|
54
|
-
finalSubtreeDeepnessAfterMove <= @maxDepth
|
55
|
-
|
56
|
-
handleOrderUpdated: (e) =>
|
57
|
-
|
58
|
-
# Get all involved tree leaves that may include a subtree
|
59
|
-
treeLeaves = [$(e.target), $(e.from), $(e.item).find('.tree')]
|
60
|
-
|
61
|
-
# Update their placeholder display wether they can be a drop target or not
|
62
|
-
@handlePlaceholder($el) for $el in treeLeaves
|
63
|
-
# Save the tree structure on the server
|
64
|
-
@updateOrder()
|
65
|
-
|
66
|
-
# This method checks wether a given tree leaf can be a drop target, depending
|
67
|
-
# on wether it's located at the maximum allowed depth for the tree or not, and adds or
|
68
|
-
# remove a the visual placeholder to indicate its droppable state.
|
69
|
-
#
|
70
|
-
handlePlaceholder: ($el) ->
|
71
|
-
$placeholder = $el.find("> .placeholder")
|
72
|
-
parentsCount = $el.parents('.node').length - 1
|
73
|
-
hasChildren = $el.find('> .node').length
|
74
|
-
|
75
|
-
if parentsCount >= @maxDepth or hasChildren
|
76
|
-
$placeholder.hide()
|
77
|
-
$el.children(".tree").each (index, el) => @handlePlaceholder($(el))
|
78
|
-
else
|
79
|
-
$placeholder.show()
|
80
|
-
|
81
|
-
updateOrder: ->
|
82
|
-
Para.ajax(
|
83
|
-
url: @orderUrl
|
84
|
-
method: 'patch'
|
85
|
-
data: { resources: @buildOrderedData() }
|
86
|
-
success: @orderUpdated
|
87
|
-
)
|
88
|
-
|
89
|
-
buildOrderedData: ->
|
90
|
-
data = {}
|
91
|
-
$(".node").each (index) ->
|
92
|
-
$this = $(this)
|
93
|
-
data[index] = {
|
94
|
-
id: $this.data("id"),
|
95
|
-
position: index,
|
96
|
-
parent_id: $this.parents(".node:first").data("id")
|
97
|
-
}
|
98
|
-
data
|
99
|
-
|
100
|
-
orderUpdated: =>
|
101
|
-
# TODO: Add flash message to display ordering success
|
102
|
-
|
103
|
-
$(document).on 'page:change turbolinks:load', ->
|
104
|
-
$('.root-tree').each (i, el) ->
|
105
|
-
new Para.ResourceTree($(el))
|
@@ -1,22 +0,0 @@
|
|
1
|
-
#= require jquery
|
2
|
-
#= require jquery_ujs
|
3
|
-
#= require turbolinks
|
4
|
-
#= require bootstrap
|
5
|
-
#= require vertebra
|
6
|
-
#= require jasny-bootstrap
|
7
|
-
#= require simple_form_extension
|
8
|
-
#= require jquery.scrollto
|
9
|
-
#= require Sortable
|
10
|
-
#= require jquery.sortable
|
11
|
-
#= require cocoon
|
12
|
-
#= require jquery.remote-modal-form
|
13
|
-
#= require jquery.iframe-transport
|
14
|
-
#= require_self
|
15
|
-
#= require_tree ./lib
|
16
|
-
#= require_tree ./inputs
|
17
|
-
#= require_tree ./admin
|
18
|
-
#= require ./plugins-includes
|
19
|
-
#= require admin/app
|
20
|
-
|
21
|
-
# Initialize scope
|
22
|
-
window.Para = {}
|
@@ -1,13 +0,0 @@
|
|
1
|
-
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
-
// listed below.
|
3
|
-
//
|
4
|
-
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
-
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
6
|
-
//
|
7
|
-
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
-
// compiled file.
|
9
|
-
//
|
10
|
-
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
11
|
-
// about supported directives.
|
12
|
-
//
|
13
|
-
//= require_tree .
|
@@ -1,174 +0,0 @@
|
|
1
|
-
class Para.MultiSelectInput extends Vertebra.View
|
2
|
-
events:
|
3
|
-
'keyup [data-search-field]': 'onSearchKeyUp'
|
4
|
-
'click [data-add-all]': 'onAllItemsAdded'
|
5
|
-
'click [data-remove-all]': 'onAllItemsRemoved'
|
6
|
-
|
7
|
-
initialize: ->
|
8
|
-
@$searchField = @$('[data-search-field]')
|
9
|
-
@$selectedItems = @$('[data-selected-items] tbody')
|
10
|
-
@$availableItems = @$('[data-available-items]')
|
11
|
-
@$input = @$('[data-multi-select-input-field]')
|
12
|
-
|
13
|
-
@searchURL = @$el.data('search-url')
|
14
|
-
@orderable = @$el.is('[data-orderable]')
|
15
|
-
@searchParam = @$searchField.attr('name')
|
16
|
-
|
17
|
-
@noSelectedItemsTemplate = @$('[data-no-selected-items]').data('no-selected-items')
|
18
|
-
@noAvailableItemsTemplate = @$('[data-no-available-items]').data('no-available-items')
|
19
|
-
|
20
|
-
@throttledTriggerSearch = throttle(@triggerSearch, 300, trailing: true)
|
21
|
-
|
22
|
-
@availableItems = []
|
23
|
-
@selectedItems = (@buildSelectedItem(el) for el in @$selectedItems.find('[data-selected-item-id]'))
|
24
|
-
@refreshSelectedItems()
|
25
|
-
@refreshAvailableItems()
|
26
|
-
|
27
|
-
onSearchKeyUp: ->
|
28
|
-
@throttledTriggerSearch()
|
29
|
-
|
30
|
-
triggerSearch: =>
|
31
|
-
@searchFor(@$searchField.val())
|
32
|
-
|
33
|
-
searchFor: (terms) ->
|
34
|
-
terms = trim(terms)
|
35
|
-
return if terms is @terms or (!terms and !@terms)
|
36
|
-
@terms = terms
|
37
|
-
@setLoading(true)
|
38
|
-
@_currentSearchXHR?.abort()
|
39
|
-
data = @$('[data-search-field-input]').serialize()
|
40
|
-
@_currentSearchXHR = $.get(@searchURL, data).done(@onSearchReturn)
|
41
|
-
|
42
|
-
onSearchReturn: (results) =>
|
43
|
-
@_currentSearchXHR = null
|
44
|
-
@setLoading(false)
|
45
|
-
@$('[data-available-items] tbody').html(results)
|
46
|
-
@refreshAvailableItems()
|
47
|
-
|
48
|
-
refreshAvailableItems: ->
|
49
|
-
item.destroy() for item in @availableItems
|
50
|
-
@availableItems = (@buildAvailableItem(el) for el in @$('[data-available-items] tr'))
|
51
|
-
@showEmptyListHint(@noAvailableItemsTemplate, @$availableItems) unless @availableItems.length
|
52
|
-
|
53
|
-
buildAvailableItem: (el) ->
|
54
|
-
availableItem = new Para.MultiSelectAvailableItem(el: el)
|
55
|
-
availableItem.setSelected(true) for selectedItem in @selectedItems when selectedItem.id is availableItem.id
|
56
|
-
@listenTo(availableItem, 'add', @onItemAdded)
|
57
|
-
availableItem
|
58
|
-
|
59
|
-
onItemAdded: (item) =>
|
60
|
-
@selectItem(item)
|
61
|
-
|
62
|
-
buildSelectedItem: (el) ->
|
63
|
-
selectedItem = new Para.MultiSelectSelectedItem(el: el)
|
64
|
-
@listenTo(selectedItem, 'remove', @onItemRemoved)
|
65
|
-
selectedItem
|
66
|
-
|
67
|
-
selectItem: (item) ->
|
68
|
-
return if @alreadySelected(item)
|
69
|
-
|
70
|
-
item.setSelected(true)
|
71
|
-
selectedItem = @buildSelectedItem(item.$el.attr('data-selected-item-template'))
|
72
|
-
@selectedItems.push(selectedItem)
|
73
|
-
@refreshSelectedItems()
|
74
|
-
|
75
|
-
alreadySelected: (item) ->
|
76
|
-
return true for selectedItem in @selectedItems when selectedItem.id is item.id
|
77
|
-
false
|
78
|
-
|
79
|
-
refreshSelectedItems: ->
|
80
|
-
@$selectedItems.empty()
|
81
|
-
selectedItem.renderTo(@$selectedItems) for selectedItem in @selectedItems
|
82
|
-
|
83
|
-
selectedItemIds = (selectedItem.id for selectedItem in @selectedItems).join(', ')
|
84
|
-
@$input.val(selectedItemIds)
|
85
|
-
|
86
|
-
if @selectedItems.length
|
87
|
-
@initializeOrderable()
|
88
|
-
else
|
89
|
-
@showEmptyListHint(@noSelectedItemsTemplate, @$selectedItems)
|
90
|
-
|
91
|
-
showEmptyListHint: (template, $container) ->
|
92
|
-
$(template).appendTo($container)
|
93
|
-
|
94
|
-
onItemRemoved: (selectedItem) =>
|
95
|
-
itemIndex = index for item, index in @selectedItems when item.id is selectedItem.id
|
96
|
-
@selectedItems.splice(itemIndex, 1)
|
97
|
-
selectedItem.destroy()
|
98
|
-
@refreshSelectedItems()
|
99
|
-
|
100
|
-
availableItem = item for item in @availableItems when item.id is selectedItem.id
|
101
|
-
availableItem.setSelected(false) if availableItem
|
102
|
-
|
103
|
-
onAllItemsAdded: ->
|
104
|
-
return unless @availableItems.length
|
105
|
-
|
106
|
-
@selectItem(availableItem) for availableItem in @availableItems
|
107
|
-
|
108
|
-
onAllItemsRemoved: ->
|
109
|
-
return unless @selectedItems.length
|
110
|
-
|
111
|
-
@selectedItems = []
|
112
|
-
@refreshSelectedItems()
|
113
|
-
|
114
|
-
item.setSelected(false) for item in @availableItems
|
115
|
-
|
116
|
-
initializeOrderable: ->
|
117
|
-
return unless @orderable
|
118
|
-
|
119
|
-
columnsCount = @$selectedItems.find('> tr > td').length
|
120
|
-
|
121
|
-
@$selectedItems.sortable('destroy')
|
122
|
-
|
123
|
-
@$selectedItems.sortable
|
124
|
-
handle: '.order-anchor'
|
125
|
-
animation: 150
|
126
|
-
|
127
|
-
@$selectedItems.on('sort', @selectedItemsSorted)
|
128
|
-
|
129
|
-
selectedItemsSorted: =>
|
130
|
-
indices = {}
|
131
|
-
indices[$(el).data('selected-item-id')] = index for el, index in @$selectedItems.find('[data-selected-item-id]')
|
132
|
-
|
133
|
-
@selectedItems.sort (a, b) =>
|
134
|
-
aIndex = indices[a.id]
|
135
|
-
bIndex = indices[b.id]
|
136
|
-
if aIndex > bIndex then 1 else -1
|
137
|
-
|
138
|
-
@refreshSelectedItems()
|
139
|
-
|
140
|
-
setLoading: (state) ->
|
141
|
-
@$el.toggleClass('loading', state)
|
142
|
-
@$('.fa-search').toggleClass('fa-spin', state)
|
143
|
-
|
144
|
-
class Para.MultiSelectAvailableItem extends Vertebra.View
|
145
|
-
events:
|
146
|
-
'click [data-add-item]': 'addItem'
|
147
|
-
|
148
|
-
initialize: ->
|
149
|
-
@id = @$el.data('available-item-id')
|
150
|
-
|
151
|
-
addItem: ->
|
152
|
-
@trigger('add', this)
|
153
|
-
|
154
|
-
setSelected: (@selected) ->
|
155
|
-
@$el.toggleClass('selected', @selected)
|
156
|
-
|
157
|
-
|
158
|
-
class Para.MultiSelectSelectedItem extends Vertebra.View
|
159
|
-
initialize: ->
|
160
|
-
@id = @$el.data('selected-item-id')
|
161
|
-
|
162
|
-
bindEvents: ->
|
163
|
-
@$('[data-remove-item]').on 'click', @removeItem
|
164
|
-
|
165
|
-
renderTo: ($container) ->
|
166
|
-
@$el.appendTo($container)
|
167
|
-
@bindEvents()
|
168
|
-
|
169
|
-
removeItem: (e) =>
|
170
|
-
@trigger('remove', this)
|
171
|
-
|
172
|
-
|
173
|
-
$.simpleForm.onDomReady ($document) ->
|
174
|
-
$document.find('[data-multi-select-input]').each (i, el) -> new Para.MultiSelectInput(el: el)
|
@@ -1,108 +0,0 @@
|
|
1
|
-
class Para.NestedManyField
|
2
|
-
constructor: (@$field) ->
|
3
|
-
@$fieldsList = @$field.find('.fields-list')
|
4
|
-
|
5
|
-
@initializeOrderable()
|
6
|
-
@initializeCocoon()
|
7
|
-
|
8
|
-
@$field.on 'shown.bs.collapse', @stoppingPropagation(@collapseShown)
|
9
|
-
|
10
|
-
initializeOrderable: ->
|
11
|
-
@orderable = @$field.hasClass('orderable')
|
12
|
-
return unless @orderable
|
13
|
-
|
14
|
-
@$fieldsList.sortable
|
15
|
-
handle: '.order-anchor'
|
16
|
-
animation: 150
|
17
|
-
onUpdate: $.proxy(@handleOrderingUpdated, this)
|
18
|
-
|
19
|
-
handleOrderingUpdated: ->
|
20
|
-
formFields = []
|
21
|
-
|
22
|
-
@$fieldsList.find('.form-fields:visible').each (_i, el) ->
|
23
|
-
isNestedField = $parent.find(el).length for $parent in formFields
|
24
|
-
return if isNestedField
|
25
|
-
|
26
|
-
$el = $(el)
|
27
|
-
$el.find('.resource-position-field:eq(0)').val(formFields.length)
|
28
|
-
formFields.push($el)
|
29
|
-
|
30
|
-
initializeCocoon: ->
|
31
|
-
@$fieldsList.on 'cocoon:after-insert', @stoppingPropagation(@afterInsertField)
|
32
|
-
@$fieldsList.on 'cocoon:before-remove', @stoppingPropagation(@beforeRemoveField)
|
33
|
-
@$fieldsList.on 'cocoon:after-remove', @stoppingPropagation(@afterRemoveField)
|
34
|
-
|
35
|
-
stoppingPropagation: (callback) =>
|
36
|
-
(e, args...) =>
|
37
|
-
e.stopPropagation()
|
38
|
-
callback(e, args...)
|
39
|
-
|
40
|
-
afterInsertField: (e, $element) =>
|
41
|
-
if ($collapsible = $element.find('[data-open-on-insert="true"]')).length
|
42
|
-
@openInsertedField($collapsible)
|
43
|
-
|
44
|
-
if @orderable
|
45
|
-
@$fieldsList.sortable('destroy')
|
46
|
-
@initializeOrderable()
|
47
|
-
@handleOrderingUpdated()
|
48
|
-
|
49
|
-
$element.simpleForm()
|
50
|
-
|
51
|
-
beforeRemoveField: (e, $element) =>
|
52
|
-
$nextEl = $element.next()
|
53
|
-
# Remove attributes mappings field for new records since it will try to
|
54
|
-
# create an empty nested resource otherwise
|
55
|
-
$nextEl.remove() if $nextEl.is('[data-attributes-mappings]') and not $element.is('[data-persisted]')
|
56
|
-
|
57
|
-
# When a sub field is removed, update every sub field position
|
58
|
-
afterRemoveField: =>
|
59
|
-
@handleOrderingUpdated();
|
60
|
-
|
61
|
-
openInsertedField: ($field) ->
|
62
|
-
$target = $($field.attr('href'))
|
63
|
-
$target.collapse('show')
|
64
|
-
|
65
|
-
collapseShown: (e) =>
|
66
|
-
$target = $(e.target)
|
67
|
-
|
68
|
-
if $target.is("[data-rendered]") or $target.data("rendered")
|
69
|
-
@initializeCollapseContent($target)
|
70
|
-
else
|
71
|
-
@loadCollapseContent($target)
|
72
|
-
@scrollToTarget($target)
|
73
|
-
|
74
|
-
initializeCollapseContent: ($target) ->
|
75
|
-
@scrollToTarget($target)
|
76
|
-
@focusFirstField($target)
|
77
|
-
|
78
|
-
scrollToTarget: ($target) ->
|
79
|
-
$field = @$field.find("[data-toggle='collapse'][href='##{ $target.attr('id') }']")
|
80
|
-
scrollOffset = -($('[data-header]').outerHeight() + 30)
|
81
|
-
|
82
|
-
if ($affixNavTabs = $("[data-tabs-nav-affix]:eq(0)")).length
|
83
|
-
scrollOffset -= $affixNavTabs.outerHeight()
|
84
|
-
|
85
|
-
$.scrollTo($field, 200, offset: scrollOffset)
|
86
|
-
|
87
|
-
focusFirstField: ($target) ->
|
88
|
-
$target.find('input, textarea, select').eq('0').focus()
|
89
|
-
|
90
|
-
loadCollapseContent: ($target) ->
|
91
|
-
targetUrl = $target.data("render-path")
|
92
|
-
return unless targetUrl
|
93
|
-
|
94
|
-
data = {
|
95
|
-
"id": $target.data("id")
|
96
|
-
"object_name": $target.data("object-name")
|
97
|
-
"model_name": $target.data("model-name")
|
98
|
-
}
|
99
|
-
|
100
|
-
$.get(targetUrl, data).then (resp) =>
|
101
|
-
$content = $(resp)
|
102
|
-
$target.find("[data-nested-form-container]:eq(0)").html($content)
|
103
|
-
$content.simpleForm()
|
104
|
-
@focusFirstField($target)
|
105
|
-
$target.data("rendered", true)
|
106
|
-
|
107
|
-
$.simpleForm.onDomReady ($document) ->
|
108
|
-
$document.find('.nested-many-field').each (i, el) -> new Para.NestedManyField($(el))
|
@@ -1,11 +0,0 @@
|
|
1
|
-
Para.ajax = (options = {}) ->
|
2
|
-
csrfParam = $('meta[name="csrf-param"]').attr('content')
|
3
|
-
csrfToken = $('meta[name="csrf-token"]').attr('content')
|
4
|
-
|
5
|
-
csrfOptions = {}
|
6
|
-
csrfOptions[csrfParam] = csrfToken if csrfParam and csrfToken
|
7
|
-
|
8
|
-
unless options.method and options.method.match(/get/i)
|
9
|
-
options = $.extend(csrfOptions, options)
|
10
|
-
|
11
|
-
$.ajax(options)
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# This class handles rails' remote form 'ajax:aborted:file' event to serialize
|
2
|
-
# forms with files and send them through ajax using the `FormData` HTML5
|
3
|
-
# feature.
|
4
|
-
#
|
5
|
-
# The general approach and request implementation respects the same lifecycle
|
6
|
-
# of original rails' remote form requests, to allow existing handling code
|
7
|
-
# to run without any modification.
|
8
|
-
#
|
9
|
-
class @RemoteFileForm extends Vertebra.View
|
10
|
-
@supported = FormData isnt undefined
|
11
|
-
|
12
|
-
initialize: (options = {}) ->
|
13
|
-
@submitForm() if $.rails.fire(@$el, 'ajax:before')
|
14
|
-
|
15
|
-
submitForm: ->
|
16
|
-
formData = new FormData(@$el[0])
|
17
|
-
url = @$el.attr('action')
|
18
|
-
method = @$el.attr('method')
|
19
|
-
|
20
|
-
Para.ajax
|
21
|
-
url: url
|
22
|
-
method: method
|
23
|
-
data: formData
|
24
|
-
cache: false
|
25
|
-
contentType: false
|
26
|
-
processData: false
|
27
|
-
xhr: @buildXHR
|
28
|
-
beforeSend: @beforeSend
|
29
|
-
success: @success
|
30
|
-
complete: @complete
|
31
|
-
error: @error
|
32
|
-
crossDomain: $.rails.isCrossDomain?(url)
|
33
|
-
|
34
|
-
buildXHR: =>
|
35
|
-
xhr = $.ajaxSettings.xhr()
|
36
|
-
$(xhr.upload).on('progress', @progress) if xhr.upload
|
37
|
-
xhr
|
38
|
-
|
39
|
-
beforeSend: (xhr, settings) =>
|
40
|
-
if $.rails.fire(@$el, 'ajax:beforeSend', [xhr, settings])
|
41
|
-
@$el.trigger('ajax:send', xhr)
|
42
|
-
else
|
43
|
-
return false
|
44
|
-
|
45
|
-
success: (data, status, xhr) =>
|
46
|
-
@$el.trigger('ajax:success', [data, status, xhr])
|
47
|
-
|
48
|
-
complete: (xhr, status) =>
|
49
|
-
@$el.trigger('ajax:complete', [xhr, status])
|
50
|
-
|
51
|
-
error: (xhr, status, error) =>
|
52
|
-
@$el.trigger('ajax:error', [xhr, status, error])
|
53
|
-
|
54
|
-
progress: (e) =>
|
55
|
-
@trigger('progress', e)
|
56
|
-
|
57
|
-
$(document).on 'ajax:aborted:file', (e) ->
|
58
|
-
new RemoteFileForm(el: e.target)
|
59
|
-
return false
|
@@ -1,23 +0,0 @@
|
|
1
|
-
class Turbolinks.Form
|
2
|
-
constructor: (@$form) ->
|
3
|
-
@$form.on('submit', (e) => @submit(e))
|
4
|
-
|
5
|
-
submit: (e) ->
|
6
|
-
e.preventDefault()
|
7
|
-
@visit(@buildUrl())
|
8
|
-
|
9
|
-
buildUrl: ->
|
10
|
-
url = @$form.attr('action')
|
11
|
-
url += if url.indexOf('?') is -1 then '?' else '&'
|
12
|
-
url + @$form.serialize()
|
13
|
-
|
14
|
-
visit: (url) ->
|
15
|
-
Turbolinks.visit(url);
|
16
|
-
|
17
|
-
|
18
|
-
$(document).on "submit", "form[method='get']:not([data-no-turboform])", (e) ->
|
19
|
-
$form = $(e.currentTarget)
|
20
|
-
unless (turboform = $form.data('turboform-instance'))
|
21
|
-
turboform = new Turbolinks.Form($form)
|
22
|
-
$form.data('turboform-instance', turboform)
|
23
|
-
turboform.submit(e)
|
@@ -1,25 +0,0 @@
|
|
1
|
-
class Para.TurbolinksLoading
|
2
|
-
start: =>
|
3
|
-
@addLoadingMarkup()
|
4
|
-
|
5
|
-
stop: =>
|
6
|
-
@removeLoadingMarkup()
|
7
|
-
|
8
|
-
addLoadingMarkup: ->
|
9
|
-
$('<div/>', class: 'loading-overlay', 'data-loading-overlay': true)
|
10
|
-
.prependTo('body')
|
11
|
-
$('<div/>', class: 'loading-spinner', 'data-loading-spinner': true)
|
12
|
-
.prependTo('body')
|
13
|
-
|
14
|
-
removeLoadingMarkup: ->
|
15
|
-
$('[data-loading-overlay]').remove()
|
16
|
-
$('[data-loading-spinner]').remove()
|
17
|
-
|
18
|
-
# Global loading manager allowing to
|
19
|
-
Para.loadingManager = new Para.TurbolinksLoading()
|
20
|
-
|
21
|
-
$(document).on('page:fetch', Para.loadingManager.start)
|
22
|
-
|
23
|
-
$(document).on 'page:change turbolinks:load', ->
|
24
|
-
Para.loadingManager.stop()
|
25
|
-
$('body').on('submit', '[data-para-form]:not([data-remote])', Para.loadingManager.start)
|
@@ -1,19 +0,0 @@
|
|
1
|
-
class TurbolinksReloader
|
2
|
-
run: (options = {}) ->
|
3
|
-
options.keepScrollPosition ?= true
|
4
|
-
|
5
|
-
if options.keepScrollPosition
|
6
|
-
$(document).one('page:before-unload', $.proxy(@beforeUnload, this))
|
7
|
-
$(document).one('page:load', $.proxy(@onChange, this))
|
8
|
-
|
9
|
-
Turbolinks.visit(window.location.href)
|
10
|
-
|
11
|
-
beforeUnload: ->
|
12
|
-
@scrollPosition = $(document).scrollTop()
|
13
|
-
|
14
|
-
onChange: ->
|
15
|
-
$(document).scrollTop(@scrollPosition)
|
16
|
-
|
17
|
-
$ ->
|
18
|
-
Turbolinks.reloadPage = (options = {}) ->
|
19
|
-
new TurbolinksReloader().run(options)
|
@@ -1,33 +0,0 @@
|
|
1
|
-
@charset "UTF-8"
|
2
|
-
//** Import google font
|
3
|
-
@import url('//fonts.googleapis.com/css?family=Roboto:300,400,500,700,900')
|
4
|
-
|
5
|
-
@import "src/variables"
|
6
|
-
@import "bootstrap"
|
7
|
-
|
8
|
-
@import "src/mixins"
|
9
|
-
@import "src/common"
|
10
|
-
@import "src/base"
|
11
|
-
@import "src/buttons"
|
12
|
-
@import "src/form"
|
13
|
-
@import "src/nested_one"
|
14
|
-
@import "src/table"
|
15
|
-
@import "src/list"
|
16
|
-
@import "src/panel"
|
17
|
-
@import "src/affix"
|
18
|
-
@import "src/alert"
|
19
|
-
@import "src/dropdown"
|
20
|
-
@import "src/multi-select"
|
21
|
-
@import "src/nested-many"
|
22
|
-
@import "src/checkable"
|
23
|
-
@import "src/sorting"
|
24
|
-
@import "src/orderable"
|
25
|
-
@import "src/pagination"
|
26
|
-
@import "src/tree"
|
27
|
-
@import "src/navigation"
|
28
|
-
@import "src/breadcrumb"
|
29
|
-
@import "src/navtabs"
|
30
|
-
@import "src/statcard"
|
31
|
-
@import "src/page-loading"
|
32
|
-
@import "src/responsive"
|
33
|
-
@import "src/well"
|
@@ -1,17 +0,0 @@
|
|
1
|
-
//
|
2
|
-
// Alert
|
3
|
-
// --------------------------------------------------
|
4
|
-
|
5
|
-
//== Main style
|
6
|
-
//
|
7
|
-
//##
|
8
|
-
|
9
|
-
.page-content
|
10
|
-
.alert
|
11
|
-
+first-child()
|
12
|
-
margin-right: -15px
|
13
|
-
margin-left: -15px
|
14
|
-
margin-bottom: 0
|
15
|
-
border-radius: 0
|
16
|
-
&.no-results
|
17
|
-
margin: 15px 0 0 0
|
@@ -1,54 +0,0 @@
|
|
1
|
-
//
|
2
|
-
// Base
|
3
|
-
// --------------------------------------------------
|
4
|
-
|
5
|
-
//== Layout
|
6
|
-
//
|
7
|
-
//##
|
8
|
-
|
9
|
-
html *
|
10
|
-
outline: 0!important
|
11
|
-
|
12
|
-
body, html
|
13
|
-
min-height: 100%
|
14
|
-
position: relative
|
15
|
-
-webkit-font-smoothing: antialiased
|
16
|
-
-moz-osx-font-smoothing: grayscale
|
17
|
-
|
18
|
-
.body-full-height
|
19
|
-
height: 100%
|
20
|
-
body
|
21
|
-
height: 100%
|
22
|
-
|
23
|
-
body
|
24
|
-
padding: $navbar-height 0 0 0
|
25
|
-
background: image_url('para/admin/tangram-mosaic.png') repeat top left $gray-light
|
26
|
-
background-size: 128px 128px
|
27
|
-
|
28
|
-
//== Page content
|
29
|
-
//
|
30
|
-
//##
|
31
|
-
|
32
|
-
.page-title
|
33
|
-
background-color: #fff
|
34
|
-
padding: 15px 25px 15px 25px
|
35
|
-
z-index: $zindex-navbar-fixed + 1
|
36
|
-
+material-box-shadow-soft(2)
|
37
|
-
h1
|
38
|
-
margin: 0
|
39
|
-
font-weight: 300
|
40
|
-
float: left
|
41
|
-
line-height: 34px
|
42
|
-
|
43
|
-
.admin-main-page-content
|
44
|
-
position: relative
|
45
|
-
|
46
|
-
.page-content-wrap
|
47
|
-
margin: 25px 10px 25px 10px
|
48
|
-
background-color: #fff
|
49
|
-
border-radius: $border-radius-small
|
50
|
-
+material-box-shadow-soft
|
51
|
-
padding: 25px
|
52
|
-
|
53
|
-
b, strong
|
54
|
-
font-weight: 500
|