locomotive-aloha-rails 0.20.1.2 → 0.20.1.3
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.
- data/Rakefile +1 -1
- data/lib/aloha/rails/version.rb +2 -2
- data/vendor/assets/javascripts/aloha/css/aloha.css +3 -0
- data/vendor/assets/javascripts/aloha/css/ext-aloha.css +3 -0
- data/vendor/assets/javascripts/aloha/lib/aloha-bootstrap.js +4565 -3934
- data/vendor/assets/javascripts/aloha/lib/aloha.js +1357 -702
- data/vendor/assets/javascripts/aloha/lib/aloha/command.js +16 -13
- data/vendor/assets/javascripts/aloha/lib/aloha/core.js +23 -3
- data/vendor/assets/javascripts/aloha/lib/aloha/ecma5shims.js +23 -7
- data/vendor/assets/javascripts/aloha/lib/aloha/editable.js +57 -14
- data/vendor/assets/javascripts/aloha/lib/aloha/engine.js +9 -5
- data/vendor/assets/javascripts/aloha/lib/aloha/floatingmenu.js +288 -96
- data/vendor/assets/javascripts/aloha/lib/aloha/jquery.js +11 -1
- data/vendor/assets/javascripts/aloha/lib/aloha/markup.js +318 -40
- data/vendor/assets/javascripts/aloha/lib/aloha/repositorymanager.js +11 -10
- data/vendor/assets/javascripts/aloha/lib/aloha/selection.js +20 -1
- data/vendor/assets/javascripts/aloha/lib/aloha/sidebar.js +11 -1
- data/vendor/assets/javascripts/aloha/lib/jquery-plugin.js +10 -7
- data/vendor/assets/javascripts/aloha/lib/util/dom.js +18 -6
- data/vendor/assets/javascripts/aloha/lib/util/range.js +6 -6
- data/vendor/assets/javascripts/aloha/lib/vendor/ext-3.2.1/ext-all-debug.js +26 -2
- data/vendor/assets/javascripts/aloha/lib/vendor/jquery.store.js +39 -15
- data/vendor/assets/javascripts/aloha/plugins/common/abbr/lib/abbr-plugin.js +1 -0
- data/vendor/assets/javascripts/aloha/plugins/common/align/lib/align-plugin.js +344 -334
- data/vendor/assets/javascripts/aloha/plugins/common/block/css/block.css +65 -12
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/block-plugin.js +12 -15
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/block.js +796 -180
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/blockcontenthandler.js +54 -13
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/blockmanager.js +315 -78
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/editor.js +111 -8
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/editormanager.js +2 -0
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/jquery-ui-1.8.16.custom.min.js +198 -0
- data/vendor/assets/javascripts/aloha/plugins/common/block/lib/sidebarattributeeditor.js +7 -20
- data/vendor/assets/javascripts/aloha/plugins/common/characterpicker/lib/characterpicker-plugin.js +15 -3
- data/vendor/assets/javascripts/aloha/plugins/common/contenthandler/lib/sanitizecontenthandler.js +3 -2
- data/vendor/assets/javascripts/aloha/plugins/common/contenthandler/lib/wordcontenthandler.js +111 -5
- data/vendor/assets/javascripts/aloha/plugins/common/dom-to-xhtml/lib/dom-to-xhtml-plugin.js +29 -0
- data/vendor/assets/javascripts/aloha/plugins/common/dom-to-xhtml/lib/dom-to-xhtml.js +306 -0
- data/vendor/assets/javascripts/aloha/plugins/common/format/lib/format-plugin.js +59 -5
- data/vendor/assets/javascripts/aloha/plugins/common/format/nls/i18n.js +1 -1
- data/vendor/assets/javascripts/aloha/plugins/common/horizontalruler/lib/horizontalruler-plugin.js +18 -3
- data/vendor/assets/javascripts/aloha/plugins/common/image/img/crop-buttons.gif +0 -0
- data/vendor/assets/javascripts/aloha/plugins/common/image/lib/image-plugin.js +1629 -1601
- data/vendor/assets/javascripts/aloha/plugins/common/image/vendor/jcrop/jquery.jcrop.css +11 -0
- data/vendor/assets/javascripts/aloha/plugins/common/link/extra/linklist.js +8 -6
- data/vendor/assets/javascripts/aloha/plugins/common/link/lib/link-plugin.js +26 -10
- data/vendor/assets/javascripts/aloha/plugins/common/list/nls/de/i18n.js +5 -1
- data/vendor/assets/javascripts/aloha/plugins/common/paste/lib/paste-plugin.js +3 -4
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-cell.js +13 -12
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-plugin.js +108 -61
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-selection.js +61 -1
- data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table.js +1 -0
- data/vendor/assets/javascripts/aloha/plugins/common/table/nls/de/i18n.js +28 -1
- data/vendor/assets/javascripts/aloha/plugins/common/table/nls/i18n.js +36 -10
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/css/browser.jqgrid.css +292 -292
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/lib/browser.js +28 -5
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/lib/locale.js +2 -2
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/grid.locale.de.js +6 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/grid.locale.en.js +5 -0
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.jqGrid.js +5 -0
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.jstree.js +6 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.ui.js +6 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/ui-layout.js +6 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/cite/lib/cite-plugin.js +18 -4
- data/vendor/assets/javascripts/aloha/plugins/extra/formatlesspaste/lib/formatlesspaste-plugin.js +1 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/linkbrowser/lib/linkbrowser-plugin.js +14 -2
- data/vendor/assets/javascripts/aloha/plugins/extra/numerated-headers/demo/js/aloha-config.js +2 -2
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/lib/toc-plugin.js +382 -0
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/nls/de/i18n.js +3 -0
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/nls/i18n.js +4 -0
- metadata +15 -11
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/i18n/de.json +0 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/i18n/en.json +0 -1
- data/vendor/assets/javascripts/aloha/plugins/extra/toc/src/toc.js +0 -350
@@ -6,25 +6,71 @@
|
|
6
6
|
padding: 4px;
|
7
7
|
width: 100%;
|
8
8
|
}
|
9
|
-
.aloha-block
|
10
|
-
background: #FFFCE2;
|
9
|
+
.aloha-editable-active .aloha-block, .aloha-block.aloha-block-highlighted {
|
11
10
|
cursor: pointer;
|
12
|
-
|
11
|
+
box-shadow: 0 0 0px 3px #FFE767; /* We use box-shadow because of a Firefox rendering bug with "outline" */
|
13
12
|
}
|
13
|
+
.aloha-block.aloha-block-active, .aloha-block.aloha-block-active:hover {
|
14
|
+
box-shadow: 0 0 0px 3px #80B5F2; /* We use box-shadow because of a Firefox rendering bug with "outline" */
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
/* IE8 will use outline for highlighting */
|
19
|
+
.ext-ie8 .aloha-block:hover {
|
20
|
+
outline: 3px solid #FFE767; /* IE does not support box-shadow, thus we need to use border */
|
21
|
+
}
|
22
|
+
.ext-ie8 .aloha-block.aloha-block-active, .ext-ie8 .aloha-block.aloha-block-active:hover {
|
23
|
+
outline: 3px solid #80B5F2; /* IE does not support box-shadow, thus we need to use border */
|
24
|
+
}
|
25
|
+
/* IE7 will use border for outlining */
|
26
|
+
.ext-ie7 .aloha-block:hover {
|
27
|
+
border: 3px solid #FFE767; /* IE does not support box-shadow, thus we need to use border */
|
28
|
+
margin: -3px;
|
29
|
+
}
|
30
|
+
.ext-ie7 .aloha-block.aloha-block-active, .ext-ie7 .aloha-block.aloha-block-active:hover {
|
31
|
+
border: 3px solid #80B5F2; /* IE does not support box-shadow, thus we need to use border */
|
32
|
+
margin: -3px;
|
33
|
+
}
|
34
|
+
|
35
|
+
/** DRAG DROP **/
|
36
|
+
/* Display cursor for drag/drop */
|
37
|
+
.aloha-block-droppable {
|
38
|
+
border-left: 1px solid red;
|
39
|
+
margin-left:-1px;
|
40
|
+
}
|
41
|
+
.ext-ie7 .aloha-block-droppable {
|
42
|
+
margin-left:0; /* IE7 cannot handle the negative margin... */
|
43
|
+
}
|
44
|
+
|
45
|
+
.aloha-block-droppable.aloha-block-droppable-right {
|
46
|
+
margin-left:0 !important;
|
47
|
+
border-left: none !important;
|
48
|
+
margin-right:-1px;
|
49
|
+
border-right: 1px solid red;
|
50
|
+
}
|
51
|
+
|
52
|
+
.aloha-block-droppable-blocklevel {
|
53
|
+
position:relative;
|
54
|
+
}
|
55
|
+
.aloha-block-blockleveldragdropline {
|
56
|
+
position:absolute;
|
57
|
+
width: 100%;
|
58
|
+
height:2px;
|
59
|
+
background-color:red;
|
60
|
+
bottom:0;
|
61
|
+
|
62
|
+
}
|
63
|
+
/** OTHER **/
|
64
|
+
|
14
65
|
.aloha-block .aloha-editable {
|
15
66
|
cursor: auto;
|
16
67
|
}
|
17
|
-
.aloha-block-active > .aloha-block-inner,
|
18
|
-
.aloha-block-active:hover > .aloha-block-inner {
|
19
|
-
outline: 1px solid #2F7ECC;
|
20
|
-
background: #B4D7F8;
|
21
|
-
}
|
22
68
|
|
23
69
|
.aloha-block {
|
24
70
|
position:relative;
|
25
71
|
}
|
26
|
-
.aloha-block:hover .aloha-block-draghandle,
|
27
|
-
.aloha-block-active .aloha-block-draghandle {
|
72
|
+
.aloha-block:hover > .aloha-block-draghandle,
|
73
|
+
.aloha-block-active > .aloha-block-draghandle {
|
28
74
|
display:block;
|
29
75
|
}
|
30
76
|
.aloha-block-draghandle {
|
@@ -42,10 +88,17 @@
|
|
42
88
|
border-top-left-radius:5px;
|
43
89
|
border-top-right-radius:5px;
|
44
90
|
}
|
91
|
+
/* Hide drag handle while dragging takes place */
|
92
|
+
.aloha-block.ui-draggable-dragging .aloha-block-draghandle {
|
93
|
+
display: none;
|
94
|
+
}
|
45
95
|
|
46
96
|
.aloha-block-editor label {
|
47
97
|
display:block;
|
48
98
|
}
|
49
99
|
|
50
|
-
|
51
|
-
|
100
|
+
|
101
|
+
|
102
|
+
.aloha-block-dropInlineElementIntoEmptyBlock {
|
103
|
+
border: 1px solid red;
|
104
|
+
}
|
@@ -20,16 +20,16 @@ define([
|
|
20
20
|
'block/editormanager',
|
21
21
|
'block/blockcontenthandler',
|
22
22
|
'block/editor',
|
23
|
-
'css!block/css/block.css'
|
23
|
+
'css!block/css/block.css',
|
24
|
+
'block/jquery-ui-1.8.16.custom.min'
|
24
25
|
], function(Aloha, Plugin, jQuery, ContentHandlerManager, BlockManager, SidebarAttributeEditor, block, EditorManager, BlockContentHandler, editor) {
|
25
26
|
|
26
|
-
|
27
27
|
/**
|
28
|
-
* Register the plugin
|
28
|
+
* Register the 'block' plugin
|
29
29
|
*/
|
30
30
|
var BlockPlugin = Plugin.create( 'block', {
|
31
31
|
settings: {},
|
32
|
-
// dependencies: [ '
|
32
|
+
// dependencies: [ 'paste' ],
|
33
33
|
|
34
34
|
init: function () {
|
35
35
|
var that = this;
|
@@ -42,11 +42,14 @@ define([
|
|
42
42
|
EditorManager.register('number', editor.NumberEditor);
|
43
43
|
EditorManager.register('url', editor.UrlEditor);
|
44
44
|
EditorManager.register('email', editor.EmailEditor);
|
45
|
-
|
45
|
+
EditorManager.register('select', editor.SelectEditor);
|
46
|
+
EditorManager.register('button', editor.ButtonEditor);
|
47
|
+
|
46
48
|
// register content handler for block plugin
|
47
49
|
ContentHandlerManager.register('block', BlockContentHandler);
|
48
50
|
|
49
51
|
BlockManager.registerEventHandlers();
|
52
|
+
BlockManager.initializeBlockLevelDragDrop();
|
50
53
|
|
51
54
|
Aloha.bind('aloha-ready', function() {
|
52
55
|
// When Aloha is fully loaded, we initialize the blocks.
|
@@ -56,9 +59,11 @@ define([
|
|
56
59
|
}
|
57
60
|
});
|
58
61
|
},
|
59
|
-
_createBlocks: function() {
|
60
|
-
var defaultBlockSettings;
|
61
62
|
|
63
|
+
/**
|
64
|
+
* Create blocks from default settings
|
65
|
+
*/
|
66
|
+
_createBlocks: function() {
|
62
67
|
if (!this.settings.defaults) {
|
63
68
|
this.settings.defaults = {};
|
64
69
|
}
|
@@ -68,14 +73,6 @@ define([
|
|
68
73
|
}
|
69
74
|
});
|
70
75
|
|
71
|
-
/**
|
72
|
-
* See (http://jquery.com/).
|
73
|
-
* @name jQuery
|
74
|
-
* @class
|
75
|
-
* See the jQuery Library (http://jquery.com/) for full details. This just
|
76
|
-
* documents the function and classes that are added to jQuery by this plug-in.
|
77
|
-
*/
|
78
|
-
|
79
76
|
/**
|
80
77
|
* See (http://jquery.com/).
|
81
78
|
* @name jQuery.fn
|
@@ -6,14 +6,22 @@
|
|
6
6
|
*/
|
7
7
|
|
8
8
|
/**
|
9
|
+
* Module which contains the base class for Blocks, and a Default/Debug block.
|
10
|
+
*
|
9
11
|
* @name block.block
|
10
|
-
* @namespace
|
12
|
+
* @namespace block/block
|
11
13
|
*/
|
12
14
|
define(['aloha', 'aloha/jquery', 'block/blockmanager', 'aloha/observable', 'aloha/floatingmenu'],
|
13
15
|
function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
14
16
|
|
15
17
|
|
18
|
+
var GENTICS = window.GENTICS;
|
19
|
+
|
16
20
|
/**
|
21
|
+
* An aloha block has the following special properties, being readable through the
|
22
|
+
* "attr" function:
|
23
|
+
* - aloha-block-type -- TYPE of the AlohaBlock as registered by the BlockManager
|
24
|
+
*
|
17
25
|
* @name block.block.AbstractBlock
|
18
26
|
* @class An abstract block that must be used as a base class for custom blocks
|
19
27
|
*/
|
@@ -22,41 +30,32 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
22
30
|
{
|
23
31
|
|
24
32
|
/**
|
33
|
+
* Event which is triggered if the block attributes change.
|
34
|
+
*
|
25
35
|
* @name block.block.AbstractBlock#change
|
26
36
|
* @event
|
27
37
|
*/
|
28
38
|
|
29
39
|
/**
|
30
|
-
* Title
|
40
|
+
* Title of the block, used to display the name in the sidebar editor.
|
41
|
+
*
|
31
42
|
* @type String
|
32
43
|
* @api
|
33
44
|
*/
|
34
45
|
title: null,
|
35
46
|
|
36
47
|
/**
|
37
|
-
* Id of the
|
48
|
+
* Id of the underlying $element, used to identify the block.
|
38
49
|
* @type String
|
39
50
|
*/
|
40
51
|
id: null,
|
41
52
|
|
42
53
|
/**
|
43
|
-
* The
|
44
|
-
* @type jQuery
|
45
|
-
*/
|
46
|
-
// TODO: Rename to $element
|
47
|
-
element: null,
|
48
|
-
|
49
|
-
/**
|
50
|
-
* The inner element which is containing the actual user-provided content
|
54
|
+
* The wrapping element of the block.
|
51
55
|
* @type jQuery
|
56
|
+
* @api
|
52
57
|
*/
|
53
|
-
$
|
54
|
-
|
55
|
-
/**
|
56
|
-
* Either "inline" or "block", will be guessed from the original block dom element
|
57
|
-
* @type String
|
58
|
-
*/
|
59
|
-
_domElementType: null,
|
58
|
+
$element: null,
|
60
59
|
|
61
60
|
/**
|
62
61
|
* if TRUE, the rendering is currently taking place. Used to prevent recursion
|
@@ -66,181 +65,333 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
66
65
|
_currentlyRendering: false,
|
67
66
|
|
68
67
|
/**
|
69
|
-
* set to TRUE once the block is fully initialized
|
68
|
+
* set to TRUE once the block is fully initialized.
|
70
69
|
*
|
71
70
|
* @type Boolean
|
72
71
|
*/
|
73
72
|
_initialized: false,
|
74
73
|
|
75
74
|
/**
|
76
|
-
*
|
75
|
+
* Set to TRUE if the last click activated a *nested editable*.
|
76
|
+
* If FALSE; the block itself is selected.
|
77
|
+
* This is needed when a block is deleted in IE7/8.
|
78
|
+
*/
|
79
|
+
_isInsideNestedEditable: false,
|
80
|
+
|
81
|
+
/**************************
|
82
|
+
* SECTION: Initialization and Lifecycle
|
83
|
+
**************************/
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Initialize the basic block. Do not call directly; instead use jQuery(...).alohaBlock() to
|
87
|
+
* create new blocks.
|
88
|
+
*
|
89
|
+
* This function shall only be called through the BlockManager. See BlockManager::_blockify().
|
90
|
+
*
|
91
|
+
* @param {jQuery} $element Element that declares the block
|
77
92
|
* @constructor
|
78
93
|
*/
|
79
|
-
_constructor: function($
|
94
|
+
_constructor: function($element) {
|
80
95
|
var that = this;
|
81
|
-
this.id = GENTICS.Utils.guid();
|
82
96
|
|
83
|
-
this.$
|
97
|
+
this.$element = $element;
|
84
98
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
99
|
+
if ($element.attr('id')) {
|
100
|
+
this.id = $element.attr('id');
|
101
|
+
} else {
|
102
|
+
this.id = GENTICS.Utils.guid();
|
103
|
+
$element.attr('id', this.id);
|
104
|
+
}
|
89
105
|
|
90
|
-
|
106
|
+
$element.contentEditable(false);
|
91
107
|
|
92
|
-
|
93
|
-
$innerElement.addClass('aloha-block-inner');
|
108
|
+
$element.addClass('aloha-block');
|
94
109
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}
|
110
|
+
if (this.isDraggable()) {
|
111
|
+
// Remove default drag/drop behavior of the browser
|
112
|
+
$element.find('img').attr('draggable', 'false');
|
113
|
+
$element.find('a').attr('draggable', 'false');
|
114
|
+
}
|
100
115
|
|
101
|
-
|
102
|
-
|
103
|
-
|
116
|
+
// While the event handler is defined here, it is connected to the DOM element inside "_connectThisBlockToDomElement"
|
117
|
+
this._onElementClickHandler = function(event) {
|
118
|
+
// We only activate ourselves if we are the innermost aloha-block.
|
119
|
+
// If we are not the innermost aloha-block, we get highlighted (but not activated) automatically
|
120
|
+
// by the innermost block.
|
121
|
+
if (jQuery(event.target).closest('.aloha-block').get(0) === that.$element.get(0)) {
|
122
|
+
that._fixScrollPositionBugsInIE();
|
123
|
+
that.activate(event.target, event);
|
104
124
|
}
|
105
|
-
}
|
125
|
+
};
|
106
126
|
|
107
|
-
//
|
108
|
-
|
109
|
-
// the block.
|
110
|
-
this.element.bind('mousedown', function() {
|
111
|
-
Aloha.eventHandled = true;
|
112
|
-
}).bind('focus', function() {
|
113
|
-
Aloha.eventHandled = true;
|
114
|
-
}).bind('dblclick', function() {
|
115
|
-
Aloha.eventHandled = true;
|
116
|
-
});
|
117
|
-
this.init();
|
127
|
+
// Register event handlers on the block
|
128
|
+
this._connectThisBlockToDomElement($element);
|
118
129
|
|
119
|
-
this._registerAsBlockified();
|
120
|
-
},
|
121
130
|
|
122
|
-
|
123
|
-
// TODO
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
}
|
129
|
-
|
131
|
+
// This is executed when a block is selected through caret handling
|
132
|
+
// TODO!
|
133
|
+
//Aloha.bind('aloha-block-selected', function(event,obj) {
|
134
|
+
// if (that.$element.get(0) === obj) {
|
135
|
+
// that.activate();
|
136
|
+
// }
|
137
|
+
//});
|
138
|
+
|
130
139
|
|
131
|
-
_registerAsBlockified: function() {
|
132
140
|
this._initialized = true;
|
133
|
-
this.element.trigger('block-initialized');
|
134
141
|
},
|
135
142
|
|
136
143
|
/**
|
137
|
-
*
|
138
|
-
*
|
144
|
+
* Is set inside the constructor to the event handler function
|
145
|
+
* which should be executed when the element is clicked.
|
146
|
+
*
|
147
|
+
* NOTE: Purely internal, "this" is not available inside this method!
|
139
148
|
*/
|
140
|
-
|
149
|
+
_onElementClickHandler: null,
|
141
150
|
|
142
151
|
/**
|
143
|
-
*
|
152
|
+
* We need to tell Aloha that we handle the event already;
|
153
|
+
* else a selection of a nested editable will *not* select
|
154
|
+
* the block.
|
144
155
|
*
|
145
|
-
*
|
156
|
+
* This callback is bound to the mousedown, focus and dblclick events.
|
146
157
|
*
|
147
|
-
*
|
148
|
-
* @returns {Object}
|
158
|
+
* NOTE: Purely internal, "this" is not available inside this method!
|
149
159
|
*/
|
150
|
-
|
151
|
-
|
160
|
+
_preventSelectionChangedEventHandler: function() {
|
161
|
+
Aloha.Selection.preventSelectionChanged();
|
152
162
|
},
|
153
163
|
|
154
164
|
/**
|
155
|
-
*
|
165
|
+
* This method connects this block object to the passed DOM element.
|
166
|
+
* In detail, this method does the following:
|
167
|
+
*
|
168
|
+
* - if this.$element is already set, remove all block event handlers
|
169
|
+
* - sets this.$element = jQuery(newElement)
|
170
|
+
* - initialize event listeners on this.$element
|
171
|
+
* - call init()
|
172
|
+
*
|
173
|
+
* The method is called in two contexts: First, when a block is constructed
|
174
|
+
* to initialize the event listeners etc. Second, it is ALSO called when
|
175
|
+
* a block inside a nested block with editable in between is detected
|
176
|
+
* as inconsistent.
|
156
177
|
*/
|
157
|
-
|
158
|
-
|
178
|
+
_connectThisBlockToDomElement: function(newElement) {
|
179
|
+
var that = this;
|
180
|
+
var $newElement = jQuery(newElement);
|
181
|
+
if (this.$element) {
|
182
|
+
this.$element.unbind('click', this._onElementClickHandler);
|
183
|
+
this.$element.unbind('mousedown', this._preventSelectionChangedEventHandler);
|
184
|
+
this.$element.unbind('focus', this._preventSelectionChangedEventHandler);
|
185
|
+
this.$element.unbind('dblclick', this._preventSelectionChangedEventHandler);
|
186
|
+
}
|
187
|
+
this.$element = $newElement;
|
188
|
+
|
189
|
+
this.$element.bind('click', this._onElementClickHandler);
|
190
|
+
this.$element.bind('mousedown', this._preventSelectionChangedEventHandler);
|
191
|
+
this.$element.bind('focus', this._preventSelectionChangedEventHandler);
|
192
|
+
this.$element.bind('dblclick', this._preventSelectionChangedEventHandler);
|
193
|
+
|
194
|
+
this.init(this.$element, function() {
|
195
|
+
// WORKAROUND against loading order dependencies. If we have
|
196
|
+
// nested Blocks inside each other (with no editables in between)
|
197
|
+
// it could be that the *inner* block is initialized *before* the outer one.
|
198
|
+
//
|
199
|
+
// However, the inner block needs to know whether it shall render drag handles or not,
|
200
|
+
// and this depends on whether it is inside an editable or a block.
|
201
|
+
//
|
202
|
+
// In order to fix this case, we delay the the drag-handle-rendering (and all the other
|
203
|
+
// post-processing) to the next JavaScript Run Loop using a small timeout.
|
204
|
+
window.setTimeout(function() {
|
205
|
+
that._postProcessElementIfNeeded();
|
206
|
+
}, 5);
|
207
|
+
});
|
159
208
|
},
|
160
209
|
|
161
210
|
/**
|
162
|
-
*
|
163
|
-
*
|
164
|
-
*
|
211
|
+
* IE HACK: Our beloved Internet Explorer sometimes scrolls to the top
|
212
|
+
* of the page when activating an aloha block, and on numerous other occasions
|
213
|
+
* like when an <span> block is moved via drag/drop.
|
214
|
+
*
|
215
|
+
* We can detect this and scroll right back; although this will flicker
|
216
|
+
* a little (but still a lot better than before)
|
217
|
+
*/
|
218
|
+
_fixScrollPositionBugsInIE: function() {
|
219
|
+
var scrollPositionBefore = jQuery(window).scrollTop();
|
220
|
+
window.setTimeout(function() {
|
221
|
+
if (jQuery(window).scrollTop() !== scrollPositionBefore) {
|
222
|
+
jQuery(window).scrollTop(scrollPositionBefore);
|
223
|
+
}
|
224
|
+
}, 10);
|
225
|
+
},
|
226
|
+
/**
|
227
|
+
* Template method to initialize the block. Can be used to set attributes
|
228
|
+
* on the block, depending on the block contents. You will most probably
|
229
|
+
* use $element and this.attr() inside this function.
|
230
|
+
*
|
231
|
+
* !!! This method can be called *multiple times*, as it is called each time
|
232
|
+
* when $element has been disconnected from the DOM (which can happen because of various reasons)
|
233
|
+
* and the block needs to re-initialize. So make sure this method can be called *MULTIPLE TIMES*
|
234
|
+
* and always returns predictable results. This method must be idempotent, same as update().
|
235
|
+
*
|
236
|
+
* Furthermore, always when this method is finished, you need to call postProcessFn() afterwards.
|
237
|
+
* This function adds drag handles and other controls if necessary.
|
238
|
+
*
|
239
|
+
* @param {jQuery} $element a shortcut to the block's DOM element (this.$element) for easy processing
|
240
|
+
* @param {Function} postProcessFn this function MUST be called at all times the $element has been updated; as it adds drag/drop/delete/... handles if necessary
|
165
241
|
* @api
|
166
242
|
*/
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
delete previouslyActiveBlocks[this.id];
|
172
|
-
|
173
|
-
this._selectBlock(clickedDomNode);
|
174
|
-
|
175
|
-
// Set scope to current block
|
176
|
-
FloatingMenu.setScope('Aloha.Block.' + this.attr('block-type'));
|
177
|
-
|
178
|
-
this._highlight();
|
179
|
-
activeBlocks.push(this);
|
180
|
-
|
181
|
-
this.element.parents('.aloha-block').each(function() {
|
182
|
-
var block = BlockManager.getBlock(this);
|
183
|
-
delete previouslyActiveBlocks[block.id];
|
184
|
-
|
185
|
-
block._highlight();
|
186
|
-
activeBlocks.push(block);
|
187
|
-
});
|
188
|
-
jQuery.each(previouslyActiveBlocks, function() {
|
189
|
-
this.deactivate();
|
190
|
-
});
|
191
|
-
|
192
|
-
BlockManager.trigger('block-selection-change', activeBlocks);
|
243
|
+
init: function($element, postProcessFn) {
|
244
|
+
postProcessFn();
|
245
|
+
},
|
193
246
|
|
194
|
-
|
247
|
+
/**
|
248
|
+
* Callback which is executed when somebody triggers destroy().
|
249
|
+
*
|
250
|
+
* This only allows destruction if the block is *inside* an aloha-editable and *not* inside an aloha-block.
|
251
|
+
*
|
252
|
+
* @return {Boolean} true of destruction should happen, false otherwise
|
253
|
+
*/
|
254
|
+
shouldDestroy: function() {
|
255
|
+
var $closest = this.$element.parent().closest('.aloha-block,.aloha-editable,.aloha-block-collection');
|
256
|
+
if ($closest.hasClass('aloha-block-collection') && this.$element[0].tagName.toLowerCase() === 'div') {
|
257
|
+
return true;
|
258
|
+
} else {
|
259
|
+
return $closest.hasClass('aloha-editable');
|
260
|
+
}
|
195
261
|
},
|
196
262
|
|
197
263
|
/**
|
198
264
|
* Destroy this block instance completely. Removes the element from the DOM,
|
199
|
-
* unregisters it, and triggers a delete event on the BlockManager.
|
265
|
+
* unregisters it, and triggers a block-delete event on the BlockManager.
|
200
266
|
*
|
201
|
-
* @
|
267
|
+
* @param {Boolean} force TRUE if you want to force deletion, despite shouldDestroy() returning false.
|
202
268
|
* @api
|
203
269
|
*/
|
204
|
-
destroy: function() {
|
270
|
+
destroy: function(force) {
|
271
|
+
if (!this.shouldDestroy() && force !== true) return;
|
272
|
+
|
205
273
|
var that = this;
|
274
|
+
var newRange = new GENTICS.Utils.RangeObject();
|
275
|
+
|
276
|
+
newRange.startContainer = newRange.endContainer = this.$element.parent()[0];
|
277
|
+
newRange.startOffset = newRange.endOffset = GENTICS.Utils.Dom.getIndexInParent(this.$element[0]);
|
278
|
+
|
206
279
|
BlockManager.trigger('block-delete', this);
|
207
280
|
BlockManager._unregisterBlock(this);
|
208
281
|
|
209
282
|
this.unbindAll();
|
210
283
|
|
211
|
-
this
|
212
|
-
|
284
|
+
var isInlineElement = this.$element[0].tagName.toLowerCase() === 'span';
|
285
|
+
|
286
|
+
this.$element.fadeOut('fast', function() {
|
287
|
+
that.$element.remove();
|
213
288
|
BlockManager.trigger('block-selection-change', []);
|
289
|
+
window.setTimeout(function() {
|
290
|
+
if (isInlineElement) {
|
291
|
+
newRange.select();
|
292
|
+
}
|
293
|
+
}, 5);
|
214
294
|
});
|
215
295
|
},
|
216
296
|
|
297
|
+
/**************************
|
298
|
+
* SECTION: Getters and Helpers
|
299
|
+
**************************/
|
300
|
+
|
217
301
|
/**
|
218
|
-
*
|
302
|
+
* Get the id of the block
|
303
|
+
* @returns {String}
|
219
304
|
*/
|
220
|
-
|
221
|
-
|
222
|
-
this.element.addClass('aloha-block-active');
|
305
|
+
getId: function() {
|
306
|
+
return this.id;
|
223
307
|
},
|
224
308
|
|
309
|
+
/**
|
310
|
+
* Get a schema of attributes which shall be rendered / edited
|
311
|
+
* in the sidebar.
|
312
|
+
*
|
313
|
+
* @api
|
314
|
+
* @returns {Object}
|
315
|
+
*/
|
316
|
+
getSchema: function() {
|
317
|
+
return null;
|
318
|
+
},
|
225
319
|
|
226
|
-
|
227
|
-
|
228
|
-
|
320
|
+
/**
|
321
|
+
* Template Method which should return the block title. Needed for editing sidebar.
|
322
|
+
* By default, the block title is returned.
|
323
|
+
*
|
324
|
+
* @api
|
325
|
+
*/
|
326
|
+
getTitle: function() {
|
327
|
+
return this.title;
|
229
328
|
},
|
230
329
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
330
|
+
/**
|
331
|
+
* Returns true if the block is draggable because it is inside an aloha-editable, false otherwise.
|
332
|
+
*
|
333
|
+
* You cannot depend on this method's result during the *init* phase of the Aloha Block, as the
|
334
|
+
* outer block might not be initialized at that point yet. Thus, do not call this method inside init().
|
335
|
+
*
|
336
|
+
* @return Boolean
|
337
|
+
*/
|
338
|
+
isDraggable: function() {
|
339
|
+
if (this.$element[0].tagName.toLowerCase() === 'div' && this.$element.parents('.aloha-editable,.aloha-block,.aloha-block-collection').first().hasClass('aloha-block-collection')) {
|
340
|
+
// Here, we are inside an aloha-block-collection, and thus also need to be draggable.
|
341
|
+
return true;
|
236
342
|
}
|
343
|
+
return this.$element.parents('.aloha-editable,.aloha-block').first().hasClass('aloha-editable');
|
344
|
+
},
|
345
|
+
|
346
|
+
/**************************
|
347
|
+
* SECTION: Activation / Deactivation
|
348
|
+
**************************/
|
349
|
+
|
350
|
+
/**
|
351
|
+
* activates the block
|
352
|
+
* will select the block's contents, highlight it, update the floating menu and update the sidebar (if needed).
|
353
|
+
*
|
354
|
+
* When calling programmatically, do not set eventTarget or event arguments.
|
355
|
+
* @api
|
356
|
+
*/
|
357
|
+
activate: function(eventTarget, event) {
|
358
|
+
var highlightedBlocks = [];
|
359
|
+
|
360
|
+
// Deactivate currently highlighted blocks
|
361
|
+
jQuery.each(BlockManager._getHighlightedBlocks(), function() {
|
362
|
+
this.deactivate();
|
363
|
+
});
|
237
364
|
|
238
|
-
|
239
|
-
|
240
|
-
|
365
|
+
// Activate current block
|
366
|
+
if (this.$element.attr('data-block-skip-scope') !== 'true') {
|
367
|
+
FloatingMenu.setScope('Aloha.Block.' + this.attr('aloha-block-type'));
|
241
368
|
}
|
369
|
+
this.$element.addClass('aloha-block-active');
|
370
|
+
this._highlight();
|
371
|
+
highlightedBlocks.push(this);
|
242
372
|
|
243
|
-
|
373
|
+
// Highlight parent blocks
|
374
|
+
this.$element.parents('.aloha-block').each(function() {
|
375
|
+
var block = BlockManager.getBlock(this);
|
376
|
+
block._highlight();
|
377
|
+
highlightedBlocks.push(block);
|
378
|
+
});
|
379
|
+
|
380
|
+
// Browsers do not remove the cursor, so we enforce it when an aditable is clicked.
|
381
|
+
// However, when the user clicked inside a nested editable, we will not remove the cursor (as the user wants to start typing then)
|
382
|
+
// small HACK: we also do not deactivate if we are inside an aloha-table-cell-editable.
|
383
|
+
if (jQuery(eventTarget).closest('.aloha-editable,.aloha-block,.aloha-table-cell-editable').first().hasClass('aloha-block')) {
|
384
|
+
this._isInsideNestedEditable = false;
|
385
|
+
Aloha.getSelection().removeAllRanges();
|
386
|
+
} else {
|
387
|
+
this._isInsideNestedEditable = true;
|
388
|
+
if (event) {
|
389
|
+
// We now update the selection, as you clicked *inside* an editable inside the block
|
390
|
+
Aloha.Selection.updateSelection(event);
|
391
|
+
}
|
392
|
+
}
|
393
|
+
// Trigger selection change event
|
394
|
+
BlockManager.trigger('block-selection-change', highlightedBlocks);
|
244
395
|
},
|
245
396
|
|
246
397
|
/**
|
@@ -249,92 +400,545 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
249
400
|
deactivate: function() {
|
250
401
|
var that = this;
|
251
402
|
this._unhighlight();
|
252
|
-
this
|
403
|
+
this.$element.parents('.aloha-block').each(function() {
|
253
404
|
that._unhighlight();
|
254
405
|
});
|
406
|
+
|
407
|
+
this.$element.removeClass('aloha-block-active');
|
255
408
|
BlockManager.trigger('block-selection-change', []);
|
256
|
-
// TODO: remove the current selection here
|
257
409
|
},
|
258
410
|
|
259
411
|
/**
|
260
412
|
* @returns {Boolean} True if this block is active
|
261
413
|
*/
|
262
414
|
isActive: function() {
|
263
|
-
return this
|
415
|
+
return this.$element.hasClass('aloha-block-active');
|
264
416
|
},
|
265
417
|
|
266
418
|
/**
|
267
|
-
*
|
268
|
-
*
|
419
|
+
* Internal helper which sets a block as highlighted, because the block itself
|
420
|
+
* or a child block has been activated.
|
269
421
|
*/
|
270
|
-
|
271
|
-
|
422
|
+
_highlight: function() {
|
423
|
+
this.$element.addClass('aloha-block-highlighted');
|
424
|
+
BlockManager._setHighlighted(this);
|
272
425
|
},
|
273
426
|
|
274
427
|
/**
|
275
|
-
*
|
276
|
-
*
|
277
|
-
* The renderer must manually take care of flushing the inner element if it needs that.
|
278
|
-
*
|
279
|
-
* @api
|
428
|
+
* Internal helper which sets a block as un-highlighted.
|
280
429
|
*/
|
281
|
-
|
430
|
+
_unhighlight: function() {
|
431
|
+
this.$element.removeClass('aloha-block-highlighted');
|
432
|
+
BlockManager._setUnhighlighted(this);
|
433
|
+
},
|
434
|
+
|
435
|
+
/**************************
|
436
|
+
* SECTION: Block Rendering
|
437
|
+
**************************/
|
282
438
|
|
283
|
-
|
439
|
+
/**
|
440
|
+
* Internal _update method, which needs to be called internally if a property
|
441
|
+
* changed. This is just a wrapper around update().
|
442
|
+
*/
|
443
|
+
_update: function() {
|
444
|
+
var that = this;
|
284
445
|
if (this._currentlyRendering) return;
|
285
446
|
if (!this._initialized) return;
|
286
447
|
|
287
448
|
this._currentlyRendering = true;
|
288
449
|
|
289
|
-
|
450
|
+
this.update(this.$element, function() {
|
451
|
+
that._postProcessElementIfNeeded();
|
452
|
+
});
|
290
453
|
|
291
|
-
|
292
|
-
|
293
|
-
|
454
|
+
this._currentlyRendering = false;
|
455
|
+
},
|
456
|
+
|
457
|
+
/**
|
458
|
+
* Template method to render contents of the block, must be implemented by specific block type.
|
459
|
+
* $element can be augumented by additional DOM elements like drag/drop handles. If you do
|
460
|
+
* any jQuery selection, you need to ignore all results which have a "aloha-block-handle" class
|
461
|
+
* set.
|
462
|
+
*
|
463
|
+
* Furthermore, always when you update $element, you need to call postProcessFn() afterwards.
|
464
|
+
* This function adds drag handles and other controls if necessary.
|
465
|
+
*
|
466
|
+
* This method should *only* be called from the internal _update method.
|
467
|
+
*
|
468
|
+
* @param {jQuery} $element a shortcut to the block's DOM element (this.$element) for easy processing
|
469
|
+
* @param {Function} postProcessFn this function MUST be called at all times the $element has been updated; as it adds drag/drop/delete/... handles if necessary
|
470
|
+
*
|
471
|
+
* @api
|
472
|
+
*/
|
473
|
+
update: function($element, postProcessFn) {
|
474
|
+
postProcessFn();
|
475
|
+
},
|
476
|
+
|
477
|
+
|
478
|
+
/**
|
479
|
+
* Post processor, being called to augument the Block Element's DOM by drag handles etc.
|
480
|
+
*
|
481
|
+
* This method must be idempotent. I.e. it must produce the same results
|
482
|
+
* when called once or twice.
|
483
|
+
*/
|
484
|
+
_postProcessElementIfNeeded: function() {
|
485
|
+
this.createEditablesIfNeeded();
|
486
|
+
this._checkThatNestedBlocksAreStillConsistent();
|
487
|
+
this._makeNestedBlockCollectionsSortable();
|
488
|
+
|
489
|
+
this.renderBlockHandlesIfNeeded();
|
490
|
+
if (this.isDraggable() && this.$element[0].tagName.toLowerCase() === 'span') {
|
491
|
+
this._setupDragDropForInlineElements();
|
492
|
+
this._disableUglyInternetExplorerDragHandles();
|
493
|
+
} else if (this.isDraggable() && this.$element[0].tagName.toLowerCase() === 'div') {
|
494
|
+
this._setupDragDropForBlockElements();
|
495
|
+
this._disableUglyInternetExplorerDragHandles();
|
294
496
|
}
|
497
|
+
},
|
295
498
|
|
296
|
-
|
499
|
+
/**
|
500
|
+
* Due to indeterminate initialization order of nested blocks,
|
501
|
+
* it can happen that blockifying a parent block deconnects $element inside
|
502
|
+
* child blocks.
|
503
|
+
*
|
504
|
+
* This is the case we detect here; and if it happens, we reconnect the
|
505
|
+
* block to its currently visible DOM element.
|
506
|
+
*/
|
507
|
+
_checkThatNestedBlocksAreStillConsistent: function() {
|
508
|
+
this.$element.find('.aloha-block').each(function() {
|
509
|
+
var block = BlockManager.getBlock(this);
|
510
|
+
if (block && block.$element[0] !== this) {
|
511
|
+
block._connectThisBlockToDomElement(this);
|
512
|
+
}
|
513
|
+
});
|
514
|
+
},
|
297
515
|
|
298
|
-
|
516
|
+
/**
|
517
|
+
* If a nested element is marked as "aloha-block-collection",
|
518
|
+
* we want to make it sortable, by calling the appropriate Block Manager
|
519
|
+
* function.
|
520
|
+
*/
|
521
|
+
_makeNestedBlockCollectionsSortable: function() {
|
522
|
+
var that = this;
|
523
|
+
this.$element.find('.aloha-block-collection').each(function() {
|
524
|
+
var $blockCollection = jQuery(this);
|
525
|
+
if ($blockCollection.closest('.aloha-block').get(0) === that.$element.get(0)) {
|
526
|
+
// We are only responsible for one-level-down Block Collections, not
|
527
|
+
// for nested ones.
|
528
|
+
BlockManager.createBlockLevelSortableForEditableOrBlockCollection($blockCollection);
|
529
|
+
}
|
530
|
+
})
|
531
|
+
},
|
532
|
+
|
533
|
+
/**
|
534
|
+
* Helper which disables the ugly IE drag handles. They are still shown, but at
|
535
|
+
* least they do not work anymore
|
536
|
+
*/
|
537
|
+
_disableUglyInternetExplorerDragHandles: function() {
|
538
|
+
this.$element.get( 0 ).onresizestart = function ( e ) { return false; };
|
539
|
+
this.$element.get( 0 ).oncontrolselect = function ( e ) { return false; };
|
540
|
+
// We do NOT abort the "ondragstart" event as it is required for drag/drop.
|
541
|
+
this.$element.get( 0 ).onmovestart = function ( e ) { return false; };
|
542
|
+
this.$element.get( 0 ).onselectstart = function ( e ) { return false; };
|
299
543
|
},
|
300
544
|
|
301
|
-
|
302
|
-
|
303
|
-
|
545
|
+
/**************************
|
546
|
+
* SECTION: Drag&Drop for INLINE elements
|
547
|
+
**************************/
|
548
|
+
_setupDragDropForInlineElements: function() {
|
549
|
+
var that = this;
|
304
550
|
|
305
|
-
|
551
|
+
// Here, we store the character DOM element which has been hovered upon recently.
|
552
|
+
// This is needed as somehow, the "drop" event on the character is not fired.
|
553
|
+
// Furthermore, we use it to know whether we need to "revert" the draggable to the original state or not.
|
554
|
+
var lastHoveredCharacter = null;
|
555
|
+
|
556
|
+
// HACK for IE7: Internet Explorer 7 has a very weird behavior in
|
557
|
+
// not always firing the "drop" callback of the inner droppable... However,
|
558
|
+
// the "over" and "out" callbacks are fired correctly.
|
559
|
+
// Because of this, we handle the "drop" inside the "stop" callback in IE7
|
560
|
+
// instead of the "drop" callback (where it is handled in all other browsers)
|
561
|
+
|
562
|
+
// This $currentDraggable is also needed as part of the IE 7 hack.
|
563
|
+
// $currentDraggable contains a reference to the current draggable, but
|
564
|
+
// only makes sense to read when lastHoveredCharacter !== NULL.
|
565
|
+
var $currentDraggable = null;
|
566
|
+
|
567
|
+
// This dropFn is the callback which handles the actual moving of
|
568
|
+
// nodes. We created a separate function for it, as it is called inside the "stop" callback
|
569
|
+
// in IE7 and inside the "drop" callback in all other browsers.
|
570
|
+
var dropFn = function() {
|
571
|
+
if (lastHoveredCharacter) {
|
572
|
+
// the user recently hovered over a character
|
573
|
+
var $dropReferenceNode = jQuery(lastHoveredCharacter);
|
574
|
+
|
575
|
+
if ($dropReferenceNode.is('.aloha-block-dropInlineElementIntoEmptyBlock')) {
|
576
|
+
// the user wanted to drop INTO an empty block!
|
577
|
+
$dropReferenceNode.children().remove();
|
578
|
+
$dropReferenceNode.append($currentDraggable);
|
579
|
+
} else if ($dropReferenceNode.is('.aloha-block-droppable-right')) {
|
580
|
+
$dropReferenceNode.html($dropReferenceNode.html() + ' ');
|
581
|
+
|
582
|
+
// Move draggable after drop reference node
|
583
|
+
$dropReferenceNode.after($currentDraggable);
|
584
|
+
} else {
|
585
|
+
// Insert space in the beginning of the drop reference node
|
586
|
+
if ($dropReferenceNode.prev('[data-i]').length > 0) {
|
587
|
+
// If not the last element, insert space in front of next element (i.e. after the moved block)
|
588
|
+
$dropReferenceNode.prev('[data-i]').html($dropReferenceNode.prev('[data-i]').html() + ' ');
|
589
|
+
}
|
590
|
+
$dropReferenceNode.html(' ' + $dropReferenceNode.html());
|
591
|
+
|
592
|
+
// Move draggable before drop reference node
|
593
|
+
$dropReferenceNode.before($currentDraggable);
|
594
|
+
}
|
306
595
|
|
307
|
-
|
596
|
+
$currentDraggable.removeClass('ui-draggable').css({'left': 0, 'top': 0}); // Remove "draggable" options... somehow "Destroy" does not work
|
597
|
+
that._fixScrollPositionBugsInIE();
|
598
|
+
}
|
599
|
+
jQuery('.aloha-block-dropInlineElementIntoEmptyBlock').removeClass('aloha-block-dropInlineElementIntoEmptyBlock');
|
600
|
+
};
|
601
|
+
var editablesWhichNeedToBeCleaned = [];
|
602
|
+
this.$element.draggable({
|
603
|
+
handle: '.aloha-block-draghandle',
|
604
|
+
scope: 'aloha-block-inlinedragdrop',
|
605
|
+
revert: function() {
|
606
|
+
return (lastHoveredCharacter === null);
|
607
|
+
},
|
608
|
+
revertDuration: 250,
|
609
|
+
stop: function() {
|
610
|
+
if (Ext.isIE7) {
|
611
|
+
dropFn();
|
612
|
+
}
|
613
|
+
jQuery.each(editablesWhichNeedToBeCleaned, function() {
|
614
|
+
that._dd_traverseDomTreeAndRemoveSpans(this);
|
615
|
+
})
|
616
|
+
$currentDraggable = null;
|
617
|
+
|
618
|
+
editablesWhichNeedToBeCleaned = [];
|
619
|
+
},
|
620
|
+
start: function() {
|
621
|
+
editablesWhichNeedToBeCleaned = [];
|
622
|
+
|
623
|
+
// In order to make Inline Blocks droppable into empty paragraphs, we insert a manually before the placeholder-br.
|
624
|
+
// -> for IE
|
625
|
+
jQuery('.aloha-editable').children('p:empty').html(' ');
|
626
|
+
|
627
|
+
|
628
|
+
// Make **ALL** editables on the page droppable, such that it is possible
|
629
|
+
// to drag/drop *across* editable boundaries
|
630
|
+
var droppableCfg = {
|
631
|
+
// make block elements droppable
|
632
|
+
tolerance: 'pointer',
|
633
|
+
addClasses: false, // performance optimization
|
634
|
+
scope: 'aloha-block-inlinedragdrop',
|
635
|
+
|
636
|
+
/**
|
637
|
+
* When hovering over a paragraph, we make convert its contents into spans, to make
|
638
|
+
* them droppable.
|
639
|
+
*/
|
640
|
+
over: function(event, ui) {
|
641
|
+
if (editablesWhichNeedToBeCleaned.indexOf(this) === -1) {
|
642
|
+
editablesWhichNeedToBeCleaned.push(this);
|
643
|
+
}
|
644
|
+
|
645
|
+
$currentDraggable = ui.draggable;
|
646
|
+
if (jQuery(this).is(':empty') || jQuery(this).children('br.aloha-end-br').length > 0 || jQuery(this).html() === ' ') {
|
647
|
+
// the user tries to drop into an empty container, thus we highlight the container and do an early return
|
648
|
+
jQuery(this).addClass('aloha-block-dropInlineElementIntoEmptyBlock');
|
649
|
+
lastHoveredCharacter = this;
|
650
|
+
return;
|
651
|
+
}
|
652
|
+
|
653
|
+
that._dd_traverseDomTreeAndWrapCharactersWithSpans(this);
|
654
|
+
jQuery('span[data-i]', this).droppable({
|
655
|
+
tolerance: 'pointer',
|
656
|
+
addClasses: false,
|
657
|
+
scope: 'aloha-block-inlinedragdrop',
|
658
|
+
over: function() {
|
659
|
+
if (lastHoveredCharacter) {
|
660
|
+
// Just to be sure, we remove the css class of the last hovered character.
|
661
|
+
// This is needed such that spans are deselected which contain multiple
|
662
|
+
// lines.
|
663
|
+
jQuery(lastHoveredCharacter).removeClass('aloha-block-droppable');
|
664
|
+
}
|
665
|
+
lastHoveredCharacter = this;
|
666
|
+
jQuery(this).addClass('aloha-block-droppable');
|
667
|
+
},
|
668
|
+
out: function() {
|
669
|
+
jQuery(this).removeClass('aloha-block-droppable');
|
670
|
+
if (lastHoveredCharacter === this) {
|
671
|
+
lastHoveredCharacter = null;
|
672
|
+
}
|
673
|
+
}
|
674
|
+
});
|
675
|
+
// Now that we updated the droppables in the system, we need to recalculate
|
676
|
+
// the Drag Drop offsets.
|
677
|
+
jQuery.ui.ddmanager.prepareOffsets(ui.draggable.data('draggable'), event);
|
678
|
+
},
|
679
|
+
out: function() {
|
680
|
+
jQuery(this).removeClass('aloha-block-dropInlineElementIntoEmptyBlock');
|
681
|
+
},
|
682
|
+
|
683
|
+
/**
|
684
|
+
* When dropping over a paragraph, we use the "lastHoveredCharacter"
|
685
|
+
* as drop target.
|
686
|
+
*/
|
687
|
+
drop: function() {
|
688
|
+
if (!Ext.isIE7) {
|
689
|
+
dropFn();
|
690
|
+
}
|
691
|
+
}
|
692
|
+
};
|
693
|
+
|
694
|
+
|
695
|
+
jQuery('.aloha-editable').children(':not(.aloha-block)').droppable(droppableCfg);
|
696
|
+
// Small HACK: Also make table cells droppable
|
697
|
+
jQuery('.aloha-table-cell-editable').droppable(droppableCfg);
|
698
|
+
}
|
699
|
+
});
|
308
700
|
},
|
309
701
|
|
310
|
-
|
311
|
-
|
702
|
+
/**
|
703
|
+
* Helper which traverses the DOM tree starting from el and wraps all non-empty texts with spans,
|
704
|
+
* such that they can act as drop target.
|
705
|
+
*
|
706
|
+
* @param {DomElement} el
|
707
|
+
*/
|
708
|
+
_dd_traverseDomTreeAndWrapCharactersWithSpans: function(el) {
|
709
|
+
var child;
|
710
|
+
for(var i=0, l=el.childNodes.length; i < l; i++) {
|
711
|
+
child = el.childNodes[i];
|
712
|
+
if (child.nodeType === 1) { // DOM Nodes
|
713
|
+
if (!~child.className.indexOf('aloha-block') && child.attributes['data-i'] === undefined) {
|
714
|
+
// We only recurse if child does NOT have the class "aloha-block", and is NOT data-i
|
715
|
+
this._dd_traverseDomTreeAndWrapCharactersWithSpans(child);
|
716
|
+
} else if (child.attributes['data-i']) {
|
717
|
+
// data-i set -> we have converted this hierarchy level already --> early return!
|
718
|
+
return;
|
719
|
+
}
|
720
|
+
} else if (child.nodeType === 3) { // Text Nodes
|
721
|
+
var numberOfSpansInserted = this._dd_insertSpans(child);
|
722
|
+
i += numberOfSpansInserted;
|
723
|
+
l += numberOfSpansInserted;
|
724
|
+
}
|
725
|
+
}
|
312
726
|
},
|
313
727
|
|
728
|
+
/**
|
729
|
+
* Helper which splits text on word boundaries, adding whitespaces to the following element.
|
730
|
+
* Examples:
|
731
|
+
* - "Hello world" -> ["Hello", " world"]
|
732
|
+
* - " Hello world" -> [" Hello", " world"]
|
733
|
+
* --> see the unit tests for the specification
|
734
|
+
*/
|
735
|
+
_dd_splitText: function(text) {
|
736
|
+
var textParts = text.split(/(?=\b)/);
|
737
|
+
var cleanedTextParts = [];
|
738
|
+
|
739
|
+
var isWhitespace = false;
|
740
|
+
for (var i=0,l=textParts.length; i<l; i++) {
|
741
|
+
if (!/[^\t\n\r ]/.test(textParts[i])) {
|
742
|
+
// if the current text part is just whitespace, we add a flag...
|
743
|
+
isWhitespace = true;
|
744
|
+
} else {
|
745
|
+
if (isWhitespace) {
|
746
|
+
// we have a whitespace to add
|
747
|
+
cleanedTextParts.push(' ' + textParts[i]);
|
748
|
+
isWhitespace = false;
|
749
|
+
} else {
|
750
|
+
cleanedTextParts.push(textParts[i]);
|
751
|
+
}
|
752
|
+
}
|
753
|
+
}
|
754
|
+
if (isWhitespace) {
|
755
|
+
cleanedTextParts[cleanedTextParts.length - 1] += ' ';
|
756
|
+
}
|
757
|
+
return cleanedTextParts;
|
758
|
+
},
|
759
|
+
|
760
|
+
/**
|
761
|
+
* This is a helper for _dd_traverseDomTreeAndWrapCharactersWithSpans,
|
762
|
+
* performing the actual conversion.
|
763
|
+
*
|
764
|
+
* This function returns the number of additional DOM elements inserted.
|
765
|
+
* This is "numberOfSpansCreated - 1" (because one text node has been initially there)
|
766
|
+
*/
|
767
|
+
_dd_insertSpans: function(el) {
|
768
|
+
var text = el.nodeValue;
|
769
|
+
|
770
|
+
// If node just contains empty strings, we do not do anything.
|
771
|
+
// Use ECMA-262 Edition 3 String and RegExp features
|
772
|
+
if (!/[^\t\n\r ]/.test(text)) {
|
773
|
+
return 0;
|
774
|
+
}
|
775
|
+
var newNodes = document.createDocumentFragment();
|
776
|
+
|
777
|
+
var splitText = this._dd_splitText(text);
|
778
|
+
|
779
|
+
var l = splitText.length;
|
780
|
+
var x, word, leftWordPartLength, t;
|
781
|
+
var numberOfSpansInserted = 0;
|
782
|
+
|
783
|
+
for (var i=0; i<l; i++) {
|
784
|
+
// left half of word
|
785
|
+
word = splitText[i];
|
786
|
+
if (word.length === 0) continue;
|
787
|
+
// We use "floor" here such that sentence delimiters like "!" can have a block placed afterwards
|
788
|
+
leftWordPartLength = Math.floor(word.length/2);
|
789
|
+
|
790
|
+
// For Internet Explorer, we only make dropping AFTER words possible to improve performance
|
791
|
+
if (Ext.isIE7 || Ext.isIE8) {
|
792
|
+
leftWordPartLength = 0;
|
793
|
+
}
|
794
|
+
|
795
|
+
if (leftWordPartLength > 0) {
|
796
|
+
x = document.createElement('span');
|
797
|
+
x.appendChild(document.createTextNode(word.substr(0, leftWordPartLength)));
|
798
|
+
x.setAttribute('data-i', i);
|
799
|
+
|
800
|
+
newNodes.appendChild(x);
|
801
|
+
numberOfSpansInserted++;
|
802
|
+
}
|
803
|
+
|
804
|
+
// right half of word
|
805
|
+
x = document.createElement('span');
|
806
|
+
t = word.substr(leftWordPartLength);
|
807
|
+
x.appendChild(document.createTextNode(t));
|
808
|
+
x.setAttribute('data-i', i);
|
809
|
+
x.setAttribute('class', 'aloha-block-droppable-right');
|
810
|
+
|
811
|
+
newNodes.appendChild(x);
|
812
|
+
numberOfSpansInserted++;
|
813
|
+
}
|
814
|
+
el.parentNode.replaceChild(newNodes, el);
|
815
|
+
return numberOfSpansInserted-1;
|
816
|
+
},
|
817
|
+
|
818
|
+
/**
|
819
|
+
* After the Drag/Drop operation, we need to remove the SPAN elements
|
820
|
+
* again.
|
821
|
+
*/
|
822
|
+
_dd_traverseDomTreeAndRemoveSpans: function(el) {
|
823
|
+
var nodesToDelete = [], convertBack;
|
824
|
+
convertBack = function(el) {
|
825
|
+
var currentlyTraversingExpandedText = false, currentText, lastNode;
|
826
|
+
var child;
|
827
|
+
for(var i=0, l=el.childNodes.length; i < l; i++) {
|
828
|
+
child = el.childNodes[i];
|
829
|
+
if (child.nodeType === 1) { // Node
|
830
|
+
if (child.attributes['data-i'] !== undefined) {
|
831
|
+
if (!currentlyTraversingExpandedText) {
|
832
|
+
// We did not traverse expanded text before, and just entered an expanded text section
|
833
|
+
// thus, we reset all variables to their initial state
|
834
|
+
currentlyTraversingExpandedText = true;
|
835
|
+
currentText = '';
|
836
|
+
lastNode = undefined;
|
837
|
+
}
|
838
|
+
if (currentlyTraversingExpandedText) {
|
839
|
+
// We are currently traversing the expanded text nodes, so we collect their data
|
840
|
+
// together in the currentText variable. We know that they only
|
841
|
+
// have one TextNode child, as this is the way we constructed them.
|
842
|
+
//
|
843
|
+
// Note: we do NOT use child.innerHTML here, as this returns HTML entities;
|
844
|
+
// but we need the HTML entities already processed:
|
845
|
+
// - child.innerHTML returns "Hello World"
|
846
|
+
// - child.firstChild.nodeValue returns "Hello World"
|
847
|
+
currentText += child.firstChild.nodeValue;
|
848
|
+
|
849
|
+
if (lastNode) {
|
850
|
+
nodesToDelete.push(lastNode);
|
851
|
+
}
|
852
|
+
lastNode = child;
|
853
|
+
}
|
854
|
+
} else {
|
855
|
+
if (currentlyTraversingExpandedText) {
|
856
|
+
currentlyTraversingExpandedText = false;
|
857
|
+
// We just left a region with data-i elements set.
|
858
|
+
// so, we need to store the currentText back to the region.
|
859
|
+
// We do this by using the last visited node as anchor.
|
860
|
+
lastNode.parentNode.replaceChild(document.createTextNode(currentText), lastNode);
|
861
|
+
}
|
862
|
+
// Recursion
|
863
|
+
if (!~child.className.indexOf('aloha-block')) {
|
864
|
+
// If child does not have the class "aloha-block", we iterate into it
|
865
|
+
convertBack(child);
|
866
|
+
}
|
867
|
+
}
|
868
|
+
}
|
869
|
+
}
|
870
|
+
if (currentlyTraversingExpandedText) {
|
871
|
+
// Special case: the last child node *is* a wrapped text node and we are at the end of the collection.
|
872
|
+
// In this case, we convert the text as well.
|
873
|
+
lastNode.parentNode.replaceChild(document.createTextNode(currentText), lastNode);
|
874
|
+
}
|
875
|
+
};
|
876
|
+
|
877
|
+
convertBack(el);
|
878
|
+
|
879
|
+
for (var i=0, l=nodesToDelete.length; i<l; i++) {
|
880
|
+
nodesToDelete[i].parentNode.removeChild(nodesToDelete[i]);
|
881
|
+
}
|
882
|
+
},
|
883
|
+
|
884
|
+
/**************************
|
885
|
+
* SECTION: Drag&Drop for Block elements
|
886
|
+
**************************/
|
887
|
+
_setupDragDropForBlockElements: function() {
|
888
|
+
// Mark the drag handle with an extra CSS class, such that it is picked up by BlockManager.initializeBlockLevelDragDrop()
|
889
|
+
this.$element.find('.aloha-block-draghandle').addClass('aloha-block-draghandle-blocklevel');
|
890
|
+
},
|
891
|
+
|
892
|
+
|
893
|
+
/**************************
|
894
|
+
* SECTION: Other Rendering Helpers
|
895
|
+
**************************/
|
896
|
+
|
314
897
|
/**
|
315
898
|
* Create editables from the inner content that was
|
316
899
|
* rendered for this block.
|
317
900
|
*
|
901
|
+
* This method must be idempotent. I.e. it must produce the same results
|
902
|
+
* when called once or twice.
|
903
|
+
*
|
318
904
|
* Override to use a custom implementation and to pass
|
319
905
|
* special configuration to .aloha()
|
320
|
-
*
|
321
|
-
* @param {jQuery} innerElement
|
322
906
|
*/
|
323
|
-
|
324
|
-
|
907
|
+
createEditablesIfNeeded: function() {
|
908
|
+
// TODO: only create them if they are no aloha element yet...
|
909
|
+
// TODO: should only happen inside Aloha
|
910
|
+
this.$element.find('.aloha-editable').aloha();
|
325
911
|
},
|
326
912
|
|
327
913
|
/**
|
328
914
|
* Render block toolbar elements
|
329
915
|
*
|
916
|
+
* This method must be idempotent. I.e. it must produce the same results
|
917
|
+
* when called once or twice.
|
918
|
+
*
|
330
919
|
* Template method to render custom block UI.
|
920
|
+
* @api
|
331
921
|
*/
|
332
|
-
|
333
|
-
this.
|
922
|
+
renderBlockHandlesIfNeeded: function() {
|
923
|
+
if (this.isDraggable()) {
|
924
|
+
if (this.$element.children('.aloha-block-draghandle').length === 0) {
|
925
|
+
this.$element.prepend('<span class="aloha-block-handle aloha-block-draghandle"></span>');
|
926
|
+
}
|
927
|
+
}
|
334
928
|
},
|
335
929
|
|
930
|
+
/**************************
|
931
|
+
* SECTION: Attribute Handling
|
932
|
+
**************************/
|
933
|
+
|
336
934
|
/**
|
337
|
-
* Get or set one or many
|
935
|
+
* Get or set one or many attribute, similar to the jQuery attr() function.
|
936
|
+
*
|
937
|
+
* The attribute keys are converted internally to lowercase,
|
938
|
+
* so attr('foo', 'bar') and attr('FoO', 'bar') are the same internally.
|
939
|
+
* The same applies to reading.
|
940
|
+
*
|
941
|
+
* It is not allowed to set internal attributes (starting with aloha-block-) through this API.
|
338
942
|
*
|
339
943
|
* @api
|
340
944
|
* @param {String|Object} attributeNameOrObject
|
@@ -345,12 +949,20 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
345
949
|
var that = this, attributeChanged = false;
|
346
950
|
|
347
951
|
if (arguments.length >= 2) {
|
952
|
+
if (attributeNameOrObject.substr(0, 12) === 'aloha-block-') {
|
953
|
+
Aloha.Log.error('block/block', 'It is not allowed to set internal block attributes (starting with aloha-block-) through Block.attr() (You tried to set ' + attributeNameOrObject + ')');
|
954
|
+
return;
|
955
|
+
}
|
348
956
|
if (this._getAttribute(attributeNameOrObject) !== attributeValue) {
|
349
957
|
attributeChanged = true;
|
350
958
|
}
|
351
959
|
this._setAttribute(attributeNameOrObject, attributeValue);
|
352
960
|
} else if (typeof attributeNameOrObject === 'object') {
|
353
961
|
jQuery.each(attributeNameOrObject, function(key, value) {
|
962
|
+
if (key.substr(0, 12) === 'aloha-block-') {
|
963
|
+
Aloha.Log.error('block/block', 'It is not allowed to set internal block attributes (starting with aloha-block-) through Block.attr() (You tried to set ' + key + ')');
|
964
|
+
return;
|
965
|
+
}
|
354
966
|
if (that._getAttribute(key) !== value) {
|
355
967
|
attributeChanged = true;
|
356
968
|
}
|
@@ -362,33 +974,36 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
362
974
|
return this._getAttributes();
|
363
975
|
}
|
364
976
|
if (attributeChanged && !suppressEvents) {
|
365
|
-
this.
|
977
|
+
this._update();
|
366
978
|
this.trigger('change');
|
367
979
|
}
|
368
|
-
return
|
980
|
+
return null;
|
369
981
|
},
|
370
982
|
|
983
|
+
/**
|
984
|
+
* Internal helper for setting a single attribute.
|
985
|
+
*/
|
371
986
|
_setAttribute: function(name, value) {
|
372
|
-
|
373
|
-
this.element.attr('about', value);
|
374
|
-
} else {
|
375
|
-
this.element.attr('data-' + name, value);
|
376
|
-
}
|
987
|
+
this.$element.attr('data-' + name.toLowerCase(), value);
|
377
988
|
},
|
378
989
|
|
990
|
+
/**
|
991
|
+
* Internal helper for getting an attribute
|
992
|
+
*/
|
379
993
|
_getAttribute: function(name) {
|
380
|
-
return this.
|
994
|
+
return this.$element.attr('data-' + name.toLowerCase());
|
381
995
|
},
|
382
996
|
|
997
|
+
/**
|
998
|
+
* Internal helper for getting all attributes
|
999
|
+
*/
|
383
1000
|
_getAttributes: function() {
|
384
1001
|
var attributes = {};
|
385
1002
|
|
386
1003
|
// element.data() not always up-to-date, that's why we iterate over the attributes directly.
|
387
|
-
jQuery.each(this
|
388
|
-
if (attribute.name === '
|
389
|
-
attributes[
|
390
|
-
} else if (attribute.name.substr(0, 5) === 'data-') {
|
391
|
-
attributes[attribute.name.substr(5)] = attribute.value;
|
1004
|
+
jQuery.each(this.$element[0].attributes, function(i, attribute) {
|
1005
|
+
if (attribute.name.substr(0, 5) === 'data-') {
|
1006
|
+
attributes[attribute.name.substr(5).toLowerCase()] = attribute.value;
|
392
1007
|
}
|
393
1008
|
});
|
394
1009
|
|
@@ -404,11 +1019,8 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
404
1019
|
var DefaultBlock = AbstractBlock.extend(
|
405
1020
|
/** @lends block.block.DefaultBlock */
|
406
1021
|
{
|
407
|
-
|
408
|
-
|
409
|
-
},
|
410
|
-
render: function() {
|
411
|
-
return this.attr('default-content');
|
1022
|
+
update: function($element, postProcessFn) {
|
1023
|
+
postProcessFn();
|
412
1024
|
}
|
413
1025
|
});
|
414
1026
|
|
@@ -421,8 +1033,11 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
421
1033
|
/** @lends block.block.DebugBlock */
|
422
1034
|
{
|
423
1035
|
title: 'Debugging',
|
424
|
-
|
425
|
-
this.element
|
1036
|
+
init: function($element, postProcessFn) {
|
1037
|
+
this.update($element, postProcessFn);
|
1038
|
+
},
|
1039
|
+
update: function($element, postProcessFn) {
|
1040
|
+
$element.css({display: 'block'});
|
426
1041
|
var renderedAttributes = '<table class="debug-block">';
|
427
1042
|
jQuery.each(this.attr(), function(k, v) {
|
428
1043
|
renderedAttributes += '<tr><th>' + k + '</th><td>' + v + '</td></tr>';
|
@@ -430,7 +1045,8 @@ function(Aloha, jQuery, BlockManager, Observable, FloatingMenu) {
|
|
430
1045
|
|
431
1046
|
renderedAttributes += '</table>';
|
432
1047
|
|
433
|
-
|
1048
|
+
$element.html(renderedAttributes);
|
1049
|
+
postProcessFn();
|
434
1050
|
}
|
435
1051
|
});
|
436
1052
|
|