mercury-rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +152 -0
- data/VERSION +1 -0
- data/app/assets/images/mercury/button.png +0 -0
- data/app/assets/images/mercury/clippy.png +0 -0
- data/app/assets/images/mercury/default-snippet.png +0 -0
- data/app/assets/images/mercury/loading-dark.gif +0 -0
- data/app/assets/images/mercury/loading-light.gif +0 -0
- data/app/assets/images/mercury/search-icon.png +0 -0
- data/app/assets/images/mercury/toolbar/editable/buttons.png +0 -0
- data/app/assets/images/mercury/toolbar/markupable/buttons.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/_expander.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/_pressed.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/historypanel.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/insertcharacter.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/insertlink.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/insertmedia.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/inserttable.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/inspectorpanel.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/notespanel.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/objectspanel.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/preview.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/redo.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/save.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/todospanel.png +0 -0
- data/app/assets/images/mercury/toolbar/primary/undo.png +0 -0
- data/app/assets/images/mercury/toolbar/snippetable/buttons.png +0 -0
- data/app/assets/javascripts/mercury.js +30 -0
- data/app/assets/javascripts/mercury/dialog.js.coffee +75 -0
- data/app/assets/javascripts/mercury/dialogs/backcolor.js.coffee +6 -0
- data/app/assets/javascripts/mercury/dialogs/forecolor.js.coffee +6 -0
- data/app/assets/javascripts/mercury/dialogs/formatblock.js.coffee +4 -0
- data/app/assets/javascripts/mercury/dialogs/objectspanel.js.coffee +10 -0
- data/app/assets/javascripts/mercury/dialogs/style.js.coffee +4 -0
- data/app/assets/javascripts/mercury/history_buffer.js.coffee +30 -0
- data/app/assets/javascripts/mercury/mercury.js.coffee +293 -0
- data/app/assets/javascripts/mercury/modal.js.coffee +177 -0
- data/app/assets/javascripts/mercury/modals/htmleditor.js.coffee +10 -0
- data/app/assets/javascripts/mercury/modals/insertcharacter.js.coffee +4 -0
- data/app/assets/javascripts/mercury/modals/insertlink.js.coffee +92 -0
- data/app/assets/javascripts/mercury/modals/insertmedia.js.coffee +72 -0
- data/app/assets/javascripts/mercury/modals/insertsnippet.js.coffee +11 -0
- data/app/assets/javascripts/mercury/modals/inserttable.js.coffee +56 -0
- data/app/assets/javascripts/mercury/native_extensions.js.coffee +47 -0
- data/app/assets/javascripts/mercury/page_editor.js.coffee +139 -0
- data/app/assets/javascripts/mercury/palette.js.coffee +29 -0
- data/app/assets/javascripts/mercury/panel.js.coffee +97 -0
- data/app/assets/javascripts/mercury/region.js.coffee +103 -0
- data/app/assets/javascripts/mercury/regions/editable.js.coffee +546 -0
- data/app/assets/javascripts/mercury/regions/markupable.js.coffee +380 -0
- data/app/assets/javascripts/mercury/regions/snippetable.js.coffee +127 -0
- data/app/assets/javascripts/mercury/select.js.coffee +40 -0
- data/app/assets/javascripts/mercury/snippet.js.coffee +92 -0
- data/app/assets/javascripts/mercury/snippet_toolbar.js.coffee +69 -0
- data/app/assets/javascripts/mercury/statusbar.js.coffee +25 -0
- data/app/assets/javascripts/mercury/table_editor.js.coffee +266 -0
- data/app/assets/javascripts/mercury/toolbar.button.js.coffee +152 -0
- data/app/assets/javascripts/mercury/toolbar.button_group.js.coffee +42 -0
- data/app/assets/javascripts/mercury/toolbar.expander.js.coffee +56 -0
- data/app/assets/javascripts/mercury/toolbar.js.coffee +72 -0
- data/app/assets/javascripts/mercury/tooltip.js.coffee +67 -0
- data/app/assets/javascripts/mercury/uploader.js.coffee +213 -0
- data/app/assets/javascripts/mercury/websocket.js.coffee +34 -0
- data/app/assets/stylesheets/mercury.css +31 -0
- data/app/assets/stylesheets/mercury/dialog.scss +178 -0
- data/app/assets/stylesheets/mercury/mercury.scss +119 -0
- data/app/assets/stylesheets/mercury/modal.scss +192 -0
- data/app/assets/stylesheets/mercury/statusbar.scss +23 -0
- data/app/assets/stylesheets/mercury/toolbar.scss +417 -0
- data/app/assets/stylesheets/mercury/tooltip.scss +26 -0
- data/app/assets/stylesheets/mercury/uploader.scss +109 -0
- data/app/controllers/images_controller.rb +19 -0
- data/app/controllers/mercury_controller.rb +20 -0
- data/app/models/image.rb +14 -0
- data/app/views/layouts/mercury.html.haml +12 -0
- data/app/views/mercury/modals/character.html.haml +252 -0
- data/app/views/mercury/modals/htmleditor.html.haml +8 -0
- data/app/views/mercury/modals/link.html.haml +31 -0
- data/app/views/mercury/modals/media.html.haml +33 -0
- data/app/views/mercury/modals/sanitizer.html.haml +4 -0
- data/app/views/mercury/modals/table.html.haml +49 -0
- data/app/views/mercury/palettes/backcolor.html.haml +79 -0
- data/app/views/mercury/palettes/forecolor.html.haml +79 -0
- data/app/views/mercury/panels/history.html.haml +0 -0
- data/app/views/mercury/panels/notes.html.haml +0 -0
- data/app/views/mercury/panels/snippets.html.haml +10 -0
- data/app/views/mercury/selects/formatblock.html.haml +10 -0
- data/app/views/mercury/selects/style.html.haml +4 -0
- data/app/views/mercury/snippets/example.html.haml +2 -0
- data/app/views/mercury/snippets/example_options.html.haml +16 -0
- data/config/engine.rb +6 -0
- data/config/routes.rb +15 -0
- data/db/migrate/20110526035601_create_images.rb +11 -0
- data/features/editing/basic.feature +11 -0
- data/features/step_definitions/debug_steps.rb +14 -0
- data/features/step_definitions/web_steps.rb +211 -0
- data/features/support/env.rb +46 -0
- data/features/support/paths.rb +35 -0
- data/features/support/selectors.rb +42 -0
- data/lib/mercury-rails.rb +4 -0
- data/log/.gitkeep +0 -0
- data/mercury-rails.gemspec +230 -0
- data/spec/javascripts/mercury/dialog_spec.js.coffee +258 -0
- data/spec/javascripts/mercury/history_buffer_spec.js.coffee +79 -0
- data/spec/javascripts/mercury/mercury_spec.js.coffee +52 -0
- data/spec/javascripts/mercury/native_extensions_spec.js.coffee +66 -0
- data/spec/javascripts/mercury/page_editor_spec.js.coffee +435 -0
- data/spec/javascripts/mercury/palette_spec.js.coffee +51 -0
- data/spec/javascripts/mercury/panel_spec.js.coffee +147 -0
- data/spec/javascripts/mercury/region_spec.js.coffee +261 -0
- data/spec/javascripts/mercury/regions/_editable_.js.coffee +0 -0
- data/spec/javascripts/mercury/regions/_markupable_.js.coffee +0 -0
- data/spec/javascripts/mercury/regions/snippetable_spec.js.coffee +368 -0
- data/spec/javascripts/mercury/select_spec.js.coffee +51 -0
- data/spec/javascripts/mercury/snippet_spec.js.coffee +246 -0
- data/spec/javascripts/mercury/snippet_toolbar_spec.js.coffee +186 -0
- data/spec/javascripts/mercury/statusbar_spec.js.coffee +78 -0
- data/spec/javascripts/mercury/table_editor_spec.js.coffee +192 -0
- data/spec/javascripts/mercury/toolbar.button_group_spec.js.coffee +92 -0
- data/spec/javascripts/mercury/toolbar.button_spec.js.coffee +341 -0
- data/spec/javascripts/mercury/toolbar.expander_spec.js.coffee +120 -0
- data/spec/javascripts/mercury/toolbar_spec.js.coffee +152 -0
- data/spec/javascripts/mercury/tooltip_spec.js.coffee +188 -0
- data/spec/javascripts/mercury/uploader_spec.js.coffee +512 -0
- data/spec/javascripts/responses/blank.html +1 -0
- data/spec/javascripts/spec_helper.js +513 -0
- data/spec/javascripts/templates/mercury/dialog.html +2 -0
- data/spec/javascripts/templates/mercury/page_editor.html +24 -0
- data/spec/javascripts/templates/mercury/palette.html +16 -0
- data/spec/javascripts/templates/mercury/panel.html +16 -0
- data/spec/javascripts/templates/mercury/region.html +2 -0
- data/spec/javascripts/templates/mercury/regions/snippetable.html +4 -0
- data/spec/javascripts/templates/mercury/select.html +16 -0
- data/spec/javascripts/templates/mercury/snippet.html +1 -0
- data/spec/javascripts/templates/mercury/snippet_toolbar.html +16 -0
- data/spec/javascripts/templates/mercury/statusbar.html +7 -0
- data/spec/javascripts/templates/mercury/table_editor.html +65 -0
- data/spec/javascripts/templates/mercury/toolbar.button.html +64 -0
- data/spec/javascripts/templates/mercury/toolbar.button_group.html +9 -0
- data/spec/javascripts/templates/mercury/toolbar.expander.html +18 -0
- data/spec/javascripts/templates/mercury/toolbar.html +10 -0
- data/spec/javascripts/templates/mercury/tooltip.html +12 -0
- data/spec/javascripts/templates/mercury/uploader.html +11 -0
- data/vendor/assets/javascripts/jquery-1.6.js +8865 -0
- data/vendor/assets/javascripts/jquery-ui-1.8.13.custom.min.js +249 -0
- data/vendor/assets/javascripts/jquery-ui-1.8.13.sortable.custom.js +1078 -0
- data/vendor/assets/javascripts/jquery.easing.js +173 -0
- data/vendor/assets/javascripts/jquery.json2.js +178 -0
- data/vendor/assets/javascripts/jquery.serialize_object.js +16 -0
- data/vendor/assets/javascripts/jquery.ujs.js +289 -0
- data/vendor/assets/javascripts/liquidmetal.js +88 -0
- data/vendor/assets/javascripts/showdown.js +1362 -0
- metadata +364 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
class @Mercury.Select extends Mercury.Dialog
|
2
|
+
|
3
|
+
constructor: (@url, @name, @options = {}) ->
|
4
|
+
super
|
5
|
+
|
6
|
+
|
7
|
+
build: ->
|
8
|
+
@element = $('<div>', {class: "mercury-select mercury-#{@name}-select loading", style: 'display:none'})
|
9
|
+
@element.appendTo($(@options.appendTo).get(0) ? 'body')
|
10
|
+
|
11
|
+
|
12
|
+
bindEvents: ->
|
13
|
+
Mercury.bind 'hide:dialogs', (event, dialog) => @hide() unless dialog == @
|
14
|
+
super
|
15
|
+
|
16
|
+
|
17
|
+
position: (keepVisible) ->
|
18
|
+
@element.css({top: 0, left: 0, display: 'block', visibility: 'hidden'})
|
19
|
+
position = @button.offset()
|
20
|
+
elementWidth = @element.width()
|
21
|
+
elementHeight = @element.height()
|
22
|
+
documentHeight = $(document).height()
|
23
|
+
|
24
|
+
top = position.top + (@button.height() / 2) - (elementHeight / 2)
|
25
|
+
top = position.top - 100 if top < position.top - 100
|
26
|
+
top = 20 if top < 20
|
27
|
+
|
28
|
+
height = if @loaded then 'auto' else elementHeight
|
29
|
+
height = documentHeight - top - 20 if top + elementHeight >= documentHeight - 20
|
30
|
+
|
31
|
+
left = position.left
|
32
|
+
left = left - elementWidth + @button.width() if left + elementWidth > $(window).width()
|
33
|
+
|
34
|
+
@element.css {
|
35
|
+
top: top,
|
36
|
+
left: left,
|
37
|
+
height: height,
|
38
|
+
display: if keepVisible then 'block' else 'none',
|
39
|
+
visibility: 'visible'
|
40
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class @Mercury.Snippet
|
2
|
+
|
3
|
+
@all: []
|
4
|
+
|
5
|
+
@displayOptionsFor: (name) ->
|
6
|
+
Mercury.modal("/mercury/snippets/#{name}/options", {title: 'Snippet Options', handler: 'insertsnippet'})
|
7
|
+
Mercury.snippet = null
|
8
|
+
|
9
|
+
|
10
|
+
@create: (name, options) ->
|
11
|
+
identity = "snippet_#{@all.length}"
|
12
|
+
instance = new Mercury.Snippet(name, identity, options)
|
13
|
+
@all.push(instance)
|
14
|
+
return instance
|
15
|
+
|
16
|
+
|
17
|
+
@find: (identity) ->
|
18
|
+
for snippet in @all
|
19
|
+
return snippet if snippet.identity == identity
|
20
|
+
return null
|
21
|
+
|
22
|
+
|
23
|
+
@load: (snippets) ->
|
24
|
+
for identity, details of snippets
|
25
|
+
instance = new Mercury.Snippet(details.name, identity, details.options)
|
26
|
+
@all.push(instance)
|
27
|
+
|
28
|
+
|
29
|
+
constructor: (@name, @identity, options = {}) ->
|
30
|
+
@version = 0
|
31
|
+
@data = ''
|
32
|
+
@history = new Mercury.HistoryBuffer()
|
33
|
+
@setOptions(options)
|
34
|
+
|
35
|
+
|
36
|
+
getHTML: (context, callback = null) ->
|
37
|
+
element = $('<div class="mercury-snippet" contenteditable="false">', context)
|
38
|
+
element.attr({'data-snippet': @identity})
|
39
|
+
element.attr({'data-version': @version})
|
40
|
+
element.html("[#{@identity}]")
|
41
|
+
@loadPreview(element, callback)
|
42
|
+
return element
|
43
|
+
|
44
|
+
|
45
|
+
getText: (callback) ->
|
46
|
+
return "[--#{@identity}--]"
|
47
|
+
|
48
|
+
|
49
|
+
loadPreview: (element, callback = null) ->
|
50
|
+
$.ajax "/mercury/snippets/#{@name}/preview", {
|
51
|
+
type: 'POST'
|
52
|
+
data: @options
|
53
|
+
success: (data) =>
|
54
|
+
@data = data
|
55
|
+
element.html(data)
|
56
|
+
callback() if callback
|
57
|
+
error: =>
|
58
|
+
alert("Error loading the preview for the #{@name} snippet.")
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
displayOptions: ->
|
63
|
+
Mercury.snippet = @
|
64
|
+
Mercury.modal "/mercury/snippets/#{@name}/options", {
|
65
|
+
title: 'Snippet Options',
|
66
|
+
handler: 'insertsnippet',
|
67
|
+
loadType: 'post',
|
68
|
+
loadData: @options
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
setOptions: (@options) ->
|
73
|
+
delete(@options['authenticity_token'])
|
74
|
+
delete(@options['utf8'])
|
75
|
+
@version += 1
|
76
|
+
@history.push(@options)
|
77
|
+
|
78
|
+
|
79
|
+
setVersion: (version = null) ->
|
80
|
+
version = parseInt(version)
|
81
|
+
if version && @history.stack[version - 1]
|
82
|
+
@version = version - 1
|
83
|
+
@options = @history.stack[@version]
|
84
|
+
return true
|
85
|
+
return false
|
86
|
+
|
87
|
+
|
88
|
+
serialize: ->
|
89
|
+
return {
|
90
|
+
name: @name,
|
91
|
+
options: @options
|
92
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class @Mercury.SnippetToolbar extends Mercury.Toolbar
|
2
|
+
|
3
|
+
constructor: (@document, @options = {}) ->
|
4
|
+
super(@options)
|
5
|
+
|
6
|
+
|
7
|
+
build: ->
|
8
|
+
@element = $('<div>', {class: 'mercury-toolbar mercury-snippet-toolbar', style: 'display:none'})
|
9
|
+
@element.appendTo($(@options.appendTo).get(0) ? 'body')
|
10
|
+
|
11
|
+
for buttonName, options of Mercury.config.toolbars.snippetable
|
12
|
+
button = @buildButton(buttonName, options)
|
13
|
+
button.appendTo(@element) if button
|
14
|
+
|
15
|
+
|
16
|
+
bindEvents: ->
|
17
|
+
Mercury.bind 'show:toolbar', (event, options) =>
|
18
|
+
return unless options.snippet
|
19
|
+
options.snippet.mouseout => @hide()
|
20
|
+
@show(options.snippet)
|
21
|
+
|
22
|
+
Mercury.bind 'hide:toolbar', (event, options) =>
|
23
|
+
return unless options.type && options.type == 'snippet'
|
24
|
+
@hide(options.immediately)
|
25
|
+
|
26
|
+
$(@document).scroll => @position() if @visible
|
27
|
+
|
28
|
+
@element.mousemove => clearTimeout(@hideTimeout)
|
29
|
+
@element.mouseout => @hide()
|
30
|
+
|
31
|
+
|
32
|
+
show: (@snippet) ->
|
33
|
+
Mercury.tooltip.hide()
|
34
|
+
@position()
|
35
|
+
@appear()
|
36
|
+
|
37
|
+
|
38
|
+
position: ->
|
39
|
+
offset = @snippet.offset()
|
40
|
+
|
41
|
+
top = offset.top + Mercury.displayRect.top - $(@document).scrollTop() - @height() + 10
|
42
|
+
left = offset.left - $(@document).scrollLeft()
|
43
|
+
|
44
|
+
@element.css {
|
45
|
+
top: top,
|
46
|
+
left: left
|
47
|
+
}
|
48
|
+
|
49
|
+
|
50
|
+
appear: ->
|
51
|
+
clearTimeout(@hideTimeout)
|
52
|
+
return if @visible
|
53
|
+
@visible = true
|
54
|
+
@element.css({display: 'block', opacity: 0})
|
55
|
+
@element.stop().animate({opacity: 1}, 200, 'easeInOutSine')
|
56
|
+
|
57
|
+
|
58
|
+
hide: (immediately = false) ->
|
59
|
+
clearTimeout(@hideTimeout)
|
60
|
+
if immediately
|
61
|
+
@element.hide()
|
62
|
+
@visible = false
|
63
|
+
else
|
64
|
+
@hideTimeout = setTimeout((=>
|
65
|
+
@element.stop().animate {opacity: 0}, 300, 'easeInOutSine', =>
|
66
|
+
@element.hide()
|
67
|
+
@visible = false
|
68
|
+
), 500)
|
69
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class @Mercury.Statusbar
|
2
|
+
|
3
|
+
constructor: (@options = {}) ->
|
4
|
+
@build()
|
5
|
+
@bindEvents()
|
6
|
+
|
7
|
+
|
8
|
+
build: ->
|
9
|
+
@element = $('<div>', {class: 'mercury-statusbar'}).appendTo($(@options.appendTo).get(0) ? 'body')
|
10
|
+
|
11
|
+
|
12
|
+
bindEvents: ->
|
13
|
+
Mercury.bind 'region:update', (event, options) =>
|
14
|
+
@setPath(options.region.path()) if options.region && $.type(options.region.path) == 'function'
|
15
|
+
|
16
|
+
|
17
|
+
height: ->
|
18
|
+
@element.outerHeight()
|
19
|
+
|
20
|
+
|
21
|
+
setPath: (elements) ->
|
22
|
+
path = []
|
23
|
+
path.push("<a>#{element.tagName.toLowerCase()}</a>") for element in elements
|
24
|
+
|
25
|
+
@element.html("<span><strong>Path: </strong></span>#{path.reverse().join(' » ')}")
|
@@ -0,0 +1,266 @@
|
|
1
|
+
@Mercury.tableEditor = (table, cell) ->
|
2
|
+
Mercury.tableEditor.load(table, cell)
|
3
|
+
return Mercury.tableEditor
|
4
|
+
|
5
|
+
$.extend Mercury.tableEditor, {
|
6
|
+
|
7
|
+
load: (@table, @cell) ->
|
8
|
+
@row = @cell.parent('tr')
|
9
|
+
@columnCount = @getColumnCount()
|
10
|
+
@rowCount = @getRowCount()
|
11
|
+
|
12
|
+
|
13
|
+
addColumn: (position = 'after') ->
|
14
|
+
sig = @cellSignatureFor(@cell)
|
15
|
+
|
16
|
+
for row, i in @table.find('tr')
|
17
|
+
rowSpan = 1
|
18
|
+
matchOptions = if position == 'after' then {right: sig.right} else {left: sig.left}
|
19
|
+
if matching = @findCellByOptionsFor(row, matchOptions)
|
20
|
+
newCell = $("<#{matching.cell.get(0).tagName}>").text('\00')
|
21
|
+
@setRowspanFor(newCell, matching.height)
|
22
|
+
if position == 'before' then matching.cell.before(newCell) else matching.cell.after(newCell)
|
23
|
+
i += matching.height - 1
|
24
|
+
else if intersecting = @findCellByIntersectionFor(row, sig)
|
25
|
+
@setColspanFor(intersecting.cell, intersecting.width + 1)
|
26
|
+
|
27
|
+
|
28
|
+
removeColumn: ->
|
29
|
+
sig = @cellSignatureFor(@cell)
|
30
|
+
return if sig.width > 1
|
31
|
+
|
32
|
+
removing = []
|
33
|
+
adjusting = []
|
34
|
+
for row, i in @table.find('tr')
|
35
|
+
if matching = @findCellByOptionsFor(row, {left: sig.left, width: sig.width})
|
36
|
+
removing.push(matching.cell)
|
37
|
+
i += matching.height - 1
|
38
|
+
else if intersecting = @findCellByIntersectionFor(row, sig)
|
39
|
+
adjusting.push(intersecting.cell)
|
40
|
+
|
41
|
+
$(cell).remove() for cell in removing
|
42
|
+
@setColspanFor(cell, @colspanFor(cell) - 1) for cell in adjusting
|
43
|
+
|
44
|
+
|
45
|
+
addRow: (position = 'after') ->
|
46
|
+
newRow = $('<tr>')
|
47
|
+
|
48
|
+
if (rowspan = @rowspanFor(@cell)) > 1 && position == 'after'
|
49
|
+
@row = $(@row.nextAll('tr')[rowspan - 2])
|
50
|
+
|
51
|
+
cellCount = 0
|
52
|
+
for cell in @row.find('th, td')
|
53
|
+
colspan = @colspanFor(cell)
|
54
|
+
newCell = $("<#{cell.tagName}>").text('\00')
|
55
|
+
@setColspanFor(newCell, colspan)
|
56
|
+
cellCount += colspan
|
57
|
+
if (rowspan = @rowspanFor(cell)) > 1 && position == 'after'
|
58
|
+
@setRowspanFor(cell, rowspan + 1)
|
59
|
+
continue
|
60
|
+
|
61
|
+
newRow.append(newCell)
|
62
|
+
|
63
|
+
if cellCount < @columnCount
|
64
|
+
rowCount = 0
|
65
|
+
for previousRow in @row.prevAll('tr')
|
66
|
+
rowCount += 1
|
67
|
+
for cell in $(previousRow).find('td[rowspan], th[rowspan]')
|
68
|
+
rowspan = @rowspanFor(cell)
|
69
|
+
if rowspan - 1 >= rowCount && position == 'before'
|
70
|
+
@setRowspanFor(cell, rowspan + 1)
|
71
|
+
else if rowspan - 1 >= rowCount && position == 'after'
|
72
|
+
if rowspan - 1 == rowCount
|
73
|
+
newCell = $("<#{cell.tagName}>", {colspan: @colspanFor(cell)})
|
74
|
+
newRow.append(newCell);
|
75
|
+
else
|
76
|
+
@setRowspanFor(cell, rowspan + 1)
|
77
|
+
|
78
|
+
if position == 'before' then @row.before(newRow) else @row.after(newRow)
|
79
|
+
|
80
|
+
|
81
|
+
removeRow: ->
|
82
|
+
# check to see that all cells have the same rowspan, and figure out the minimum rowspan
|
83
|
+
rowspansMatch = true
|
84
|
+
prevRowspan = 0
|
85
|
+
minRowspan = 0
|
86
|
+
for cell in @row.find('td, th')
|
87
|
+
rowspan = @rowspanFor(cell)
|
88
|
+
rowspansMatch = false if prevRowspan && rowspan != prevRowspan
|
89
|
+
minRowspan = rowspan if rowspan < minRowspan || !minRowspan
|
90
|
+
prevRowspan = rowspan
|
91
|
+
|
92
|
+
return if !rowspansMatch && @rowspanFor(@cell) > minRowspan
|
93
|
+
|
94
|
+
# remove any emtpy rows below
|
95
|
+
if minRowspan > 1
|
96
|
+
$(@row.nextAll('tr')[i]).remove() for i in [0..minRowspan - 2]
|
97
|
+
|
98
|
+
# find and move down any cells that have a larger rowspan
|
99
|
+
for cell in @row.find('td[rowspan], th[rowspan]')
|
100
|
+
sig = @cellSignatureFor(cell)
|
101
|
+
continue if sig.height == minRowspan
|
102
|
+
if match = @findCellByOptionsFor(@row.nextAll('tr')[minRowspan - 1], {left: sig.left, forceAdjacent: true})
|
103
|
+
@setRowspanFor(cell, @rowspanFor(cell) - @rowspanFor(@cell))
|
104
|
+
if match.direction == 'before' then match.cell.before($(cell).clone()) else match.cell.after($(cell).clone())
|
105
|
+
|
106
|
+
if @columnsFor(@row.find('td, th')) < @columnCount
|
107
|
+
# move up rows looking for cells with rowspans that might intersect
|
108
|
+
rowsAbove = 0
|
109
|
+
for aboveRow in @row.prevAll('tr')
|
110
|
+
rowsAbove += 1
|
111
|
+
for cell in $(aboveRow).find('td[rowspan], th[rowspan]')
|
112
|
+
# if the cell intersects with the row we're trying to calculate on, and it's index is less than where we've
|
113
|
+
# gotten so far, add it
|
114
|
+
rowspan = @rowspanFor(cell)
|
115
|
+
@setRowspanFor(cell, rowspan - @rowspanFor(@cell)) if rowspan > rowsAbove
|
116
|
+
|
117
|
+
@row.remove()
|
118
|
+
|
119
|
+
|
120
|
+
increaseColspan: ->
|
121
|
+
cell = @cell.next('td, th')
|
122
|
+
return unless cell.length
|
123
|
+
return if @rowspanFor(cell) != @rowspanFor(@cell)
|
124
|
+
return if @cellIndexFor(cell) > @cellIndexFor(@cell) + @colspanFor(@cell)
|
125
|
+
@setColspanFor(@cell, @colspanFor(@cell) + @colspanFor(cell))
|
126
|
+
cell.remove()
|
127
|
+
|
128
|
+
|
129
|
+
decreaseColspan: ->
|
130
|
+
return if @colspanFor(@cell) == 1
|
131
|
+
@setColspanFor(@cell, @colspanFor(@cell) - 1)
|
132
|
+
newCell = $("<#{@cell.get(0).tagName}>").text('\00')
|
133
|
+
@setRowspanFor(newCell, @rowspanFor(@cell))
|
134
|
+
@cell.after(newCell)
|
135
|
+
|
136
|
+
|
137
|
+
increaseRowspan: ->
|
138
|
+
sig = @cellSignatureFor(@cell)
|
139
|
+
nextRow = @row.nextAll('tr')[sig.height - 1]
|
140
|
+
if nextRow && match = @findCellByOptionsFor(nextRow, {left: sig.left, width: sig.width})
|
141
|
+
@setRowspanFor(@cell, sig.height + match.height)
|
142
|
+
match.cell.remove()
|
143
|
+
|
144
|
+
decreaseRowspan: ->
|
145
|
+
sig = @cellSignatureFor(@cell)
|
146
|
+
return if sig.height == 1
|
147
|
+
nextRow = @row.nextAll('tr')[sig.height - 2]
|
148
|
+
if match = @findCellByOptionsFor(nextRow, {left: sig.left, forceAdjacent: true})
|
149
|
+
newCell = $("<#{@cell.get(0).tagName}>").text('\00')
|
150
|
+
@setColspanFor(newCell, @colspanFor(@cell))
|
151
|
+
@setRowspanFor(@cell, sig.height - 1)
|
152
|
+
if match.direction == 'before' then match.cell.before(newCell) else match.cell.after(newCell)
|
153
|
+
|
154
|
+
# Counts the columns of the first row (alpha row) in the table. We can safely rely on the first row always being
|
155
|
+
# comprised of a full set of cells or cells with colspans.
|
156
|
+
getColumnCount: ->
|
157
|
+
return @columnsFor(@table.find('thead tr:first-child, tbody tr:first-child, tfoot tr:first-child').first().find('td, th'))
|
158
|
+
|
159
|
+
|
160
|
+
# Counts the rows of the table.
|
161
|
+
getRowCount: ->
|
162
|
+
return @table.find('tr').length
|
163
|
+
|
164
|
+
|
165
|
+
# Gets the index for a given cell, taking into account that rows above it can have cells that have rowspans.
|
166
|
+
cellIndexFor: (cell) ->
|
167
|
+
cell = $(cell)
|
168
|
+
|
169
|
+
# get the row for the cell and calculate all the columns in it
|
170
|
+
row = cell.parent('tr')
|
171
|
+
columns = @columnsFor(row.find('td, th'))
|
172
|
+
index = @columnsFor(cell.prevAll('td, th'))
|
173
|
+
|
174
|
+
# if the columns is less than expected, we need to look above for rowspans
|
175
|
+
if columns < @columnCount
|
176
|
+
# move up rows looking for cells with rowspans that might intersect
|
177
|
+
rowsAbove = 0
|
178
|
+
for aboveRow in row.prevAll('tr')
|
179
|
+
rowsAbove += 1
|
180
|
+
for aboveCell in $(aboveRow).find('td[rowspan], th[rowspan]')
|
181
|
+
# if the cell intersects with the row we're trying to calculate on, and it's index is less than where we've
|
182
|
+
# gotten so far, add it
|
183
|
+
if @rowspanFor(aboveCell) > rowsAbove && @cellIndexFor(aboveCell) <= index
|
184
|
+
index += @colspanFor(aboveCell)
|
185
|
+
|
186
|
+
return index
|
187
|
+
|
188
|
+
# Creates a signature for a given cell, which is made up if it's size, and itself.
|
189
|
+
cellSignatureFor: (cell) ->
|
190
|
+
sig = {cell: $(cell)}
|
191
|
+
sig.left = @cellIndexFor(cell)
|
192
|
+
sig.width = @colspanFor(cell)
|
193
|
+
sig.height = @rowspanFor(cell)
|
194
|
+
sig.right = sig.left + sig.width
|
195
|
+
return sig
|
196
|
+
|
197
|
+
# Find a cell based on options. Options can be:
|
198
|
+
# right
|
199
|
+
# or
|
200
|
+
# left, [width], [forceAdjacent]
|
201
|
+
# eg. findCellByOptionsFor(@row, {left: 1, width: 2, forceAdjacent: true})
|
202
|
+
findCellByOptionsFor: (row, options) ->
|
203
|
+
for cell in $(row).find('td, th')
|
204
|
+
sig = @cellSignatureFor(cell)
|
205
|
+
if typeof(options.right) != 'undefined'
|
206
|
+
return sig if sig.right == options.right
|
207
|
+
if typeof(options.left) != 'undefined'
|
208
|
+
|
209
|
+
if options.width
|
210
|
+
return sig if sig.left == options.left && sig.width == options.width
|
211
|
+
else if !options.forceAdjacent
|
212
|
+
return sig if sig.left == options.left
|
213
|
+
else if options.forceAdjacent
|
214
|
+
if sig.left > options.left
|
215
|
+
prev = $(cell).prev('td, th')
|
216
|
+
if prev.length
|
217
|
+
sig = @cellSignatureFor(prev)
|
218
|
+
sig.direction = 'after'
|
219
|
+
else
|
220
|
+
sig.direction = 'before'
|
221
|
+
return sig
|
222
|
+
|
223
|
+
if options.forceAdjacent
|
224
|
+
sig.direction = 'after'
|
225
|
+
return sig
|
226
|
+
|
227
|
+
return null
|
228
|
+
|
229
|
+
# Finds a cell that intersects with the current signature
|
230
|
+
findCellByIntersectionFor: (row, signature) ->
|
231
|
+
for cell in $(row).find('td, th')
|
232
|
+
sig = @cellSignatureFor(cell)
|
233
|
+
return sig if sig.right - signature.left >= 0 && sig.right > signature.left
|
234
|
+
return null
|
235
|
+
|
236
|
+
|
237
|
+
# Counts all the columns in a given array of columns, taking colspans into
|
238
|
+
# account.
|
239
|
+
columnsFor: (cells) ->
|
240
|
+
count = 0
|
241
|
+
count += @colspanFor(cell) for cell in cells
|
242
|
+
return count
|
243
|
+
|
244
|
+
|
245
|
+
# Tries to get the colspan of a cell, falling back to 1 if there's none
|
246
|
+
# specified.
|
247
|
+
colspanFor: (cell) ->
|
248
|
+
return parseInt($(cell).attr('colspan')) || 1
|
249
|
+
|
250
|
+
|
251
|
+
# Tries to get the rowspan of a cell, falling back to 1 if there's none
|
252
|
+
# specified.
|
253
|
+
rowspanFor: (cell) ->
|
254
|
+
return parseInt($(cell).attr('rowspan')) || 1
|
255
|
+
|
256
|
+
|
257
|
+
# Sets the colspan of a cell, removing it if it's 1.
|
258
|
+
setColspanFor: (cell, value) ->
|
259
|
+
$(cell).attr('colspan', if value > 1 then value else null)
|
260
|
+
|
261
|
+
|
262
|
+
# Sets the rowspan of a cell, removing it if it's 1
|
263
|
+
setRowspanFor: (cell, value) ->
|
264
|
+
$(cell).attr('rowspan', if value > 1 then value else null)
|
265
|
+
|
266
|
+
}
|