card-mod-script 0.13.1 → 0.13.2
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.
- checksums.yaml +4 -4
- data/assets/script/decko/autosave.js.coffee +30 -0
- data/assets/script/decko/bridge.js.coffee +31 -0
- data/assets/script/decko/card_menu.js.coffee +26 -0
- data/assets/script/decko/components.js.coffee +46 -0
- data/assets/script/decko/decko.js.coffee +97 -0
- data/assets/script/decko/doubleclick.js.coffee +30 -0
- data/assets/script/decko/editor.js.coffee +55 -0
- data/assets/script/decko/filter.js.coffee +176 -0
- data/assets/script/decko/filter_items.js.coffee +128 -0
- data/assets/script/decko/filter_links.js.coffee +81 -0
- data/assets/script/decko/follow.js.coffee +22 -0
- data/assets/script/decko/layout.js.coffee +76 -0
- data/assets/script/decko/link_editor.js.coffee +61 -0
- data/assets/script/decko/mod.js.coffee +85 -0
- data/assets/script/decko/modal.js.coffee +113 -0
- data/assets/script/decko/name_editor.js.coffee +58 -0
- data/assets/script/decko/navbox.js.coffee +74 -0
- data/assets/script/decko/nest_editor.js.coffee +166 -0
- data/assets/script/decko/nest_editor_name.js.coffee +102 -0
- data/assets/script/decko/nest_editor_options.js.coffee +93 -0
- data/assets/script/decko/nest_editor_rules.js.coffee +3 -0
- data/assets/script/decko/overlay.js.coffee +57 -0
- data/assets/script/decko/recaptcha.js.coffee +19 -0
- data/assets/script/decko/selectable_filtered_content.js.coffee +12 -0
- data/assets/script/decko/slot.js.coffee +182 -0
- data/assets/script/decko/slot_ready.js.coffee +11 -0
- data/assets/script/decko/slotter.js.coffee +276 -0
- data/assets/script/decko/upload.js.coffee +57 -0
- data/assets/script/jquery-ui.js +10 -0
- data/assets/script/jquery.autosize.js +274 -0
- data/assets/script/manifest.yml +44 -0
- data/assets/script/script_pointer_config.js.coffee +80 -0
- data/assets/script/script_pointer_list_editor.js.coffee +67 -0
- metadata +42 -9
@@ -0,0 +1,93 @@
|
|
1
|
+
$(document).ready ->
|
2
|
+
$('body').on 'keyup', 'input._nest-option-value', () ->
|
3
|
+
nest.updatePreview()
|
4
|
+
|
5
|
+
$('body').on "select2:select", "._nest-option-name", () ->
|
6
|
+
nest.toggleOptionName($(this).closest("._options-select"), $(this).val(), true)
|
7
|
+
nest.updatePreview()
|
8
|
+
|
9
|
+
$('body').on "select2:selecting", "._nest-option-name", () ->
|
10
|
+
nest.toggleOptionName($(this).closest("._options-select"), $(this).val(), false)
|
11
|
+
|
12
|
+
$('body').on "select2:select", "._nest-option-name._new-row", () ->
|
13
|
+
$(this).closest(".input-group").find(".input-group-prepend").removeClass("d-none")
|
14
|
+
row = $(this).closest("._nest-option-row")
|
15
|
+
row.find("._nest-option-value").removeAttr("disabled")
|
16
|
+
template = row.parent().find("._nest-option-row._template")
|
17
|
+
$(this).removeClass("_new-row")
|
18
|
+
nest.addRow(template)
|
19
|
+
|
20
|
+
$('body').on "click", "._configure-items-button", () ->
|
21
|
+
nest.addItemsOptions($(this))
|
22
|
+
|
23
|
+
$('body').on 'click', 'button._nest-delete-option', () ->
|
24
|
+
nest.removeRow $(this).closest("._nest-option-row")
|
25
|
+
|
26
|
+
$.extend nest,
|
27
|
+
showTemplate: (elem) ->
|
28
|
+
elem.removeClass("_template") #.removeClass("_#{name}-template").addClass("_#{name}")
|
29
|
+
|
30
|
+
addRow: (template) ->
|
31
|
+
select_tag = template.find("select")
|
32
|
+
select_tag.select2("destroy")
|
33
|
+
select_tag.removeAttr("data-select2-id")
|
34
|
+
double = template.clone()
|
35
|
+
#double = template.cloneSelect2(true, true)
|
36
|
+
decko.initSelect2(select_tag)
|
37
|
+
nest.showTemplate template
|
38
|
+
template.after(double)
|
39
|
+
decko.initSelect2(double.find("select"))
|
40
|
+
|
41
|
+
removeRow: (row) ->
|
42
|
+
name = row.find("._nest-option-name").val()
|
43
|
+
nest.toggleOptionName(row.closest("._options-select"), name,false)
|
44
|
+
row.remove()
|
45
|
+
nest.updatePreview()
|
46
|
+
|
47
|
+
addItemsOptions: (button) ->
|
48
|
+
container = button.closest("._configure-items")
|
49
|
+
next = container.cloneSelect2(true)
|
50
|
+
title = button.text()
|
51
|
+
button.replaceWith($("<h6>#{title.substr(9)}<h6>"))
|
52
|
+
nest.showTemplate container.find("._options-select._template")
|
53
|
+
next.find("._configure-items-button").text(title.replace("items", "subitems"))
|
54
|
+
container.after(next)
|
55
|
+
nest.updatePreview()
|
56
|
+
|
57
|
+
options: () ->
|
58
|
+
options = []
|
59
|
+
for ele in $("._options-select:not(._template")
|
60
|
+
options.push nest.extractOptions($(ele))
|
61
|
+
|
62
|
+
level_options = options.map (opts) ->
|
63
|
+
nest.toNestSyntax(opts)
|
64
|
+
level_options.join "|"
|
65
|
+
|
66
|
+
# extract options for one item level
|
67
|
+
extractOptions: (ele) ->
|
68
|
+
options = {}
|
69
|
+
nest.addOption(options, $(row)) for row in ele.find("._nest-option-row:not(.template)")
|
70
|
+
options
|
71
|
+
|
72
|
+
addOption: (options, row) ->
|
73
|
+
val = row.find("._nest-option-value").val()
|
74
|
+
return unless val? && val.length > 0
|
75
|
+
|
76
|
+
name = row.find("._nest-option-name").val()
|
77
|
+
if options[name]?
|
78
|
+
options[name].push val
|
79
|
+
else
|
80
|
+
options[name] = [val]
|
81
|
+
|
82
|
+
toggleOptionName: (container, name, active) ->
|
83
|
+
return true if name == "show" || name == "hide"
|
84
|
+
for sel in container.find("._nest-option-name")
|
85
|
+
if $(sel).val() != name
|
86
|
+
$(sel).find("option[value=#{name}]").attr "disabled", active
|
87
|
+
# $(sel).find("option[value=#{val}]").removeAttr "disabled"
|
88
|
+
decko.initSelect2($(sel))
|
89
|
+
|
90
|
+
toNestSyntax: (opts) ->
|
91
|
+
str = []
|
92
|
+
str.push "#{name}: #{values.join ', '}" for name, values of opts
|
93
|
+
str.join "; "
|
@@ -0,0 +1,57 @@
|
|
1
|
+
jQuery.fn.extend
|
2
|
+
overlaySlot: ->
|
3
|
+
oslot = @closest(".card-slot._overlay")
|
4
|
+
return oslot if oslot[0]?
|
5
|
+
oslot = @closest(".overlay-container").find("._overlay")
|
6
|
+
oslot[0]? && $(oslot[0])
|
7
|
+
|
8
|
+
addOverlay: (overlay, $slotter) ->
|
9
|
+
if @parent().hasClass("overlay-container")
|
10
|
+
if $(overlay).hasClass("_stack-overlay")
|
11
|
+
@before overlay
|
12
|
+
else
|
13
|
+
$("._overlay-origin").removeClass("_overlay-origin")
|
14
|
+
@replaceOverlay(overlay)
|
15
|
+
else
|
16
|
+
#@find(".tinymce-textarea").each ->
|
17
|
+
# tinymce.remove("##{$(this).attr("id")}")
|
18
|
+
# #tinyMCE.execCommand('mceRemoveControl', false, $(this).attr("id"))
|
19
|
+
if @parent().hasClass("_overlay-container-placeholder")
|
20
|
+
@parent().addClass("overlay-container")
|
21
|
+
else
|
22
|
+
@wrapAll('<div class="overlay-container">')
|
23
|
+
@addClass("_bottomlay-slot")
|
24
|
+
@before overlay
|
25
|
+
|
26
|
+
$slotter.registerAsOrigin("overlay", overlay)
|
27
|
+
decko.contentLoaded(overlay, $slotter)
|
28
|
+
|
29
|
+
replaceOverlay: (overlay) ->
|
30
|
+
@overlaySlot().trigger "slotDestroy"
|
31
|
+
@overlaySlot().replaceWith overlay
|
32
|
+
$(".bridge-sidebar .tab-pane:not(.active) .bridge-pills > .nav-item > .nav-link.active").removeClass("active")
|
33
|
+
|
34
|
+
isInOverlay: ->
|
35
|
+
return @closest(".card-slot._overlay").length
|
36
|
+
|
37
|
+
removeOverlay: () ->
|
38
|
+
slot = @overlaySlot()
|
39
|
+
if slot
|
40
|
+
slot.removeOverlaySlot()
|
41
|
+
|
42
|
+
removeOverlaySlot: () ->
|
43
|
+
@trigger "slotDestroy"
|
44
|
+
if @siblings().length == 1
|
45
|
+
bottomlay = $(@siblings()[0])
|
46
|
+
if bottomlay.hasClass("_bottomlay-slot")
|
47
|
+
if bottomlay.parent().hasClass("_overlay-container-placeholder")
|
48
|
+
bottomlay.parent().removeClass("overlay-container")
|
49
|
+
else
|
50
|
+
bottomlay.unwrap()
|
51
|
+
bottomlay.removeClass("_bottomlay-slot").updateBridge(true, bottomlay)
|
52
|
+
|
53
|
+
#bottomlay.find(".tinymce-textarea").each ->
|
54
|
+
# tinymce.EditorManager.execCommand('mceAddControl',true, editor_id);
|
55
|
+
# decko.initTinyMCE($(this).attr("id"))
|
56
|
+
|
57
|
+
@remove()
|
@@ -0,0 +1,19 @@
|
|
1
|
+
jQuery.fn.extend {
|
2
|
+
updateRecaptchaToken: (event) ->
|
3
|
+
recaptcha = @find("input._recaptcha-token")
|
4
|
+
|
5
|
+
if !recaptcha[0]?
|
6
|
+
recaptcha.val "recaptcha-token-field-missing"
|
7
|
+
else if !grecaptcha?
|
8
|
+
recaptcha.val("grecaptcha-undefined")
|
9
|
+
else
|
10
|
+
$slotter = $(this)
|
11
|
+
event.stopPropagation() if event
|
12
|
+
grecaptcha.execute(recaptcha.data("site-key"), action: recaptcha.data("action"))
|
13
|
+
.then (token) ->
|
14
|
+
recaptcha.val(token)
|
15
|
+
recaptcha.addClass("_token-updated")
|
16
|
+
if event
|
17
|
+
$slotter.submit()
|
18
|
+
false
|
19
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$(window).ready ->
|
2
|
+
|
3
|
+
# TODO: generalize so this works with item views other than bar
|
4
|
+
$("body").on "click", "._selectable-filtered-content .bar-body", (e) ->
|
5
|
+
item = $(this)
|
6
|
+
name = item.slot().data("card-name")
|
7
|
+
container = item.closest("._selectable-filtered-content")
|
8
|
+
input = $(container.data("input-selector"))
|
9
|
+
input.val name
|
10
|
+
item.closest('.modal').modal('hide')
|
11
|
+
e.preventDefault()
|
12
|
+
e.stopPropagation()
|
@@ -0,0 +1,182 @@
|
|
1
|
+
$.extend decko,
|
2
|
+
snakeCase: (str)->
|
3
|
+
str.replace /([a-z])([A-Z])/g, (match) -> match[0] + '_' +
|
4
|
+
match[1].toLowerCase()
|
5
|
+
|
6
|
+
# returns full path with slot parameters
|
7
|
+
slotPath: (path, slot)->
|
8
|
+
params = decko.slotData(slot)
|
9
|
+
decko.path(path) + ( (if path.match /\?/ then '&' else '?') + $.param(params) )
|
10
|
+
|
11
|
+
slotData: (slot) ->
|
12
|
+
xtra = {}
|
13
|
+
main = $('#main').children('.card-slot').data 'cardName'
|
14
|
+
xtra['main'] = main if main?
|
15
|
+
|
16
|
+
if slot
|
17
|
+
xtra['is_main'] = true if slot.isMain()
|
18
|
+
slotdata = slot.data 'slot'
|
19
|
+
if slotdata?
|
20
|
+
decko.slotParams slotdata, xtra, 'slot'
|
21
|
+
xtra['type'] = slotdata['type'] if slotdata['type']
|
22
|
+
xtra
|
23
|
+
|
24
|
+
slotEditView: (slot) ->
|
25
|
+
data = decko.slotData(slot)
|
26
|
+
switch data["slot[edit]"]
|
27
|
+
when "inline" then "edit_inline"
|
28
|
+
when "full" then "bridge"
|
29
|
+
else "edit"
|
30
|
+
|
31
|
+
slotEditLink: (slot) ->
|
32
|
+
edit_links =
|
33
|
+
slot.find(".edit-link").filter (i, el) ->
|
34
|
+
$(el).slot().data('slotId') == slot.data('slotId')
|
35
|
+
|
36
|
+
if edit_links[0] then $(edit_links[0]) else false
|
37
|
+
|
38
|
+
slotParams: (raw, processed, prefix)->
|
39
|
+
$.each raw, (key, value)->
|
40
|
+
cgiKey = prefix + '[' + decko.snakeCase(key) + ']'
|
41
|
+
if key == 'items'
|
42
|
+
decko.slotParams value, processed, cgiKey
|
43
|
+
else
|
44
|
+
processed[cgiKey] = value
|
45
|
+
|
46
|
+
contentLoaded: (el, slotter)->
|
47
|
+
decko.initializeEditors(el)
|
48
|
+
notice = slotter.attr('notify-success')
|
49
|
+
if notice?
|
50
|
+
el.notify notice, "success"
|
51
|
+
el.triggerSlotReady(slotter)
|
52
|
+
|
53
|
+
slotReady: (func)->
|
54
|
+
$('document').ready ->
|
55
|
+
$('body').on 'slotReady', '.card-slot', (e, slotter) ->
|
56
|
+
e.stopPropagation()
|
57
|
+
if slotter?
|
58
|
+
func.call this, $(this), $(slotter)
|
59
|
+
else
|
60
|
+
func.call this, $(this)
|
61
|
+
|
62
|
+
slotDestroy: (func)->
|
63
|
+
$('document').ready ->
|
64
|
+
$('body').on 'slotDestroy', '.card-slot, ._modal-slot', (e) ->
|
65
|
+
e.stopPropagation()
|
66
|
+
func.call this, $(this)
|
67
|
+
|
68
|
+
jQuery.fn.extend
|
69
|
+
slot: (status="success", mode="replace") ->
|
70
|
+
if mode == "modal"
|
71
|
+
@modalSlot()
|
72
|
+
else
|
73
|
+
@selectSlot("slot-#{status}-selector") ||
|
74
|
+
@selectSlot("slot-selector") ||
|
75
|
+
@closest(".card-slot")
|
76
|
+
|
77
|
+
selectSlot: (selectorName) ->
|
78
|
+
if selector = @data(selectorName)
|
79
|
+
slot = @findSlot selector
|
80
|
+
slot && slot[0] && slot
|
81
|
+
|
82
|
+
isSlot: ->
|
83
|
+
$(this).hasClass "card-slot"
|
84
|
+
|
85
|
+
isMain: ->
|
86
|
+
@slot().parent('#main')[0]
|
87
|
+
|
88
|
+
isMainOrMainModal: ->
|
89
|
+
el = $(this)
|
90
|
+
el = el.findOriginSlot("modal") if el.closest(".modal")[0]
|
91
|
+
el.isMain()
|
92
|
+
|
93
|
+
findSlot: (selector) ->
|
94
|
+
if selector == "modal-origin"
|
95
|
+
@findOriginSlot("modal")
|
96
|
+
else if selector == "overlay-origin"
|
97
|
+
@findOriginSlot("overlay")
|
98
|
+
else
|
99
|
+
target_slot = @closest(selector)
|
100
|
+
parent_slot = @closest '.card-slot'
|
101
|
+
|
102
|
+
# if slot-selector doesn't apply to a child, search in all parent slots and finally in the body
|
103
|
+
while target_slot.length == 0 and parent_slot.length > 0
|
104
|
+
target_slot = $(parent_slot).find(selector)
|
105
|
+
parent_slot = $(parent_slot).parent().closest '.card-slot'
|
106
|
+
if target_slot.length == 0
|
107
|
+
$(selector)
|
108
|
+
else
|
109
|
+
target_slot
|
110
|
+
|
111
|
+
# type can be "modal" or "overlay"
|
112
|
+
findOriginSlot: (type) ->
|
113
|
+
overlaySlot = @closest("[data-#{type}-origin-slot-id]")
|
114
|
+
origin_slot_id = overlaySlot.data("#{type}-origin-slot-id")
|
115
|
+
origin_slot = $("[data-slot-id=#{origin_slot_id}]")
|
116
|
+
if origin_slot[0]?
|
117
|
+
origin_slot
|
118
|
+
else
|
119
|
+
console.log "couldn't find origin with slot id #{origin_slot_id}"
|
120
|
+
|
121
|
+
reloadSlot: (url) ->
|
122
|
+
$slot = $(this)
|
123
|
+
if $slot.length > 1
|
124
|
+
$slot.each ->
|
125
|
+
$(this).reloadSlot url
|
126
|
+
return
|
127
|
+
|
128
|
+
$slot = $slot.slot() unless $slot.isSlot
|
129
|
+
return unless $slot[0]
|
130
|
+
|
131
|
+
unless url?
|
132
|
+
url = $slot.slotUrl()
|
133
|
+
$slot.addClass 'slotter'
|
134
|
+
$slot.attr 'href', url
|
135
|
+
$slot.data "url", url
|
136
|
+
this[0].href = url # that's where handleRemote gets the url from
|
137
|
+
# .attr(href, url) only works for anchors
|
138
|
+
$slot.data "remote", true
|
139
|
+
$.rails.handleRemote($slot)
|
140
|
+
|
141
|
+
clearSlot: () ->
|
142
|
+
@triggerSlotDestroy()
|
143
|
+
@empty()
|
144
|
+
|
145
|
+
slotUrl: ->
|
146
|
+
decko.slotPath "#{this.slotMark()}?view=#{@data("slot")["view"]}"
|
147
|
+
|
148
|
+
slotMark: ->
|
149
|
+
if @data('cardId') then "~#{@data('cardId')}" else @data("cardName")
|
150
|
+
|
151
|
+
setSlotContent: (val, mode, $slotter) ->
|
152
|
+
v = $(val)[0] && $(val) || val
|
153
|
+
|
154
|
+
if typeof(v) == "string"
|
155
|
+
# Needed to support "TEXT: result" pattern in success (eg deleting nested cards)
|
156
|
+
@slot("success", mode).replaceWith v
|
157
|
+
else
|
158
|
+
if v.hasClass("_overlay")
|
159
|
+
mode = "overlay"
|
160
|
+
else if v.hasClass("_modal")
|
161
|
+
mode = "modal"
|
162
|
+
@slot("success", mode).setSlotContentFromElement v, mode, $slotter
|
163
|
+
v
|
164
|
+
|
165
|
+
setSlotContentFromElement: (el, mode, $slotter) ->
|
166
|
+
if mode == "overlay"
|
167
|
+
@addOverlay(el, $slotter)
|
168
|
+
else if el.hasClass("_modal-slot") or mode == "modal"
|
169
|
+
el.showAsModal($slotter)
|
170
|
+
else
|
171
|
+
slot_id = @data("slot-id")
|
172
|
+
el.attr("data-slot-id", slot_id) if slot_id
|
173
|
+
@triggerSlotDestroy()
|
174
|
+
@replaceWith el
|
175
|
+
decko.contentLoaded(el, $slotter)
|
176
|
+
|
177
|
+
triggerSlotReady: (slotter) ->
|
178
|
+
@trigger "slotReady", slotter if @isSlot()
|
179
|
+
@find(".card-slot").trigger "slotReady", slotter
|
180
|
+
|
181
|
+
triggerSlotDestroy: () ->
|
182
|
+
@trigger "slotDestroy"
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# There are three places that can control what happens after an ajax request
|
2
|
+
# 1. the element that triggered the request (eg. a link or a button)
|
3
|
+
# 2. the closest ".slotter" element
|
4
|
+
# (if the trigger itself isn't a slotter,
|
5
|
+
# a common example is that a form is a slotter but the form buttons aren't)
|
6
|
+
# 3. the slot returned by the request
|
7
|
+
#
|
8
|
+
# A slot is an element marked with "card-slot" class, a slotter has a "slotter" class.
|
9
|
+
# By the default, the closest slot of the slotter is replaced with the new slot that the
|
10
|
+
# request returned. The behavior can be changed with css classes and data attributes.
|
11
|
+
#
|
12
|
+
# To 1. The trigger element has only a few options to override the slotter.
|
13
|
+
# classes:
|
14
|
+
# "_close-modal-on-success"
|
15
|
+
# "_close-overlay-on-success"
|
16
|
+
# "_update-origin"
|
17
|
+
#
|
18
|
+
# To 2. The slotter is the standard way to define what happens with request result
|
19
|
+
# data:
|
20
|
+
# slot-selector
|
21
|
+
# a css selector that defines which slot will be replaced.
|
22
|
+
# You can also use "modal-origin" and "overlay-origin" to refer to the origin slots.
|
23
|
+
# slot-success-selector/slot-error-selector
|
24
|
+
# the same as slot-selector but only used
|
25
|
+
# for success case or error case, respectively
|
26
|
+
# update-foreign-slot
|
27
|
+
# a css selector to specify an additional slot that will be
|
28
|
+
# updated after the request.
|
29
|
+
# update-foreign-slot-url
|
30
|
+
# a url to fetch the new content for the additional slot
|
31
|
+
# if not given the slot is updated with the same card and view that was used before
|
32
|
+
# update-origin
|
33
|
+
# if present then the slot from where the current modal or overlay slot was opened
|
34
|
+
# will be updated
|
35
|
+
# slotter-mode
|
36
|
+
# possible values are
|
37
|
+
# replace (default)
|
38
|
+
# replace the closest slot with new slot
|
39
|
+
# modal
|
40
|
+
# show new slot in modal; if there is already a modal then put it on top
|
41
|
+
# modal-replace
|
42
|
+
# replace existing modal
|
43
|
+
# overlay
|
44
|
+
# show new slot in overlay
|
45
|
+
# update-origin
|
46
|
+
# update closest slot of the slotter that opened the modal or overlay
|
47
|
+
# (assumes that the request was triggered from a modal or overlay)
|
48
|
+
# If you need the update-origin mode together with another mode then use
|
49
|
+
# data-update-origin="true".
|
50
|
+
# silent-success
|
51
|
+
# do nothing
|
52
|
+
#
|
53
|
+
# classes:
|
54
|
+
# _close-overlay
|
55
|
+
# _close-modal
|
56
|
+
#
|
57
|
+
# To 3. Similar as 1, the slot has only overlay and modal options.
|
58
|
+
# classes:
|
59
|
+
# _modal
|
60
|
+
# show slot in modal
|
61
|
+
# _overlay
|
62
|
+
# show slot in overlay
|
63
|
+
#
|
64
|
+
#
|
65
|
+
$(window).ready ->
|
66
|
+
$('body').on 'ajax:success', '.slotter', (event, data, c, d) ->
|
67
|
+
$(this).slotterSuccess event, data
|
68
|
+
|
69
|
+
$('body').on 'ajax:error', '.slotter', (event, xhr) ->
|
70
|
+
$(this).showErrorResponse xhr.status, xhr.responseText
|
71
|
+
|
72
|
+
$('body').on 'click', 'button.slotter', (event)->
|
73
|
+
return false if !$.rails.allowAction $(this)
|
74
|
+
$.rails.handleRemote $(this)
|
75
|
+
|
76
|
+
$('body').on 'click', '._clickable.slotter', (event)->
|
77
|
+
$(this)[0].href = $(this).attr("href") # that's where rails.handleRemote
|
78
|
+
# expects the url
|
79
|
+
$.rails.handleRemote $(this)
|
80
|
+
|
81
|
+
$('body').on 'click', '[data-dismiss="overlay"]', (event) ->
|
82
|
+
$(this).findSlot(".card-slot._overlay").removeOverlay()
|
83
|
+
|
84
|
+
$('body').on 'click', '._close-overlay-on-success', (event) ->
|
85
|
+
$(this).closeOnSuccess("overlay")
|
86
|
+
|
87
|
+
$('body').on 'click', '._close-modal-on-success', (event) ->
|
88
|
+
$(this).closeOnSuccess("modal")
|
89
|
+
|
90
|
+
$('body').on 'click', '._close-on-success', (event) ->
|
91
|
+
$(this).closeOnSuccess()
|
92
|
+
|
93
|
+
$('body').on 'click', '._update-origin', (event) ->
|
94
|
+
$(this).closest('.slotter').data("slotter-mode", "update-origin")
|
95
|
+
|
96
|
+
$('body').on 'submit', 'form.slotter', (event)->
|
97
|
+
form = $(this)
|
98
|
+
if form.data("main-success") and form.isMainOrMainModal()
|
99
|
+
form.mainSuccess()
|
100
|
+
if form.data('recaptcha') == 'on'
|
101
|
+
return form.handleRecaptchaBeforeSubmit(event)
|
102
|
+
|
103
|
+
$('body').on 'ajax:beforeSend', '.slotter', (event, xhr, opt)->
|
104
|
+
$(this).slotterBeforeSend opt
|
105
|
+
|
106
|
+
jQuery.fn.extend
|
107
|
+
mainSuccess: ()->
|
108
|
+
form = $(this)
|
109
|
+
$.each form.data("main-success"), (key, value)->
|
110
|
+
inputSelector = "[name=success\\[" + key + "\\]]"
|
111
|
+
input = form.find inputSelector
|
112
|
+
unless input[0]
|
113
|
+
input = $('<input type="hidden" name="success[' + key + ']"/>')
|
114
|
+
form.append input
|
115
|
+
input.val value
|
116
|
+
|
117
|
+
slotterSuccess: (event, data) ->
|
118
|
+
unless @hasClass("slotter")
|
119
|
+
console.log "warning: slotterSuccess called on non-slotter element #{this}"
|
120
|
+
return
|
121
|
+
|
122
|
+
return if event.slotSuccessful
|
123
|
+
|
124
|
+
if @data("reload")
|
125
|
+
window.location.reload(true)
|
126
|
+
|
127
|
+
if @data("update-modal-origin")
|
128
|
+
@updateModalOrigin()
|
129
|
+
|
130
|
+
if @data("update-origin")
|
131
|
+
@updateOrigin()
|
132
|
+
|
133
|
+
if @data('original-slotter-mode')
|
134
|
+
@attr 'data-slotter-mode', @data('original-slotter-mode')
|
135
|
+
|
136
|
+
mode = @data("slotter-mode")
|
137
|
+
@showSuccessResponse data, mode
|
138
|
+
|
139
|
+
if @hasClass "_close-overlay"
|
140
|
+
@removeOverlay()
|
141
|
+
if @hasClass "_close-modal"
|
142
|
+
@closest('.modal').modal('hide')
|
143
|
+
|
144
|
+
# should scroll to top after clicking on new page
|
145
|
+
if @hasClass "card-paging-link"
|
146
|
+
slot_top_pos = @slot().offset().top
|
147
|
+
$("body").scrollTop slot_top_pos
|
148
|
+
if @data("update-foreign-slot")
|
149
|
+
$slot = @findSlot @data("update-foreign-slot")
|
150
|
+
reload_url = @data("update-foreign-slot-url")
|
151
|
+
$slot.reloadSlot reload_url
|
152
|
+
|
153
|
+
event.slotSuccessful = true
|
154
|
+
|
155
|
+
showSuccessResponse: (data, mode) ->
|
156
|
+
if mode == "silent-success"
|
157
|
+
return
|
158
|
+
else if mode == "update-modal-origin"
|
159
|
+
@updateModalOrigin()
|
160
|
+
else if mode == "update-origin"
|
161
|
+
@updateOrigin()
|
162
|
+
else if data.redirect
|
163
|
+
window.location = data.redirect
|
164
|
+
else if data.reload
|
165
|
+
window.location.reload(true)
|
166
|
+
else
|
167
|
+
@updateSlot data, mode
|
168
|
+
|
169
|
+
showErrorResponse: (status, result) ->
|
170
|
+
if status == 403 #permission denied
|
171
|
+
$(result).showAsModal $(this)
|
172
|
+
else if status == 900
|
173
|
+
$(result).showAsModal $(this)
|
174
|
+
else
|
175
|
+
@notify result, "error"
|
176
|
+
|
177
|
+
if status == 409 #edit conflict
|
178
|
+
@slot().find('.current_revision_id').val(
|
179
|
+
@slot().find('.new-current-revision-id').text()
|
180
|
+
)
|
181
|
+
|
182
|
+
updateModalOrigin: () ->
|
183
|
+
if @overlaySlot()
|
184
|
+
overlayOrigin = @findOriginSlot("overlay")
|
185
|
+
overlayOrigin.updateOrigin()
|
186
|
+
else if @closest("#modal-container")[0]
|
187
|
+
@updateOrigin()
|
188
|
+
|
189
|
+
updateOrigin: () ->
|
190
|
+
type = if @overlaySlot()
|
191
|
+
"overlay"
|
192
|
+
else if @closest("#modal-container")[0]
|
193
|
+
"modal"
|
194
|
+
|
195
|
+
return unless type?
|
196
|
+
|
197
|
+
origin = @findOriginSlot(type)
|
198
|
+
if origin && origin[0]?
|
199
|
+
origin.reloadSlot()
|
200
|
+
|
201
|
+
registerAsOrigin: (type, slot) ->
|
202
|
+
if slot.hasClass("_modal-slot")
|
203
|
+
slot = slot.find(".modal-body .card-slot")
|
204
|
+
slot.attr("data-#{type}-origin-slot-id", @closest(".card-slot").data("slot-id"))
|
205
|
+
|
206
|
+
updateSlot: (data, mode) ->
|
207
|
+
mode ||= "replace"
|
208
|
+
@setSlotContent data, mode, $(this)
|
209
|
+
|
210
|
+
# close modal or overlay
|
211
|
+
closeOnSuccess: (type) ->
|
212
|
+
slotter = @closest('.slotter')
|
213
|
+
if !type?
|
214
|
+
type = if @isInOverlay() then "overlay" else "modal"
|
215
|
+
slotter.addClass "_close-#{type}"
|
216
|
+
|
217
|
+
slotterBeforeSend: (opt) ->
|
218
|
+
return if opt.skip_before_send
|
219
|
+
|
220
|
+
# avoiding duplication. could be better test?
|
221
|
+
unless (opt.url.match(/home_view/) or @data("slotter-mode") == "modal")
|
222
|
+
opt.url = decko.slotPath opt.url, @slot()
|
223
|
+
|
224
|
+
if @is('form')
|
225
|
+
if data = @data 'file-data'
|
226
|
+
# NOTE - this entire solution is temporary.
|
227
|
+
@uploadWithBlueimp(data, opt)
|
228
|
+
false
|
229
|
+
|
230
|
+
uploadWithBlueimp: (data, opt) ->
|
231
|
+
input = @find '.file-upload'
|
232
|
+
if input[1]
|
233
|
+
@notify(
|
234
|
+
"Decko does not yet support multiple files in a single form.",
|
235
|
+
"error"
|
236
|
+
)
|
237
|
+
return false
|
238
|
+
|
239
|
+
widget = input.data 'blueimpFileupload' #jQuery UI widget
|
240
|
+
|
241
|
+
# browsers that can't do ajax uploads use iframe
|
242
|
+
unless widget._isXHRUpload(widget.options)
|
243
|
+
# can't do normal redirects.
|
244
|
+
@find('[name=success]').val('_self')
|
245
|
+
# iframe response not passed back;
|
246
|
+
# all responses treated as success. boo
|
247
|
+
opt.url += '&simulate_xhr=true'
|
248
|
+
# iframe is not xhr request,
|
249
|
+
# so would otherwise get full response with layout
|
250
|
+
iframeUploadFilter = (data)-> data.find('body').html()
|
251
|
+
opt.dataFilter = iframeUploadFilter
|
252
|
+
# gets rid of default html and body tags
|
253
|
+
|
254
|
+
args = $.extend opt, (widget._getAJAXSettings data), url: opt.url
|
255
|
+
# combines settings from decko's slotter and jQuery UI's upload widget
|
256
|
+
args.skip_before_send = true #avoid looping through this method again
|
257
|
+
|
258
|
+
$.ajax( args )
|
259
|
+
|
260
|
+
handleRecaptchaBeforeSubmit: (event) ->
|
261
|
+
recaptcha = @find("input._recaptcha-token")
|
262
|
+
|
263
|
+
if !recaptcha[0]?
|
264
|
+
# monkey error (bad form)
|
265
|
+
recaptcha.val "recaptcha-token-field-missing"
|
266
|
+
else if recaptcha.hasClass "_token-updated"
|
267
|
+
# recaptcha token is fine - continue submitting
|
268
|
+
recaptcha.removeClass "_token-updated"
|
269
|
+
else if !grecaptcha?
|
270
|
+
# shark error (probably recaptcha keys of pre v3 version)
|
271
|
+
recaptcha.val "grecaptcha-undefined"
|
272
|
+
else
|
273
|
+
@updateRecaptchaToken(event)
|
274
|
+
# this stops the submit here
|
275
|
+
# and submits again when the token is ready
|
276
|
+
|