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.
Files changed (74) hide show
  1. data/Rakefile +1 -1
  2. data/lib/aloha/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/aloha/css/aloha.css +3 -0
  4. data/vendor/assets/javascripts/aloha/css/ext-aloha.css +3 -0
  5. data/vendor/assets/javascripts/aloha/lib/aloha-bootstrap.js +4565 -3934
  6. data/vendor/assets/javascripts/aloha/lib/aloha.js +1357 -702
  7. data/vendor/assets/javascripts/aloha/lib/aloha/command.js +16 -13
  8. data/vendor/assets/javascripts/aloha/lib/aloha/core.js +23 -3
  9. data/vendor/assets/javascripts/aloha/lib/aloha/ecma5shims.js +23 -7
  10. data/vendor/assets/javascripts/aloha/lib/aloha/editable.js +57 -14
  11. data/vendor/assets/javascripts/aloha/lib/aloha/engine.js +9 -5
  12. data/vendor/assets/javascripts/aloha/lib/aloha/floatingmenu.js +288 -96
  13. data/vendor/assets/javascripts/aloha/lib/aloha/jquery.js +11 -1
  14. data/vendor/assets/javascripts/aloha/lib/aloha/markup.js +318 -40
  15. data/vendor/assets/javascripts/aloha/lib/aloha/repositorymanager.js +11 -10
  16. data/vendor/assets/javascripts/aloha/lib/aloha/selection.js +20 -1
  17. data/vendor/assets/javascripts/aloha/lib/aloha/sidebar.js +11 -1
  18. data/vendor/assets/javascripts/aloha/lib/jquery-plugin.js +10 -7
  19. data/vendor/assets/javascripts/aloha/lib/util/dom.js +18 -6
  20. data/vendor/assets/javascripts/aloha/lib/util/range.js +6 -6
  21. data/vendor/assets/javascripts/aloha/lib/vendor/ext-3.2.1/ext-all-debug.js +26 -2
  22. data/vendor/assets/javascripts/aloha/lib/vendor/jquery.store.js +39 -15
  23. data/vendor/assets/javascripts/aloha/plugins/common/abbr/lib/abbr-plugin.js +1 -0
  24. data/vendor/assets/javascripts/aloha/plugins/common/align/lib/align-plugin.js +344 -334
  25. data/vendor/assets/javascripts/aloha/plugins/common/block/css/block.css +65 -12
  26. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/block-plugin.js +12 -15
  27. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/block.js +796 -180
  28. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/blockcontenthandler.js +54 -13
  29. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/blockmanager.js +315 -78
  30. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/editor.js +111 -8
  31. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/editormanager.js +2 -0
  32. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/jquery-ui-1.8.16.custom.min.js +198 -0
  33. data/vendor/assets/javascripts/aloha/plugins/common/block/lib/sidebarattributeeditor.js +7 -20
  34. data/vendor/assets/javascripts/aloha/plugins/common/characterpicker/lib/characterpicker-plugin.js +15 -3
  35. data/vendor/assets/javascripts/aloha/plugins/common/contenthandler/lib/sanitizecontenthandler.js +3 -2
  36. data/vendor/assets/javascripts/aloha/plugins/common/contenthandler/lib/wordcontenthandler.js +111 -5
  37. data/vendor/assets/javascripts/aloha/plugins/common/dom-to-xhtml/lib/dom-to-xhtml-plugin.js +29 -0
  38. data/vendor/assets/javascripts/aloha/plugins/common/dom-to-xhtml/lib/dom-to-xhtml.js +306 -0
  39. data/vendor/assets/javascripts/aloha/plugins/common/format/lib/format-plugin.js +59 -5
  40. data/vendor/assets/javascripts/aloha/plugins/common/format/nls/i18n.js +1 -1
  41. data/vendor/assets/javascripts/aloha/plugins/common/horizontalruler/lib/horizontalruler-plugin.js +18 -3
  42. data/vendor/assets/javascripts/aloha/plugins/common/image/img/crop-buttons.gif +0 -0
  43. data/vendor/assets/javascripts/aloha/plugins/common/image/lib/image-plugin.js +1629 -1601
  44. data/vendor/assets/javascripts/aloha/plugins/common/image/vendor/jcrop/jquery.jcrop.css +11 -0
  45. data/vendor/assets/javascripts/aloha/plugins/common/link/extra/linklist.js +8 -6
  46. data/vendor/assets/javascripts/aloha/plugins/common/link/lib/link-plugin.js +26 -10
  47. data/vendor/assets/javascripts/aloha/plugins/common/list/nls/de/i18n.js +5 -1
  48. data/vendor/assets/javascripts/aloha/plugins/common/paste/lib/paste-plugin.js +3 -4
  49. data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-cell.js +13 -12
  50. data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-plugin.js +108 -61
  51. data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table-selection.js +61 -1
  52. data/vendor/assets/javascripts/aloha/plugins/common/table/lib/table.js +1 -0
  53. data/vendor/assets/javascripts/aloha/plugins/common/table/nls/de/i18n.js +28 -1
  54. data/vendor/assets/javascripts/aloha/plugins/common/table/nls/i18n.js +36 -10
  55. data/vendor/assets/javascripts/aloha/plugins/extra/browser/css/browser.jqgrid.css +292 -292
  56. data/vendor/assets/javascripts/aloha/plugins/extra/browser/lib/browser.js +28 -5
  57. data/vendor/assets/javascripts/aloha/plugins/extra/browser/lib/locale.js +2 -2
  58. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/grid.locale.de.js +6 -1
  59. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/grid.locale.en.js +5 -0
  60. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.jqGrid.js +5 -0
  61. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.jstree.js +6 -1
  62. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/jquery.ui.js +6 -1
  63. data/vendor/assets/javascripts/aloha/plugins/extra/browser/vendor/ui-layout.js +6 -1
  64. data/vendor/assets/javascripts/aloha/plugins/extra/cite/lib/cite-plugin.js +18 -4
  65. data/vendor/assets/javascripts/aloha/plugins/extra/formatlesspaste/lib/formatlesspaste-plugin.js +1 -1
  66. data/vendor/assets/javascripts/aloha/plugins/extra/linkbrowser/lib/linkbrowser-plugin.js +14 -2
  67. data/vendor/assets/javascripts/aloha/plugins/extra/numerated-headers/demo/js/aloha-config.js +2 -2
  68. data/vendor/assets/javascripts/aloha/plugins/extra/toc/lib/toc-plugin.js +382 -0
  69. data/vendor/assets/javascripts/aloha/plugins/extra/toc/nls/de/i18n.js +3 -0
  70. data/vendor/assets/javascripts/aloha/plugins/extra/toc/nls/i18n.js +4 -0
  71. metadata +15 -11
  72. data/vendor/assets/javascripts/aloha/plugins/extra/toc/i18n/de.json +0 -1
  73. data/vendor/assets/javascripts/aloha/plugins/extra/toc/i18n/en.json +0 -1
  74. data/vendor/assets/javascripts/aloha/plugins/extra/toc/src/toc.js +0 -350
@@ -14,13 +14,22 @@ function(jQuery, ContentHandlerManager, BlockManager) {
14
14
  * @class Special block content handler
15
15
  *
16
16
  * The blog content handler handles pasting of blocks in editables. Pasted
17
- * block markup will be replaced by a freshly rendered block instance.
17
+ * block markup will be replaced by a newly created block instance.
18
18
  */
19
19
  var BlockContentHandler = ContentHandlerManager.createHandler(
20
20
  /** @lends block.BlockContentHandler */
21
21
  {
22
22
  /**
23
23
  * Handle the pasting. Remove all unwanted stuff.
24
+ *
25
+ * There are two main cases which we need to distinguish:
26
+ * 1) Aloha Blocks are selected, and crtl+c is pressed then. In this case, *only the block* is copied / pasted.
27
+ * 2) Text is selected, but the selection also spans an aloha block.
28
+ *
29
+ * Generally, case 2) seems to work without bigger problems in Webkit / Firefox, while
30
+ * case 1) results in very much undesired and inconsistent behavior. If 1) happens,
31
+ * the property "data-aloha-block-copy-only-block" is set to "true"; so we can kick in and
32
+ * do additional cleanups.
24
33
  * @param {jQuery} content
25
34
  */
26
35
  handleContent: function( content ) {
@@ -30,27 +39,59 @@ function(jQuery, ContentHandlerManager, BlockManager) {
30
39
  content = jQuery( '<div>' ).append(content);
31
40
  }
32
41
 
42
+ if (content.find('.aloha-block[data-aloha-block-copy-only-block="true"]').length > 0) {
43
+ // We are in case 1; so some more cleanup is needed (at least in webkit and firefox).
44
+
45
+ // Webkit seems to *duplicate* the block when copying. The duplicated
46
+ // block has *no ID property* set, that's how we can find and discard it.
47
+ // Very ugly!
48
+ content.find('.aloha-block:not([id])').remove();
49
+ // Further cleanup for Webkit, removing empty nodes. Quite hacky!
50
+ content.find('.aloha-block + span:empty').remove();
51
+ content.find('div:empty').remove();
52
+ // (another) Hack for Webkit, removing superfluous BR
53
+ content.find('br.Apple-interchange-newline').remove();
54
+
55
+ // Firefox adds a <br> directly before the .aloha-block...
56
+ content.find('.aloha-block').prev('br').remove();
57
+
58
+ // Chrome (at least) sometimes adds an empty <br> inside an (otherwise empty) span
59
+ content.find('div > br:only-child').parent().remove();
60
+
61
+ }
62
+
33
63
  content.find('.aloha-block').each(function() {
34
64
  var oldBlock = jQuery(this);
35
65
 
36
- // TODO: use block.serialize();
66
+ var elementAttributes = {}; // all attributes except data-*
67
+ var blockAttributes = {}; // all data* attributes
68
+ jQuery.each(oldBlock[0].attributes, function(k, v) {
69
+ if (v.nodeName === 'id') return;
70
+
71
+ if (v.nodeName.match(/^data-/)) {
72
+ blockAttributes[v.nodeName.substr(5)] = v.nodeValue;
73
+ } else {
74
+ elementAttributes[v.nodeName] = v.nodeValue;
75
+ }
76
+ });
37
77
 
38
- var dataAttributes = {};
39
- jQuery.each(oldBlock.data(), function(k, v) {
40
- dataAttributes['data-' + k] = v;
41
- })
78
+ var newBlockId = GENTICS.Utils.guid();
42
79
 
43
80
  var newBlock = jQuery('<' + this.tagName + '/>')
44
- .attr(jQuery.extend({
45
- about: oldBlock.attr('about'),
46
- 'class': oldBlock.attr('class')
47
- }, dataAttributes))
48
- .removeClass('aloha-block-active');
81
+ .attr(elementAttributes)
82
+ .attr('id', newBlockId)
83
+ .removeClass('aloha-block-active')
84
+ .removeClass('aloha-block')
85
+ .html(oldBlock.html());
49
86
 
50
87
  oldBlock.replaceWith(newBlock);
51
- BlockManager._blockify(newBlock);
88
+
89
+ // We need to blockify the contents with a timeout, as we need the connected DOM node for it.
90
+ window.setTimeout(function() {
91
+ BlockManager._blockify(jQuery('#' + newBlockId), blockAttributes);
92
+ }, 50);
52
93
  });
53
-
94
+
54
95
  return content.html();
55
96
  }
56
97
  });
@@ -13,6 +13,8 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
13
13
  GENTICS = window.GENTICS;
14
14
 
15
15
  /**
16
+ * This is the block manager, which is the central entity for maintaining the lifecycle of blocks.
17
+ *
16
18
  * @name block.blockmanager
17
19
  * @class Block manager singleton
18
20
  */
@@ -32,8 +34,11 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
32
34
  * @param {AbstractBlock} the block to be deleted
33
35
  */
34
36
 
37
+ /**
38
+ * Default settings for blocks
39
+ */
35
40
  defaults: {
36
- 'block-type': 'DefaultBlock'
41
+ 'aloha-block-type': 'DefaultBlock'
37
42
  },
38
43
 
39
44
  /**
@@ -48,37 +53,47 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
48
53
  */
49
54
  blocks: null,
50
55
 
51
- activeBlocks: null,
56
+ /**
57
+ * Array of currently highlighted blocks
58
+ * @type Array
59
+ */
60
+ _highlightedBlocks: null,
61
+
62
+ /**
63
+ * Reference to the currently active block, if any
64
+ * @type AbstractBlock
65
+ */
66
+ _activeBlock: null,
52
67
 
68
+ /**************************
69
+ * SECTION: Initialization
70
+ **************************/
53
71
  /**
72
+ * Constructor. called immediately.
73
+ *
54
74
  * @constructor
55
75
  */
56
76
  _constructor: function() {
57
77
  FloatingMenu.createScope('Aloha.Block');
58
78
  this.blockTypes = new Registry();
59
79
  this.blocks = new Registry();
60
- this.activeBlocks = {};
80
+ this._highlightedBlocks = {};
61
81
  },
62
82
 
63
83
  /**
64
- * Register initial event handlers
84
+ * Register initial event handlers. Called from block-plugin when plugin
85
+ * is loaded.
65
86
  *
66
87
  * @private
67
88
  */
68
89
  registerEventHandlers: function() {
69
90
  var that = this;
91
+ this._registerEventHandlersForDeactivatingAlohaBlock();
92
+ this._registerEventHandlersForDeterminingCurrentlyActiveBlock();
93
+ this._registerEventHandlersForBlockDeletion();
94
+ this._registerEventHandlersForCutCopyPaste();
70
95
 
71
- // Register event handlers for deactivating an Aloha Block
72
- jQuery(document).bind('click', function(event) {
73
- if (that.activeBlocks == {}) return;
74
- if (jQuery(event.target).parents('.aloha-sidebar-bar, .aloha-block-do-not-deactivate, .aloha-floatingmenu').length > 0
75
- || jQuery(event.target).is('.aloha-sidebar-bar, .aloha-block-do-not-deactivate, .aloha-floatingmenu')) {
76
- // If we are inside the sidebar, the floating menu or other elements which should not trigger the block deactivation, we do an early return.
77
- return;
78
- }
79
- BlockManager._deactivateActiveBlocks();
80
- });
81
-
96
+ // TODO: not sure if we still need the code below. it is somehow related to caret handling
82
97
  Aloha.bind('aloha-selection-changed', function(evt, selection, originalEvent) {
83
98
  // the following line is needed to de-select blocks when navigating over them using the mouse cursors.
84
99
  // We only want to execute it though, if we are not inside a block, as it would otherwise
@@ -86,64 +101,276 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
86
101
  if (selection && jQuery(selection.getCommonAncestorContainer()).parents('.aloha-block').length > 0) {
87
102
  return;
88
103
  }
89
- that._deactivateActiveBlocks();
104
+ that._deactivateHighlightedBlocks();
90
105
  });
91
106
  },
92
107
 
93
108
  /**
94
- * Blockify a given element with the instance defaults
95
- * Directly called when one does jQuery.alohaBlock(instanceDefaults)
96
- *
97
- * @private
109
+ * Register the event handlers which deactivate aloha blocks when the user clicks outside a block
98
110
  */
99
- _blockify: function(element, instanceDefaults) {
100
- var attributes, block;
101
- element = jQuery(element);
111
+ _registerEventHandlersForDeactivatingAlohaBlock: function() {
112
+ var that = this;
102
113
 
103
- // TODO: check if object is already Block-ified
114
+ jQuery(document).bind('click', function(event) {
115
+ if (that._highlightedBlocks == {}) return;
116
+ if (jQuery(event.target).closest('.aloha-sidebar-bar, .aloha-block-do-not-deactivate, .aloha-floatingmenu, .aloha-block').length > 0) {
117
+ // If we are inside the sidebar, the floating menu or other elements which should not trigger the block deactivation, we do an early return.
118
+ return;
119
+ }
120
+ that._deactivateHighlightedBlocks();
121
+ });
122
+ },
123
+
124
+ /**
125
+ * Register the event handler which listens to block-selection-change, and
126
+ * sets _activeBlock accordingly.
127
+ */
128
+ _registerEventHandlersForDeterminingCurrentlyActiveBlock: function() {
129
+ var that = this;
130
+ this.bind('block-selection-change', function(highlightedBlocks) {
131
+ if (highlightedBlocks.length > 0) {
132
+ that._activeBlock = highlightedBlocks[0];
133
+ } else {
134
+ that._activeBlock = null;
135
+ }
136
+ });
137
+ },
104
138
 
105
- attributes = this.getConfig(element, instanceDefaults);
139
+ /**
140
+ * Implementation of block deletions, both when the block is the only selected element,
141
+ * and when the block is part of a bigger selection which should be deleted.
142
+ */
143
+ _registerEventHandlersForBlockDeletion: function() {
144
+ var that = this;
106
145
 
107
- element.contentEditable(false);
146
+ // This case executes in:
147
+ // - Chrome
148
+ // - Firefox
149
+ // - IE9
150
+ // - IE7+8 for inline blocks and for block-level blocks which are part of a bigger selection
151
+ // it does NOT execute in the following cases:
152
+ // - IE7+8 for block-level blocks which are NOT part of a bigger selection. This case is handled separately below.
153
+ Aloha.bind('aloha-command-will-execute', function(e, data) {
154
+ var commandId = data.commandId;
155
+
156
+ // Internet Explorer *magically* sets the range to the "Body" object after deselecting everything. yeah :-D
157
+ var onlyBlockSelected = (Aloha.getSelection().getRangeCount() === 0) // Firefox / Chrome
158
+ || (Aloha.getSelection().getRangeCount() === 1 && Aloha.getSelection().getRangeAt(0).endContainer === Aloha.getSelection().getRangeAt(0).startContainer && Aloha.getSelection().getRangeAt(0).endContainer === jQuery('body')[0]) // Internet explorer: Inline Elements
159
+ || (Aloha.getSelection().getRangeCount() === 1 && Aloha.getSelection().getRangeAt(0).endContainer === Aloha.getSelection().getRangeAt(0).startContainer && Aloha.getSelection().getRangeAt(0).startOffset + 1 === Aloha.getSelection().getRangeAt(0).endOffset); // Internet explorer: Block level elements
160
+
161
+ if (that._activeBlock && (commandId === 'delete' || commandId === 'forwarddelete') && onlyBlockSelected) {
162
+ // Deletion when a block is currently selected
163
+
164
+ // In this case, the default command shall not be executed.
165
+ data.preventDefault = true;
166
+ that._activeBlock.destroy();
167
+ } else if (!that._activeBlock && (commandId === 'delete' || commandId === 'forwarddelete') && Aloha.getSelection().getRangeCount() === 1 && Aloha.getSelection().getRangeAt(0).collapsed === false) {
168
+ // Deletion when a block is inside a bigger selection currently
169
+ // In this case, we check if we find an aloha-block. If yes, we delete it right away as the browser does not delete it correctly by default
170
+ var traverseSelectionTree;
171
+ traverseSelectionTree = function(selectionTree) {
172
+ var el;
173
+ for (var i=0, l=selectionTree.length; i<l; i++) {
174
+ el = selectionTree[i];
175
+ if (el.domobj.nodeType === 1) { // DOM node
176
+ var $el = jQuery(el.domobj);
177
+ if (el.selection === 'full' && $el.is('.aloha-block')) {
178
+ $el.remove();
179
+ } else {
180
+ traverseSelectionTree(el.children);
181
+ }
182
+ }
183
+ }
184
+ };
185
+ traverseSelectionTree(Aloha.Selection.getSelectionTree());
186
+ }
187
+ });
108
188
 
109
- if (!this.blockTypes.has(attributes['block-type'])) {
110
- Aloha.Log.error('block/blockmanager', 'Block Type ' + attributes['block-type'] + ' not found!');
111
- return;
112
- }
189
+ // - IE7/8 Workaround
190
+ // - deletion of blocks inside block collection
191
+ jQuery(window.document).keydown(function(e) {
113
192
 
114
- block = new (this.blockTypes.get(attributes['block-type']))(element);
115
- block.element.addClass('aloha-block-' + attributes['block-type']);
193
+ // Pressing DEL or BACKSPACE in a sidebar attribute editor form input
194
+ // causes the destruction of the block;
195
+ // if the keypress comes from a form element do nothing
196
+ if ( typeof e.srcElement !== 'undefined' && typeof e.srcElement.form !== 'undefined' ) {
197
+ return true;
198
+ }
116
199
 
117
- // Save attributes on block, but ignore jquery attribute.
118
- jQuery.each(attributes, function(k, v) {
119
- if (k.indexOf('jQuery') === 0) return;
120
- block.attr(k, v, true);
200
+ // If a block is active AND DEL or BACKSPACE key pressed, AND we are not inside a nested editable (FIX for IE7/8)
201
+ if (that._activeBlock && (e.which === 46 || e.which === 8) && that._activeBlock._isInsideNestedEditable === false) {
202
+ // ...and active block is INSIDE editable
203
+
204
+ // BROWSER QUIRK WORKAROUND
205
+ // - IE7+IE8 for block-level blocks which are NOT part of a bigger selection.
206
+ // TODO as we're going to remove Ext this browser checks should be made with jQuery
207
+ if ((Ext.isIE8 || Ext.isIE7) && that._activeBlock.$element.parents('.aloha-editable,.aloha-block').first().hasClass('aloha-editable')) {
208
+ that._activeBlock.destroy();
209
+ e.preventDefault();
210
+ return false;
211
+ } else if(that._activeBlock.shouldDestroy()) {
212
+ // .. in this case, the block should be destroyed because it
213
+ // is part of a block collection.
214
+
215
+ that._activeBlock.destroy();
216
+ e.preventDefault();
217
+ return false;
218
+ }
219
+ }
121
220
  });
221
+ },
122
222
 
123
- // Remove the attributes from the child element, as they have been moved to the parent element.
124
- jQuery.each(element.data(), function(k, v) {
125
- element.removeAttr('data-' + k);
223
+ /**
224
+ * Implementation of cut/copy; selecting the currently active block.
225
+ *
226
+ * When pasting, the blockcontenthandler is triggered. This takes care of the pasting process.
227
+ */
228
+ _registerEventHandlersForCutCopyPaste: function() {
229
+ var that = this,
230
+ currentlyCopying = false,
231
+ currentlyCutting = false,
232
+ selectionBeforeCopying = null;
233
+
234
+ jQuery(window.document).keydown(function(e) {
235
+ // IF: Ctrl/Command C pressed -- COPY
236
+ if (that._activeBlock && (e.ctrlKey || e.metaKey) && e.which === 67) {
237
+ currentlyCopying = true;
238
+ //selectionBeforeCopying = new GENTICS.Utils.RangeObject(true);
239
+ that._activeBlock.$element.attr('data-aloha-block-copy-only-block', 'true');
240
+ GENTICS.Utils.Dom.selectDomNode(that._activeBlock.$element[0]);
241
+ }
242
+
243
+ // IF: Ctrl/Command X pressed -- CUT
244
+ if (that._activeBlock && (e.ctrlKey || e.metaKey) && e.which === 88) {
245
+ currentlyCutting = true;
246
+ //selectionBeforeCopying = new GENTICS.Utils.RangeObject(true);
247
+ that._activeBlock.$element.attr('data-aloha-block-copy-only-block', 'true');
248
+ GENTICS.Utils.Dom.selectDomNode(that._activeBlock.$element[0]);
249
+ }
126
250
  });
127
- element.removeAttr('about');
251
+ jQuery(window.document).keyup(function(e) {
252
+ // IF: Release of ctrl / command C
253
+ if (!currentlyCutting && currentlyCopying && (e.which === 67 || e.which === 18 || e.which === 91)) {
254
+ currentlyCopying = false;
255
+ that._activeBlock.$element.removeAttr('data-aloha-block-copy-only-block');
256
+ if (selectionBeforeCopying) {
257
+ //selectionBeforeCopying.select();
258
+ selectionBeforeCopying = null;
259
+ }
260
+ }
261
+ // IF: Release of ctrl / command X
262
+ if (currentlyCutting && (e.which === 67 || e.which === 18 || e.which === 88)) {
263
+ currentlyCutting = false;
264
+ }
265
+ });
266
+ },
128
267
 
129
- // Register block
130
- this.blocks.register(block.getId(), block);
268
+ /**
269
+ * Initialize Block Level Drag/Drop for all editables. We need to do this
270
+ * inside the Block Manager, as we want all editables to become possible
271
+ * drop targets for block-level aloha blocks.
272
+ */
273
+ initializeBlockLevelDragDrop: function() {
274
+ var that = this;
275
+ jQuery.each(Aloha.editables, function(i, editable) {
276
+ that.createBlockLevelSortableForEditableOrBlockCollection(editable.obj);
277
+ });
278
+ Aloha.bind('aloha-editable-created', function(e, editable) {
279
+ that.createBlockLevelSortableForEditableOrBlockCollection(editable.obj);
280
+ });
281
+ },
131
282
 
132
- block._renderAndSetContent();
283
+ /**
284
+ * We make editables or block collections sortable using jQuery UI here, if we
285
+ * did not do this before.
286
+ *
287
+ * This is an internal method a user should never call!
288
+ */
289
+ createBlockLevelSortableForEditableOrBlockCollection: function($editableOrBlockCollection) {
290
+ if (!$editableOrBlockCollection.hasClass('aloha-block-blocklevel-sortable')) {
291
+
292
+ // We only want to make "block-level" aloha blocks sortable. According to the docs,
293
+ // sortable.cancel should have a CSS selector and if this matches, the element is only
294
+ // a drop target but NOT draggable. However, passing :not(.aloha-block) does not work somehow :-(
295
+ //
296
+ // Thus, we implemented the following alternative:
297
+ // Every "block-level" aloha block drag handle gets a new CSS class, and we only select this as
298
+ // drag handle. As only "block-level" aloha blocks have this CSS class, this will also only make
299
+ // aloha blocks draggable.
300
+ $editableOrBlockCollection.addClass('aloha-block-blocklevel-sortable').sortable({
301
+ revert: 100,
302
+ handle: '.aloha-block-draghandle-blocklevel',
303
+ connectWith: '.aloha-block-blocklevel-sortable' // we want to be able to drag an element to other editables
304
+ });
305
+
306
+
307
+ // Hack for Internet Explorer 8:
308
+ // If you first click inside an editable, and THEN want to drag a block-level block,
309
+ // it sometimes occurs that the *whole editable* is selected and should be dragged away.
310
+ // This breaks dragging of Aloha Blocks.
311
+ // Bugfix: We disable the "ondragstart" event on every editable.
312
+ // However, as the "ondragstart" is also fired when a nested (inline) editable is moved using drag/drop,
313
+ // we need to allow this case.
314
+ $editableOrBlockCollection.get(0).ondragstart = function (e, ui) {
315
+ if (!ui || !ui.helper || !ui.helper.is('.aloha-block')) {
316
+ // You tried to move something else than an aloha block
317
+ return false;
318
+ }
319
+ };
320
+ }
321
+ },
322
+
323
+ /**************************
324
+ * SECTION: Blockify / Block Access
325
+ **************************/
326
+
327
+ /**
328
+ * Register the given block type
329
+ *
330
+ * @param {String} Identifier
331
+ * @param {Class} A class that extends block.block.AbstractBlock
332
+ * @api
333
+ */
334
+ registerBlockType: function(identifier, blockType) {
335
+ FloatingMenu.createScope('Aloha.Block.' + identifier, 'Aloha.Block');
336
+ this.blockTypes.register(identifier, blockType);
133
337
  },
134
338
 
135
339
  /**
136
- * Deactivate all active blocks
340
+ * Blockify a given element with the instance defaults
341
+ * Directly called when one does jQuery.alohaBlock(instanceDefaults)
137
342
  *
138
343
  * @private
139
344
  */
140
- _deactivateActiveBlocks: function() {
141
- jQuery.each(jQuery.extend({}, this.activeBlocks), function(id) {
142
- var block = BlockManager.getBlock(id);
143
- if (block) {
144
- block.deactivate();
145
- }
345
+ _blockify: function(element, instanceDefaults) {
346
+ var attributes, block, $element;
347
+ $element = jQuery(element);
348
+
349
+ var tagName = $element[0].tagName.toLowerCase();
350
+ if (tagName !== 'span' && tagName !== 'div') {
351
+ Aloha.Log.error('block/blockmanager', 'Blocks can only be created from <div> or <span> element. You passed ' + tagName + '.');
352
+ return;
353
+ }
354
+
355
+ // TODO: check if object is already Block-ified
356
+
357
+ attributes = this.getConfig($element, instanceDefaults);
358
+
359
+ if (!this.blockTypes.has(attributes['aloha-block-type'])) {
360
+ Aloha.Log.error('block/blockmanager', 'Block Type ' + attributes['aloha-block-type'] + ' not found!');
361
+ return;
362
+ }
363
+
364
+ block = new (this.blockTypes.get(attributes['aloha-block-type']))($element);
365
+ block.$element.addClass('aloha-block-' + attributes['aloha-block-type']);
366
+ jQuery.each(attributes, function(k, v) {
367
+ // We use the private API here, as we need to be able to set internal properties as well, and we do not want to trigger renering.
368
+ block._setAttribute(k, v);
146
369
  });
370
+
371
+
372
+ // Register block
373
+ this.blocks.register(block.getId(), block);
147
374
  },
148
375
 
149
376
  /**
@@ -152,32 +379,33 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
152
379
  * @private
153
380
  */
154
381
  getConfig: function(blockElement, instanceDefaults) {
155
- // TODO: merge from plugin settings
156
- // TODO: What about double matches / overrides / multiple selectors applying?
157
- var settingsDefaults = {};
158
-
159
382
  return jQuery.extend(
160
383
  {},
161
384
  this.defaults,
162
- settingsDefaults,
163
385
  instanceDefaults,
164
- blockElement.data(),
165
- { // Override the "about" property
166
- about: blockElement.attr('about')
167
- }
386
+ blockElement.data()
168
387
  );
169
388
  },
170
389
 
171
390
  /**
172
- * Get a Block instance by id or DOM node
391
+ * Get a Block instance by id or DOM node. The DOM node can be either
392
+ * the DOM node of the wrapping element ($_element), the jQuery object of it,
393
+ * or the ID string.
173
394
  *
174
395
  * @param {String|DOMNode} idOrDomNode
175
396
  * @return {block.block.AbstractBlock} Block instance
397
+ * @api
176
398
  */
177
399
  getBlock: function(idOrDomNode) {
178
- var id;
400
+ var id, domNode;
179
401
  if (typeof idOrDomNode === 'object') {
180
- id = jQuery(idOrDomNode).attr('id');
402
+ domNode = jQuery(idOrDomNode);
403
+ if (domNode.hasClass('aloha-block-inner')) {
404
+ // We are at the inner block wrapper, so we have to go up one level,
405
+ // to find the block itself
406
+ domNode = domNode.parent();
407
+ }
408
+ id = domNode.attr('id');
181
409
  } else {
182
410
  id = idOrDomNode;
183
411
  }
@@ -186,7 +414,8 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
186
414
  },
187
415
 
188
416
  /**
189
- * Unregister (e.g. remove) the given block
417
+ * Unregister (e.g. remove) the given block. Do not call directly,
418
+ * instead use .destroy() on the block.
190
419
  *
191
420
  * @param {Object|String} blockOrBlockId Block or block id
192
421
  */
@@ -200,38 +429,46 @@ function(Aloha, jQuery, FloatingMenu, Observable, Registry) {
200
429
  this.blocks.unregister(blockOrBlockId);
201
430
  },
202
431
 
432
+
433
+ /**************************
434
+ * Internal helpers
435
+ **************************/
436
+
203
437
  /**
204
- * Register the given block type
438
+ * Deactivate all highlighted blocks
205
439
  *
206
- * @param {String} Identifier
207
- * @param {Class} A class that extends block.block.AbstractBlock
440
+ * @private
208
441
  */
209
- registerBlockType: function(identifier, blockType) {
210
- FloatingMenu.createScope('Aloha.Block.' + identifier, 'Aloha.Block');
211
- this.blockTypes.register(identifier, blockType);
442
+ _deactivateHighlightedBlocks: function() {
443
+ jQuery.each(jQuery.extend({}, this._highlightedBlocks), function(id) {
444
+ var block = BlockManager.getBlock(id);
445
+ if (block) {
446
+ block.deactivate();
447
+ }
448
+ });
212
449
  },
213
450
 
214
451
  /**
215
- * Get all active blocks indexed by block id
452
+ * Get all highlighted blocks indexed by block id
216
453
  *
217
454
  * @return {Object}
218
455
  */
219
- getActiveBlocks: function() {
220
- var activeBlocks = {};
456
+ _getHighlightedBlocks: function() {
457
+ var _highlightedBlocks = {};
221
458
  jQuery.each(this.blocks.getEntries(), function(blockId, block) {
222
459
  if (block.isActive()) {
223
- activeBlocks[blockId] = block;
460
+ _highlightedBlocks[blockId] = block;
224
461
  }
225
462
  });
226
- return activeBlocks;
463
+ return _highlightedBlocks;
227
464
  },
228
465
 
229
- _setActive: function(block) {
230
- this.activeBlocks[block.id] = true;
466
+ _setHighlighted: function(block) {
467
+ this._highlightedBlocks[block.id] = true;
231
468
  },
232
469
 
233
- _setInactive: function(block) {
234
- delete this.activeBlocks[block.id];
470
+ _setUnhighlighted: function(block) {
471
+ delete this._highlightedBlocks[block.id];
235
472
  }
236
473
  }))();
237
474