Created
August 27, 2012 15:27
-
-
Save wimleers/3489499 to your computer and use it in GitHub Desktop.
Edit -> use Create.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/edit.module b/edit.module | |
index d126f40..bc5de1a 100644 | |
--- a/edit.module | |
+++ b/edit.module | |
@@ -112,11 +112,32 @@ function edit_library() { | |
$wysiwyg_module = variable_get(EDIT_WYSIWYG_VARIABLE, EDIT_WYSIWYG_DEFAULT); | |
$path = drupal_get_path('module', 'edit'); | |
+ | |
+ $libraries['edit.createjs'] = array( | |
+ 'title' => 'CreateJS and deps', | |
+ 'website' => 'http://createjs.org', | |
+ 'version' => NULL, | |
+ 'js' => array( | |
+ libraries_get_path('underscore') . '/underscore-min.js' => array(), | |
+ libraries_get_path('backbone') . '/backbone-min.js' => array(), | |
+ drupal_get_path('module', 'edit') . '/js/vie.js' => array(), | |
+ drupal_get_path('module', 'jquery_update') . '/replace/ui/ui/jquery.ui.widget.js' => array(), | |
+ drupal_get_path('module', 'edit') . '/js/create.js' => array(), | |
+ // libraries_get_path('vie') .'/vie-min.js', | |
+ // libraries_get_path('create') .'/create-min.js', | |
+ ) | |
+ ); | |
$libraries['edit.frontend'] = array( | |
'title' => 'Edit: front-end', | |
'website' => 'http://drupal.org/project/edit', | |
'version' => NULL, // @TODO Figure out the correct way to do this. | |
'js' => array( | |
+ $path . '/js/editable.js' => array( | |
+ ), | |
+ $path . '/js/formwidget.js' => array( | |
+ ), | |
+ $path . '/js/views.js' => array( | |
+ ), | |
$path . '/js/edit.js' => array( | |
// @TODO Make sure that a subset of the JavaScript is loaded ASAP, so | |
// that the view/edit mode toggle works immediately. | |
@@ -154,6 +175,7 @@ function edit_library() { | |
array('system', 'jquery.form'), | |
array('system', 'drupal.form'), | |
array('system', 'drupal.ajax'), | |
+ array('edit', 'edit.createjs'), | |
), | |
); | |
diff --git a/js/ajax.js b/js/ajax.js | |
index 840bac7..2e02953 100644 | |
--- a/js/ajax.js | |
+++ b/js/ajax.js | |
@@ -110,7 +110,7 @@ $(function() { | |
} | |
// Make the freshly rendered field(s) in-place-editable again. | |
- Drupal.edit.startEditableFields(Drupal.edit.findEditableFields($parent)); | |
+ Drupal.edit.startEditableWidgets(Drupal.edit.util.findEditableFields($parent)); | |
} | |
}; | |
}); | |
diff --git a/js/edit.js b/js/edit.js | |
index 213e994..146e54e 100644 | |
--- a/js/edit.js | |
+++ b/js/edit.js | |
@@ -11,10 +11,8 @@ Drupal.behaviors.edit = { | |
$('#edit_view-edit-toggles').once('edit-init', Drupal.edit.init); | |
$('#edit_view-edit-toggles').once('edit-toggle', Drupal.edit.toggle.render); | |
- // TODO: remove this; this is to make the current prototype somewhat usable. | |
- $('a.edit_view-edit-toggle').click(function() { | |
- $(this).trigger('click.edit'); | |
- }); | |
+ // Remove URLs for the edit toggle links so we don't get redirects | |
+ $("a.edit_view-edit-toggle").attr('href', '#'); | |
} | |
}; | |
@@ -24,28 +22,29 @@ Drupal.edit.const.transitionEnd = "transitionEnd.edit webkitTransitionEnd.edit t | |
Drupal.edit.init = function() { | |
// VIE instance for Editing | |
Drupal.edit.vie = new VIE(); | |
- Drupal.edit.vie.use(new Drupal.edit.vie.RdfaService()); | |
- Drupal.edit.state = {}; | |
// We always begin in view mode. | |
- Drupal.edit.state.isViewing = true; | |
- Drupal.edit.state.entityBeingHighlighted = []; | |
- Drupal.edit.state.fieldBeingHighlighted = []; | |
- Drupal.edit.state.fieldBeingEdited = []; | |
- Drupal.edit.state.higlightedEditable = null; | |
- Drupal.edit.state.editedEditable = null; | |
- Drupal.edit.state.queues = {}; | |
- Drupal.edit.state.wysiwygReady = false; | |
- | |
- // Build inventory. | |
- var IDMapper = function() { return Drupal.edit.getID($(this)); }; | |
- Drupal.edit.state.entities = Drupal.edit.findEditableEntities().map(IDMapper); | |
- Drupal.edit.state.fields = Drupal.edit.findEditableFields().map(IDMapper); | |
- console.log('Entities:', Drupal.edit.state.entities.length, ';', Drupal.edit.state.entities); | |
- console.log('Fields:', Drupal.edit.state.fields.length, ';', Drupal.edit.state.fields); | |
+ Drupal.edit.state = { | |
+ isViewing: true, | |
+ entityBeingHighlighted: [], | |
+ fieldBeingHighlighted: [], | |
+ fieldBeingEdited: [], | |
+ higlightedEditable: null, | |
+ editedEditable: null, | |
+ queues: {}, | |
+ wysiwygReady: false | |
+ }; | |
+ | |
+ // Load the storage widget to get localStorage support | |
+ $('body').midgardStorage({ | |
+ vie: Drupal.edit.vie, | |
+ editableNs: 'createeditable' | |
+ }); | |
// Form preloader. | |
- Drupal.edit.state.queues.preload = Drupal.edit.findEditableFields().filter('.edit-type-form').map(IDMapper); | |
+ Drupal.edit.state.queues.preload = Drupal.edit.util.findEditableFields().filter('.edit-type-form').map(function () { | |
+ return Drupal.edit.util.getID($(this)); | |
+ }); | |
console.log('Fields with (server-generated) forms:', Drupal.edit.state.queues.preload); | |
// Initialize WYSIWYG, if any. | |
@@ -69,165 +68,95 @@ Drupal.edit.init = function() { | |
$('a.edit_view-edit-toggle.edit-' + (isViewing ? 'view' : 'edit')).parent().addClass('active'); | |
if (wasViewing && !isViewing) { | |
- $(Drupal.theme('editOverlay', {})) | |
- .appendTo('body') | |
- .addClass('edit-animate-slow edit-animate-invisible') | |
- .bind('click.edit', Drupal.edit.clickOverlay);; | |
- | |
- var $f = Drupal.edit.findEditableFields(); | |
- Drupal.edit.startEditableFields($f); | |
- var $e = Drupal.edit.findEditableEntities(); | |
- // Drupal.edit.startEditableEntities($e); | |
- | |
- // TODO: preload forms. We could do one request per form, but that's more | |
- // RTTs than needed. Instead, the server should support batch requests. | |
- console.log('Preloading forms that we might need!', Drupal.edit.state.queues.preload); | |
- | |
- // Animations. | |
- $('#edit_overlay').css('top', $('#navbar').outerHeight()); | |
- $('#edit_overlay').removeClass('edit-animate-invisible'); | |
- | |
- // Disable contextual links in edit mode. | |
- $('.contextual-links-region') | |
- .addClass('edit-contextual-links-region') | |
- .removeClass('contextual-links-region'); | |
- } | |
- else if (!wasViewing && isViewing) { | |
- // Animations. | |
- $('#edit_overlay') | |
- .addClass('edit-animate-invisible') | |
- .bind(Drupal.edit.const.transitionEnd, function(e) { | |
- $('#edit_overlay, .edit-form-container, .edit-toolbar-container, #edit_modal, #edit_backstage, .edit-curtain').remove(); | |
- }); | |
- | |
- var $f = Drupal.edit.findEditableFields(); | |
- Drupal.edit.stopEditableFields($f); | |
- var $e = Drupal.edit.findEditableEntities(); | |
- Drupal.edit.stopEditableEntities($e); | |
- | |
- // Re-enable contextual links in view mode. | |
- $('.edit-contextual-links-region') | |
- .addClass('contextual-links-region') | |
- .removeClass('edit-contextual-links-region'); | |
- } | |
- else { | |
- // No state change. | |
+ Drupal.edit.enterEditState(); | |
+ } else if (!wasViewing && isViewing) { | |
+ Drupal.edit.enterViewState(); | |
} | |
return false; | |
}); | |
}; | |
-Drupal.edit.findEditableEntities = function(context) { | |
- return Drupal.edit.vie.service('rdfa').readEntities(context || Drupal.settings.edit.context); | |
-}; | |
- | |
-Drupal.edit.findEditableFields = function(context) { | |
- var entities = Drupal.edit.findEditableEntities(context); | |
- var fields = []; | |
- _.each(entities, function (entity) { | |
- fields.push(Drupal.edit.vie.service('rdfa').findPredicateElements(entity.getSubject(), context)); | |
- }); | |
- return fields; | |
-}; | |
+Drupal.edit.enterEditState = function () { | |
+ $(Drupal.theme('editOverlay', {})) | |
+ .appendTo('body') | |
+ .addClass('edit-animate-slow edit-animate-invisible') | |
+ .bind('click.edit', Drupal.edit.clickOverlay); | |
-/* | |
- * findEditableFields() just looks for fields that are editable, i.e. for the | |
- * field *wrappers*. Depending on the field, however, either the whole field wrapper | |
- * will be marked as editable (in this case, an inline form will be used for editing), | |
- * *or* a specific (field-specific even!) DOM element within that field wrapper will be | |
- * marked as editable. | |
- * This function is for finding the *editables* themselves, given the *editable fields*. | |
- */ | |
-Drupal.edit.findEditablesForFields = function($fields) { | |
- var $editables = $(); | |
+ var $e = Drupal.edit.util.findEditableFields(); | |
+ Drupal.edit.startEditableWidgets($e); | |
- // type = form | |
- $editables = $editables.add($fields.filter('.edit-type-form')); | |
+ // TODO: preload forms. We could do one request per form, but that's more | |
+ // RTTs than needed. Instead, the server should support batch requests. | |
+ console.log('Preloading forms that we might need!', Drupal.edit.state.queues.preload); | |
- // type = direct | |
- var $direct = $fields.filter('.edit-type-direct'); | |
- $editables = $editables.add($direct.find('.field-item')); | |
- // Edge case: "title" pseudofield on pages with lists of nodes. | |
- $editables = $editables.add($direct.filter('h2').find('a')); | |
- // Edge case: "title" pseudofield on node pages. | |
- $editables = $editables.add($direct.find('h1')); | |
+ // Animations. | |
+ $('#edit_overlay').css('top', $('#navbar').outerHeight()); | |
+ $('#edit_overlay').removeClass('edit-animate-invisible'); | |
- return $editables; | |
+ // Disable contextual links in edit mode. | |
+ $('.contextual-links-region') | |
+ .addClass('edit-contextual-links-region') | |
+ .removeClass('contextual-links-region'); | |
}; | |
-Drupal.edit.getID = function($field) { | |
- return $field.data('edit-id'); | |
-}; | |
+Drupal.edit.enterViewState = function () { | |
+ // Animations. | |
+ $('#edit_overlay') | |
+ .addClass('edit-animate-invisible') | |
+ .bind(Drupal.edit.const.transitionEnd, function(e) { | |
+ $('#edit_overlay, .edit-form-container, .edit-toolbar-container, #edit_modal, #edit_backstage, .edit-curtain').remove(); | |
+ }); | |
-Drupal.edit.findFieldForID = function(id, context) { | |
- return $('[data-edit-id="' + id + '"]', context || $('#content')); | |
-}; | |
+ var $e = Drupal.edit.util.findEditableFields(); | |
+ Drupal.edit.stopEditableWidgets($e); | |
-Drupal.edit.findFieldForEditable = function($editable) { | |
- return $editable.filter('.edit-type-form').length ? $editable : $editable.closest('.edit-type-direct'); | |
+ // Re-enable contextual links in view mode. | |
+ $('.edit-contextual-links-region') | |
+ .addClass('contextual-links-region') | |
+ .removeClass('edit-contextual-links-region'); | |
}; | |
-Drupal.edit.findEntityForField = function($f) { | |
- var $e = $f.closest('.edit-entity'); | |
- if ($e.length == 0) { | |
- var entity_edit_id = $f.data('edit-id').split(':').slice(0,2).join(':'); | |
- $e = $('.edit-entity[data-edit-id="' + entity_edit_id + '"]'); | |
- } | |
- return $e; | |
-}; | |
+Drupal.edit.startEditableWidgets = function($fields) { | |
+ var self = this; | |
-Drupal.edit.findEntityForEditable = function($editable) { | |
- return Drupal.edit.findEntityForField(Drupal.edit.findFieldForEditable($editable)); | |
-}; | |
+ var enabler = function () { | |
+ if (Drupal.edit.state.isViewing) { | |
+ $(this).unbind('click', enabler); | |
+ return; | |
+ } | |
-Drupal.edit.startEditableEntities = function($e) { | |
- $e | |
- .once('edit') | |
- .addClass('edit-animate-fast') | |
- .addClass('edit-candidate edit-editable') | |
- .bind('mouseenter.edit', function(e) { | |
- var $e = $(this); | |
- Drupal.edit.util.ignoreHoveringVia(e, '.edit-toolbar-container', function() { | |
- if (Drupal.edit.state.fieldBeingEdited.length > 0) { | |
- return; | |
- } | |
+ // Make the fields editable | |
+ Drupal.edit.editables.startEdit($(this)); | |
+ return false; | |
+ }; | |
+ | |
+ $fields | |
+ .each(function() { | |
+ var $field = jQuery(this); | |
- console.log('entity:mouseenter'); | |
- Drupal.edit.entityEditables.startHighlight($e); | |
+ $field.bind('createeditableenable', function (event, data) { | |
+ $field.unbind('click.edit', enabler); | |
+ Drupal.edit.editables._updateDirectEditable($field); | |
}); | |
- }) | |
- .bind('mouseleave.edit', function(e) { | |
- var $e = $(this); | |
- Drupal.edit.util.ignoreHoveringVia(e, '.edit-toolbar-container', function() { | |
- console.log('entity:mouseleave'); | |
- Drupal.edit.entityEditables.stopHighlight($e); | |
+ | |
+ $field.bind('createeditabledisable', function (event, data) { | |
+ $field.bind('click.edit', enabler); | |
+ $field.removeClass('ui-state-disabled'); | |
+ Drupal.edit.editables._restoreDirectEditable($field); | |
+ }); | |
+ | |
+ var entity = Drupal.edit.util.getElementEntity(this, Drupal.edit.vie); | |
+ $field.createEditable({ | |
+ model: entity, | |
+ vie: Drupal.edit.vie, | |
+ disabled: true | |
}); | |
- }) | |
- // Hang a curtain over the comments if they're inside the entity. | |
- .find('.comment-wrapper').prepend(Drupal.theme('editCurtain', {})) | |
- .map(function() { | |
- var height = $(this).height(); | |
- $(this).find('.edit-curtain') | |
- .css('height', height) | |
- .data('edit-curtain-height', height); | |
}); | |
-}; | |
-Drupal.edit.stopEditableEntities = function($e) { | |
- $e | |
- .removeClass('edit-processed edit-candidate edit-editable edit-highlighted') | |
- .unbind('mouseenter.edit mouseleave.edit') | |
- .find('.comment-wrapper .edit-curtain').remove(); | |
+ Drupal.edit.decorateEditables(Drupal.edit.util.findEditablesForFields($fields)); | |
}; | |
-Drupal.edit.startEditableFields = function($fields) { | |
- var $fields = $fields.once('edit'); | |
- // Ignore fields that need a WYSIWYG editor if no WYSIWYG editor is present | |
- if (!Drupal.settings.edit.wysiwyg) { | |
- $fields = $fields.filter(':not(.edit-type-direct-with-wysiwyg)'); | |
- } | |
- var $editables = Drupal.edit.findEditablesForFields($fields); | |
- | |
+Drupal.edit.decorateEditables = function($editables) { | |
$editables | |
.addClass('edit-animate-fast') | |
.addClass('edit-candidate edit-editable') | |
@@ -250,15 +179,19 @@ Drupal.edit.startEditableFields = function($fields) { | |
Drupal.edit.editables.stopHighlight($editable); | |
// Leaving a field won't trigger the mouse enter event for the entity | |
// because the entity contains the field. Hence, do it manually. | |
- var $e = Drupal.edit.findEntityForEditable($editable); | |
+ var $e = Drupal.edit.util.findEntityForEditable($editable); | |
Drupal.edit.entityEditables.startHighlight($e); | |
} | |
// Prevent triggering the entity's mouse leave event. | |
e.stopPropagation(); | |
}); | |
}) | |
- .bind('click.edit', function() { | |
- Drupal.edit.editables.startEdit($(this)); return false; | |
+ .each(function() { | |
+ var editableView = new Drupal.edit.views.EditableView({ | |
+ model: Drupal.edit.util.getElementEntity(jQuery(this), Drupal.edit.vie), | |
+ el: jQuery(this), | |
+ predicate: Drupal.edit.util.getElementPredicate(jQuery(this)) | |
+ }); | |
}) | |
// Some transformations are editable-specific. | |
.map(function() { | |
@@ -266,17 +199,17 @@ Drupal.edit.startEditableFields = function($fields) { | |
}); | |
}; | |
-Drupal.edit.stopEditableFields = function($fields) { | |
- var $editables = Drupal.edit.findEditablesForFields($fields); | |
+Drupal.edit.stopEditableWidgets = function($fields) { | |
+ var $editables = Drupal.edit.util.findEditablesForFields($fields); | |
$fields | |
.removeClass('edit-processed'); | |
+ | |
+ Drupal.edit.editables.stopEdit($fields); | |
$editables | |
.removeClass('edit-candidate edit-editable edit-highlighted edit-editing edit-belowoverlay') | |
.unbind('mouseenter.edit mouseleave.edit click.edit edit-content-changed.edit') | |
- .removeAttr('contenteditable') | |
- .removeData(['edit-content-original', 'edit-content-changed']); | |
}; | |
Drupal.edit.clickOverlay = function(e) { | |
@@ -301,7 +234,6 @@ Drupal.edit.clickOverlay = function(e) { | |
// Entity editables. | |
Drupal.edit.entityEditables = { | |
startHighlight: function($editable) { | |
- return; | |
console.log('entityEditables.startHighlight'); | |
if (Drupal.edit.toolbar.create($editable)) { | |
var label = Drupal.t('Edit !entity', { '!entity': $editable.data('edit-entity-label') }); | |
@@ -337,7 +269,6 @@ Drupal.edit.entityEditables = { | |
}, | |
stopHighlight: function($editable) { | |
- return; | |
console.log('entityEditables.stopHighlight'); | |
// Animations. | |
@@ -353,7 +284,7 @@ Drupal.edit.editables = { | |
startHighlight: function($editable) { | |
console.log('editables.startHighlight'); | |
if (Drupal.edit.state.entityBeingHighlighted.length > 0) { | |
- var $e = Drupal.edit.findEntityForEditable($editable); | |
+ var $e = Drupal.edit.util.findEntityForEditable($editable); | |
Drupal.edit.entityEditables.stopHighlight($e); | |
} | |
if (Drupal.edit.toolbar.create($editable)) { | |
@@ -382,7 +313,7 @@ Drupal.edit.editables = { | |
}, 0); | |
Drupal.edit.state.fieldBeingHighlighted = $editable; | |
- Drupal.edit.state.higlightedEditable = Drupal.edit.getID(Drupal.edit.findFieldForEditable($editable)); | |
+ Drupal.edit.state.higlightedEditable = Drupal.edit.util.getID(Drupal.edit.util.findFieldForEditable($editable)); | |
}, | |
stopHighlight: function($editable) { | |
@@ -399,14 +330,14 @@ Drupal.edit.editables = { | |
Drupal.edit.state.highlightedEditable = null; | |
}, | |
- startEdit: function($editable) { | |
+ startEdit: function($field) { | |
+ $editable = Drupal.edit.util.findEditablesForFields($field); | |
if ($editable.hasClass('edit-editing')) { | |
return; | |
} | |
console.log('editables.startEdit: ', $editable); | |
var self = this; | |
- var $field = Drupal.edit.findFieldForEditable($editable); | |
// Highlight if not already highlighted. | |
if (Drupal.edit.state.fieldBeingHighlighted[0] != $editable[0]) { | |
@@ -427,7 +358,7 @@ Drupal.edit.editables = { | |
$('.edit-candidate').not('.edit-editing').removeClass('edit-editable'); | |
// Hide the curtain while editing, the above already prevents comments from | |
// showing up. | |
- Drupal.edit.findEntityForField($field).find('.comment-wrapper .edit-curtain').height(0); | |
+ Drupal.edit.util.findEntityForField($field).find('.comment-wrapper .edit-curtain').height(0); | |
// Toolbar (already created in the highlight). | |
Drupal.edit.toolbar.get($editable) | |
@@ -447,24 +378,22 @@ Drupal.edit.editables = { | |
return self._buttonFieldCloseClicked(e, $editable, $field); | |
}); | |
- // Changes to $editable based on the type. | |
- var callback = ($field.hasClass('edit-type-direct')) | |
- ? self._updateDirectEditable | |
- : self._updateFormEditable; | |
- callback($editable); | |
+ // Start the editable widget | |
+ $field.createEditable({disabled: false}); | |
// Regardless of the type, load the form for this field. We always use forms | |
// to submit the changes. | |
+ // FIXME: This should be handled by Backbone.sync | |
self._loadForm($editable, $field); | |
Drupal.edit.state.fieldBeingEdited = $editable; | |
- Drupal.edit.state.editedEditable = Drupal.edit.getID($field); | |
+ Drupal.edit.state.editedEditable = Drupal.edit.util.getID($field); | |
}, | |
- stopEdit: function($editable) { | |
+ stopEdit: function($field) { | |
+ $editable = Drupal.edit.util.findEditablesForFields($field); | |
console.log('editables.stopEdit: ', $editable); | |
var self = this; | |
- var $field = Drupal.edit.findFieldForEditable($editable); | |
if ($editable.length == 0) { | |
return; | |
} | |
@@ -479,15 +408,12 @@ Drupal.edit.editables = { | |
// Make the other fields and entities editable again. | |
$('.edit-candidate').addClass('edit-editable'); | |
// Restore curtain to original height. | |
- var $curtain = Drupal.edit.findEntityForEditable($editable) | |
+ var $curtain = Drupal.edit.util.findEntityForEditable($editable) | |
.find('.comment-wrapper .edit-curtain'); | |
$curtain.height($curtain.data('edit-curtain-height')); | |
- // Changes to $editable based on the type. | |
- var callback = ($field.hasClass('edit-type-direct')) | |
- ? self._restoreDirectEditable | |
- : self._restoreFormEditable; | |
- callback($editable); | |
+ // Start the editable widget | |
+ $field.createEditable({disabled: true}); | |
Drupal.edit.toolbar.remove($editable); | |
Drupal.edit.form.remove($editable); | |
@@ -500,7 +426,7 @@ Drupal.edit.editables = { | |
// Indicate in the 'info' toolgroup that the form is loading. | |
Drupal.edit.toolbar.addClass($editable, 'primary', 'info', 'loading'); | |
- var edit_id = Drupal.edit.getID($field); | |
+ var edit_id = Drupal.edit.util.getID($field); | |
var element_settings = { | |
url : Drupal.edit.util.calcRerenderProcessedTextURL(edit_id), | |
event : 'edit-internal-load-rerender.edit', | |
@@ -520,16 +446,14 @@ Drupal.edit.editables = { | |
// Attach, activate and show the WYSIWYG editor. | |
_wysiwygify: function($editable) { | |
$editable.addClass('edit-wysiwyg-attached'); | |
- Drupal.edit.wysiwyg[Drupal.settings.edit.wysiwyg].attach($editable); | |
- Drupal.edit.wysiwyg[Drupal.settings.edit.wysiwyg].activate($editable); | |
Drupal.edit.toolbar.show($editable, 'secondary', 'wysiwyg-tabs'); | |
Drupal.edit.toolbar.show($editable, 'tertiary', 'wysiwyg'); | |
}, | |
- _updateDirectEditable: function($editable) { | |
+ _updateDirectEditable: function($field) { | |
+ $editable = Drupal.edit.util.findEditablesForFields($field); | |
Drupal.edit.editables._padEditable($editable); | |
- var $field = Drupal.edit.findFieldForEditable($editable); | |
if ($field.hasClass('edit-type-direct-with-wysiwyg')) { | |
Drupal.edit.toolbar.get($editable) | |
.find('.edit-toolbar.secondary:not(:has(.edit-toolgroup.wysiwyg-tabs))') | |
@@ -549,8 +473,6 @@ Drupal.edit.editables = { | |
// it without the transformation filters. | |
if ($field.hasClass('edit-text-with-transformation-filters')) { | |
Drupal.edit.editables._loadRerenderedProcessedText($editable, $field); | |
- // Also store the "real" original content, i.e. the transformed one. | |
- $editable.data('edit-content-original-transformed', $editable.html()) | |
} | |
// When no transformation filters have been applied: start WYSIWYG editing | |
// immediately! | |
@@ -560,70 +482,27 @@ Drupal.edit.editables = { | |
}, 0); | |
} | |
} | |
- else { | |
- $editable.attr('contenteditable', true); | |
- } | |
$editable | |
- .data('edit-content-original', $editable.html()) | |
.data('edit-content-changed', false); | |
- // Detect content changes ourselves only when not using a WYSIWYG editor. | |
- var markContentChanged = function() { | |
+ $field.bind('createeditablechanged', function() { | |
$editable.data('edit-content-changed', true); | |
$editable.trigger('edit-content-changed.edit'); | |
- }; | |
- if (!$field.hasClass('edit-type-direct-with-wysiwyg')) { | |
- // We cannot use Drupal.behaviors.formUpdated here because we're not dealing | |
- // with a form! | |
- $editable | |
- .bind('blur.edit keyup.edit paste.edit', function() { | |
- if ($editable.html() != $editable.data('edit-content-original')) { | |
- markContentChanged(); | |
- } | |
- }) | |
- // Disallow return/enter key when editing titles. | |
- .bind('keypress.edit', function(event) { | |
- if (event.keyCode == 13) { | |
- return false; | |
- } | |
- }); | |
- } | |
- else { | |
- $editable.bind('edit-wysiwyg-content-changed.edit', function() { | |
- markContentChanged(); | |
- }); | |
- } | |
+ }); | |
}, | |
- _restoreDirectEditable: function($editable) { | |
- if (Drupal.edit.findFieldForEditable($editable).hasClass('edit-type-direct-with-wysiwyg') | |
+ _restoreDirectEditable: function($field) { | |
+ $editable = Drupal.edit.util.findEditablesForFields($field); | |
+ if (Drupal.edit.util.findFieldForEditable($editable).hasClass('edit-type-direct-with-wysiwyg') | |
&& $editable.hasClass('edit-wysiwyg-attached')) | |
{ | |
$editable.removeClass('edit-wysiwyg-attached'); | |
- Drupal.edit.wysiwyg[Drupal.settings.edit.wysiwyg].detach($editable); | |
- | |
- // Work-around for major AE bug. See: | |
- // - http://drupal.org/node/1725032 | |
- // - https://github.com/alohaeditor/Aloha-Editor/issues/693. | |
- // Also unbind to make sure this doesn't break anything when using | |
- // this version of edit.js with a fixed version of Aloha Editor. | |
- $editable | |
- .unbind('click.edit') | |
- .bind('click.edit', function() { | |
- Drupal.edit.editables.startEdit($(this)); return false; | |
- }); | |
- } | |
- else { | |
- $editable | |
- .removeAttr('contenteditable') | |
- .unbind('keypress.edit'); | |
} | |
Drupal.edit.editables._unpadEditable($editable); | |
$editable | |
- .removeData(['edit-content-original', 'edit-content-changed', 'edit-content-original-transformed']) | |
.unbind('blur.edit keyup.edit paste.edit edit-content-changed.edit'); | |
// Not only clean up the changes to $editable, but also clean up the | |
@@ -744,28 +623,8 @@ Drupal.edit.editables = { | |
}, 0); | |
}, | |
- // Creates a form container; when the $editable is inline, it will inherit CSS | |
- // properties from the toolbar container, so the toolbar must already exist. | |
- _updateFormEditable: function($editable) { | |
- if (Drupal.edit.form.create($editable)) { | |
- $editable | |
- .addClass('edit-belowoverlay') | |
- .removeClass('edit-highlighted edit-editable'); | |
- | |
- Drupal.edit.form.get($editable) | |
- .find('.edit-form') | |
- .addClass('edit-editable edit-highlighted edit-editing') | |
- .css('background-color', $editable.data('edit-background-color')); | |
- } | |
- }, | |
- | |
- _restoreFormEditable: function($editable) { | |
- // No need to do anything here; all of the field HTML will be overwritten | |
- // with the freshly rendered version from the server anyway! | |
- }, | |
- | |
_loadForm: function($editable, $field) { | |
- var edit_id = Drupal.edit.getID($field); | |
+ var edit_id = Drupal.edit.util.getID($field); | |
var element_settings = { | |
url : Drupal.edit.util.calcFormURLForField(edit_id), | |
event : 'edit-internal.edit', | |
@@ -797,25 +656,13 @@ Drupal.edit.editables = { | |
else if ($field.hasClass('edit-type-direct')) { | |
$editable.blur(); | |
- var wysiwyg = $field.hasClass('edit-type-direct-with-wysiwyg') | |
- && $editable.hasClass('edit-wysiwyg-attached'); | |
+ var entity = Drupal.edit.util.getElementEntity($field, Drupal.edit.vie); | |
+ var value = entity.get(Drupal.edit.util.getElementPredicate($editable)); | |
- // When using WYSIWYG editing, first detach the WYSIWYG editor to ensure | |
- // the content has been cleaned up before saving it. (Otherwise, | |
- // annotations and infrastructure created by the WYSIWYG editor could also | |
- // get saved). | |
- if (wysiwyg) { | |
- $editable.removeClass('edit-wysiwyg-attached'); | |
- Drupal.edit.wysiwyg[Drupal.settings.edit.wysiwyg].detach($editable); | |
- } | |
+ // TODO: Use Backbone.sync so we can support the Drupal 8 API | |
+ // without code changes in Spark | |
+ // entity.save(); | |
- // We trim the title because otherwise whitespace in the raw HTML ends | |
- // up in the title as well. | |
- // TRICKY: Drupal core does not trim the title, so in theory this is | |
- // out of line with Drupal core's behavior. | |
- var value = (wysiwyg) | |
- ? $.trim($editable.html()) | |
- : $.trim($editable.text()); | |
$('#edit_backstage form') | |
.find(':input[type!="hidden"][type!="submit"]').val(value).end() | |
.find('.edit-form-submit').trigger('click.edit'); | |
@@ -824,20 +671,12 @@ Drupal.edit.editables = { | |
}, | |
_buttonFieldCloseClicked: function(e, $editable, $field) { | |
- // Content not changed: stop editing field. | |
- if (!$editable.data('edit-content-changed')) { | |
- // Restore to original content. When dealing with processed text, it's | |
- // possible that one or more transformation filters are used. Then, the | |
- // "real" original content (i.e. the transformed one) is stored separately | |
- // from the "original content" that we use to detect changes. | |
- if (typeof $editable.data('edit-content-original-transformed') !== 'undefined') { | |
- $editable.html($editable.data('edit-content-original-transformed')); | |
- } | |
- | |
- Drupal.edit.editables.stopEdit($editable); | |
- } | |
- // Content changed: show modal. | |
- else { | |
+ if (!Drupal.edit.util.getElementEntity($field, Drupal.edit.vie).hasChanged()) { | |
+ // Content not changed: stop editing field. | |
+ // The view will restore contents automatically when we disable editor | |
+ Drupal.edit.editables.stopEdit($field); | |
+ } else { | |
+ // Content changed: show modal. | |
Drupal.edit.modal.create( | |
Drupal.t('You have unsaved changes'), | |
Drupal.theme('editButtons', { 'buttons' : [ | |
diff --git a/js/editable.js b/js/editable.js | |
new file mode 100644 | |
index 0000000..173feac | |
--- /dev/null | |
+++ b/js/editable.js | |
@@ -0,0 +1,47 @@ | |
+(function (jQuery, undefined) { | |
+ // # Create.js editing widget for Spark | |
+ // | |
+ // This widget inherits from the Create.js editable widget to accommodate | |
+ // for the fact that Spark is using custom data attributes and not RDFa | |
+ // to communicate editable fields. | |
+ jQuery.widget('Drupal.createEditable', jQuery.Midgard.midgardEditable, { | |
+ _create: function () { | |
+ this.vie = this.options.vie; | |
+ | |
+ this.options.editors.direct = { | |
+ widget: 'editWidget', | |
+ options: {} | |
+ }; | |
+ this.options.editors.directWysiwyg = { | |
+ widget: 'alohaWidget', | |
+ options: {} | |
+ }; | |
+ this.options.editors.form = { | |
+ widget: 'drupalFormWidget', | |
+ options: {} | |
+ }; | |
+ }, | |
+ | |
+ findEditableElements: function (callback) { | |
+ var model = this.options.model; | |
+ var fields = Drupal.edit.util.findEditableFields(this.element).andSelf().filter(function () { | |
+ return Drupal.edit.util.getElementSubject(jQuery(this)) == model.getSubjectUri(); | |
+ }); | |
+ Drupal.edit.util.findEditablesForFields(fields).each(callback); | |
+ }, | |
+ | |
+ getElementPredicate: function (element) { | |
+ return Drupal.edit.util.getElementPredicate(jQuery(element)); | |
+ }, | |
+ | |
+ _editorName: function (data) { | |
+ if (Drupal.settings.edit.wysiwyg && jQuery(this.element).hasClass('edit-type-direct')) { | |
+ if (jQuery(this.element).hasClass('edit-type-direct-with-wysiwyg')) { | |
+ return 'directWysiwyg'; | |
+ } | |
+ return 'direct'; | |
+ } | |
+ return 'form'; | |
+ } | |
+ }); | |
+})(jQuery); | |
diff --git a/js/formwidget.js b/js/formwidget.js | |
new file mode 100644 | |
index 0000000..3586e05 | |
--- /dev/null | |
+++ b/js/formwidget.js | |
@@ -0,0 +1,47 @@ | |
+(function (jQuery, undefined) { | |
+ // # Drupal form-based editing widget for Create.js | |
+ jQuery.widget('Drupal.drupalFormWidget', jQuery.Create.editWidget, { | |
+ options: { | |
+ editorOptions: {}, | |
+ disabled: true | |
+ }, | |
+ | |
+ enable: function () { | |
+ this.options.disabled = false; | |
+ this.loadForm(); | |
+ }, | |
+ | |
+ loadForm: function () { | |
+ if (Drupal.edit.form.create(this.element)) { | |
+ this.element | |
+ .addClass('edit-belowoverlay') | |
+ .removeClass('edit-highlighted edit-editable'); | |
+ | |
+ Drupal.edit.form.get(this.element) | |
+ .find('.edit-form') | |
+ .addClass('edit-editable edit-highlighted edit-editing') | |
+ .css('background-color', this.element.data('edit-background-color')); | |
+ } | |
+ | |
+ var field = Drupal.edit.util.findFieldForEditable(this.element); | |
+ //Drupal.edit.editables._loadForm(this.element, field); | |
+ }, | |
+ | |
+ disable: function () { | |
+ this.options.disabled = true; | |
+ | |
+ Drupal.edit.form.get(this.element).remove(); | |
+ }, | |
+ | |
+ _initialize: function () { | |
+ var self = this; | |
+ jQuery(this.element).bind('focus', function (event) { | |
+ self.options.activated(); | |
+ }); | |
+ | |
+ jQuery(this.element).bind('blur', function (event) { | |
+ self.options.deactivated(); | |
+ }); | |
+ } | |
+ }); | |
+})(jQuery); | |
diff --git a/js/ui-editables.js b/js/ui-editables.js | |
index ac14cd0..21f15f8 100644 | |
--- a/js/ui-editables.js | |
+++ b/js/ui-editables.js | |
@@ -134,9 +134,7 @@ Drupal.edit.toolbar = { | |
}, | |
_id: function($editable) { | |
- var edit_id = ($editable.hasClass('edit-entity')) | |
- ? Drupal.edit.getID($editable) | |
- : Drupal.edit.getID(Drupal.edit.findFieldForEditable($editable)); | |
+ var edit_id = Drupal.edit.util.getID($editable); | |
return 'edit-toolbar-for-' + edit_id.split(':').join('_'); | |
} | |
}; | |
@@ -190,9 +188,7 @@ Drupal.edit.form = { | |
}, | |
_id: function($editable) { | |
- var edit_id = ($editable.hasClass('edit-entity')) | |
- ? Drupal.edit.getID($editable) | |
- : Drupal.edit.getID(Drupal.edit.findFieldForEditable($editable)); | |
+ var edit_id = Drupal.edit.util.getID($editable); | |
return 'edit-form-for-' + edit_id.split(':').join('_'); | |
} | |
}; | |
diff --git a/js/util.js b/js/util.js | |
index 32ba314..6141dd6 100644 | |
--- a/js/util.js | |
+++ b/js/util.js | |
@@ -8,6 +8,112 @@ | |
Drupal.edit = Drupal.edit || {}; | |
Drupal.edit.util = Drupal.edit.util || {}; | |
+Drupal.edit.util.views = {}; | |
+ | |
+Drupal.edit.util.getID = function(element) { | |
+ var id = jQuery(element).data('edit-id'); | |
+ if (!id) { | |
+ id = jQuery(element).closest('[data-edit-id]').data('edit-id'); | |
+ } | |
+ return id; | |
+}; | |
+ | |
+Drupal.edit.util.getElementSubject = function(element) { | |
+ return Drupal.edit.util.getID(element).split(':').slice(0, 2).join(':'); | |
+}; | |
+ | |
+Drupal.edit.util.getElementPredicate = function(element) { | |
+ return Drupal.edit.util.getID(element).split(':').pop(); | |
+}; | |
+ | |
+Drupal.edit.util.getElementValue = function(element) { | |
+ var valueElement = jQuery('.field-item', element); | |
+ if (valueElement.length === 0) { | |
+ // Handle page title | |
+ valueElement = jQuery('h1', element); | |
+ } | |
+ return $.trim(valueElement.html()); | |
+}; | |
+ | |
+Drupal.edit.util.getElementEntity = function(element, vie) { | |
+ return vie.entities.get(Drupal.edit.util.getElementSubject(element)); | |
+}; | |
+ | |
+Drupal.edit.util.findEditableEntities = function(context) { | |
+ var entityElements = $('.edit-entity.edit-allowed', context || Drupal.settings.edit.context); | |
+ entityElements.each(function () { | |
+ // Register the entity with VIE | |
+ Drupal.edit.vie.entities.addOrUpdate({ | |
+ '@subject': Drupal.edit.util.getElementSubject(jQuery(this)), | |
+ '@type': jQuery(this).data('edit-entity-label') | |
+ }, { | |
+ overrideAttributes: true | |
+ }); | |
+ }); | |
+ return entityElements; | |
+}; | |
+ | |
+Drupal.edit.util.findEditableFields = function(context) { | |
+ var propertyElements = $('.edit-field.edit-allowed', context || Drupal.settings.edit.context); | |
+ propertyElements.each(function () { | |
+ // Register with VIE | |
+ var propertyName = Drupal.edit.util.getElementPredicate(jQuery(this)); | |
+ var entityData = { | |
+ '@subject': Drupal.edit.util.getElementSubject(jQuery(this)) | |
+ }; | |
+ entityData[propertyName] = Drupal.edit.util.getElementValue(jQuery(this)); | |
+ Drupal.edit.vie.entities.addOrUpdate(entityData, { | |
+ overrideAttributes: true | |
+ }); | |
+ }); | |
+ return propertyElements; | |
+}; | |
+ | |
+/* | |
+ * findEditableFields() just looks for fields that are editable, i.e. for the | |
+ * field *wrappers*. Depending on the field, however, either the whole field wrapper | |
+ * will be marked as editable (in this case, an inline form will be used for editing), | |
+ * *or* a specific (field-specific even!) DOM element within that field wrapper will be | |
+ * marked as editable. | |
+ * This function is for finding the *editables* themselves, given the *editable fields*. | |
+ */ | |
+Drupal.edit.util.findEditablesForFields = function($fields) { | |
+ var $editables = $(); | |
+ | |
+ // type = form | |
+ $editables = $editables.add($fields.filter('.edit-type-form')); | |
+ | |
+ // type = direct | |
+ var $direct = $fields.filter('.edit-type-direct'); | |
+ $editables = $editables.add($direct.find('.field-item')); | |
+ // Edge case: "title" pseudofield on pages with lists of nodes. | |
+ $editables = $editables.add($direct.filter('h2').find('a')); | |
+ // Edge case: "title" pseudofield on node pages. | |
+ $editables = $editables.add($direct.find('h1')); | |
+ | |
+ return $editables; | |
+}; | |
+ | |
+Drupal.edit.util.findFieldForID = function(id, context) { | |
+ return $('[data-edit-id="' + id + '"]', context || $('#content')); | |
+}; | |
+ | |
+Drupal.edit.util.findFieldForEditable = function($editable) { | |
+ return $editable.filter('.edit-type-form').length ? $editable : $editable.closest('.edit-type-direct'); | |
+}; | |
+ | |
+Drupal.edit.util.findEntityForEditable = function($editable) { | |
+ return Drupal.edit.util.findEntityForField(Drupal.edit.util.findFieldForEditable($editable)); | |
+}; | |
+ | |
+Drupal.edit.util.findEntityForField = function($f) { | |
+ var $e = $f.closest('.edit-entity'); | |
+ if ($e.length == 0) { | |
+ var entity_edit_id = $f.data('edit-id').split(':').slice(0,2).join(':'); | |
+ $e = $('.edit-entity[data-edit-id="' + entity_edit_id + '"]'); | |
+ } | |
+ return $e; | |
+}; | |
Drupal.edit.util.calcFormURLForField = function(id) { | |
var parts = id.split(':'); | |
diff --git a/js/views.js b/js/views.js | |
new file mode 100644 | |
index 0000000..979cc70 | |
--- /dev/null | |
+++ b/js/views.js | |
@@ -0,0 +1,17 @@ | |
+(function ($) { | |
+ Drupal.edit = Drupal.edit || {}; | |
+ Drupal.edit.views = Drupal.edit.views || {}; | |
+ | |
+ Drupal.edit.views.EditableView = Backbone.View.extend({ | |
+ predicate: null, | |
+ initialize: function (options) { | |
+ this.predicate = '<http://viejs.org/ns/' + options.predicate + '>'; | |
+ _.bindAll(this, 'render'); | |
+ this.model.bind('change:' + this.predicate, this.render); | |
+ }, | |
+ | |
+ render: function () { | |
+ jQuery(this.el).html(this.model.get(this.predicate)); | |
+ } | |
+ }); | |
+})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment