Mercurial > defr > drupal > scald > dnd
changeset 31:767ebf925654
Added forcecontainer tinymce plugin, lots and lots and lots of refactorizing.
author | David Eads <eads@chicagotech.org> |
---|---|
date | Thu, 19 Mar 2009 15:58:36 -0500 |
parents | 2d49adbd8992 |
children | ee520ba7d98b |
files | dnd.module js/dnd-library.js js/tinymce/atomic/editor_plugin.js js/tinymce/atomic/editor_plugin_src.js js/tinymce/forcecontainer/editor_plugin_src.js modules/dnd_test/dnd-library-footer.tpl.php modules/dnd_test/dnd_test.module |
diffstat | 7 files changed, 270 insertions(+), 639 deletions(-) [+] |
line wrap: on
line diff
--- a/dnd.module Tue Mar 17 21:29:00 2009 -0500 +++ b/dnd.module Thu Mar 19 15:58:36 2009 -0500 @@ -30,7 +30,7 @@ */ function dnd_theme() { return array( - 'dnd_library' => array('arguments' => array('library' => NULL, 'library_id' => NULL), 'template' => 'dnd-library'), + 'dnd_library_wrapper' => array('arguments' => array('settings' => NULL, 'element' => NULL)), ); } @@ -109,21 +109,13 @@ 'dndEnabledLibraries' => array($element['#id'] => $settings), ), 'setting'); - // Store editor representations in Drupal setting - drupal_add_js(array( - 'dndEditorRepresentations' => $library['editor_representations'], - 'dndLibraryPreviews' => $library['library_previews'], - ), 'setting'); - - // Note that we brute force the wrapper + // Generate $element['#suffix'] = '<div class="dnd-library-wrapper" id="'. $settings['library_id'] .'"></div>'. $element['#suffix']; } return $element; } -function template_preprocess_dnd_library($library) {} - /** * Implementation of hook_wywiwyg_plugin(). */ @@ -132,22 +124,28 @@ switch ($editor) { case 'tinymce': if ($version > 3) { - $plugins['atomic'] = array( - 'type' => 'external', - 'title' => t('Atomic selection plugin'), - 'description' => t('This plugin forces a selection up to the outer container of a given element. It is available via the third party repository maintained by MoxieCode on SourceForge.'), - 'extensions' => array('atomic' => t('Atomic Selection')), - 'path' => drupal_get_path('module', 'dnd') .'/js/tinymce/atomic/editor_plugin.js', - 'url' => 'http://sourceforge.net/tracker/index.php?func=detail&aid=2519211&group_id=103281&atid=738747', + $plugins['forcecontainer'] = array( + 'title' => t('Force Container Plugin'), + 'description' => t('A custom plugin to forces a selection up to the outer container of a given element.'), + 'extensions' => array('forcecontainer' => t('Force Container')), + 'path' => drupal_get_path('module', 'dnd') .'/js/tinymce/forcecontainer/editor_plugin_src.js', 'load' => TRUE, - ); - $plugins['noneditable'] = array( - 'path' => drupal_get_path('module', 'wysiwyg') .'/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js', - 'extensions' => array('noneditable' => t('Noneditable')), - 'load' => TRUE, + 'options' => array( + 'forcecontainer_class' => 'dnd-drop-wrapper', + 'forcecontainer_trigger_dnd' => TRUE, + ), ); } break; } return $plugins; } + + +/** + * Theme the markup that will surround a library loaded via JSON. + */ +function theme_dnd_library_wrapper($settings, $element = NULL) { + return '<div id="'. $settings['library_id'] .'" class="dnd-library-wrapper"></div>'; +} +
--- a/js/dnd-library.js Tue Mar 17 21:29:00 2009 -0500 +++ b/js/dnd-library.js Thu Mar 19 15:58:36 2009 -0500 @@ -17,7 +17,7 @@ (function($) { // Custom selectors $.extend($.expr[":"], { - 'empty' : function(a, i, m) { + 'dnd_empty' : function(a, i, m) { return !$(a).filter(function(i) { return !$(this).is('br'); }).length && !$.trim(a.textContent || a.innerText||$(a).text() || ""); @@ -38,6 +38,7 @@ // Set up some initial settings for BeautyTips var settings = Drupal.settings.dndEnabledLibraries[$editor.get(0).id] = $.extend({ + 'btSettings' : { 'trigger': 'none', 'width': 375, 'spikeLength': 7, @@ -46,6 +47,23 @@ 'strokeWidth': 1, 'fill': '#ffd', 'strokeStyle': '#555' + }, + 'libraryHoverIntentSettings' : { + 'interval': 500, + 'timeout' : 0, + 'over': function() { + var $this = $(this); + this.btOn(); + // Remove the preview once dragging of any image has commenced + $('img', $this).bind('drag', function(e) { + $this.btOff(); + }); + $('img', $this).bind('click', function(e) { + $this.btOff(); + }); + }, + 'out': function() { this.btOff(); } + } }, Drupal.settings.dndEnabledLibraries[$editor.get(0).id]); // Bind Drag and Drop plugin invocation to events emanating from Wysiwyg @@ -56,7 +74,7 @@ Drupal.settings.dndEditorRepresentations = {}; Drupal.settings.dndLibraryPreviews = {}; - // Populate + // Initialize the library $.getJSON(Drupal.settings.basePath + settings.url, function(data) { Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $editor); }); @@ -84,45 +102,44 @@ // Add preview behavior to editor items (thanks, BeautyTips!) $('.editor-item', $this).each(function () { - $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], settings.bt_settings); - var hover_opts = $.extend({ - 'interval': 500, - 'timeout' : 0, - 'over': function() { - var $this = $(this); - this.btOn(); - // Remove the preview once dragging of any image has commenced - $('img', $this).bind('drag', function(e) { - $this.btOff(); - }); - $('img', $this).bind('click', function(e) { - $this.btOff(); - }); - }, - 'out': function() { this.btOff(); } - }, settings.libraryHoverIntentOpts); - $(this).hoverIntent(hover_opts); + $(this).bt(Drupal.settings.dndLibraryPreviews[this.id], settings.btSettings); + $(this).hoverIntent(settings.libraryHoverIntentSettings); }); // Preload images in editor representations var cached = $.data($(editor), 'dnd_preload') || {}; - /*for (editor_id in Drupal.settings.dndEditorRepresentations) { + for (editor_id in Drupal.settings.dndEditorRepresentations) { if (!cached[editor_id]) { $representation = $(Drupal.settings.dndEditorRepresentations[editor_id].body); if ($representation.is('img') && $representation.get(0).src) { $representation.attr('src', $representation.get(0).src); } else { $('img', $representation).each(function() { - this.attr('src', this.src); + $(this).attr('src', this.src); }); } } } - $.data($(editor), 'dnd_preload', cached);*/ + $.data($(editor), 'dnd_preload', cached); + $('.pager a', $this).click(function() { + $.getJSON(this.href, function(data) { + Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); + }); + return false; + }); + $('.view-filters input[type=submit]', $this).click(function() { + $(this).ajaxSubmit({ + 'url' : Drupal.settings.basePath + settings.url, + 'dataType' : 'json', + 'success' : function(data) { + Drupal.behaviors.dndLibrary.renderLibrary.call($this.get(0), data, $(editor)); + } + }); + return false; + }); } - // Dynamically compose a callback based on the editor name Drupal.behaviors.dndLibrary.attach_library = function(e, data) { var settings = $.extend({idSelector: Drupal.behaviors.dndLibrary.idSelector}, Drupal.settings.dndEnabledLibraries[data.field]); @@ -133,12 +150,7 @@ } // Do garbage collection on detach -Drupal.behaviors.dndLibrary.detach_library = function(e, data) { - for (t in $(document).data('dnd_timers')) { - clearInterval(t); - } - $(document).removeData('dnd_timers'); -} +Drupal.behaviors.dndLibrary.detach_library = function(e, data) {} // Basic textareas Drupal.behaviors.dndLibrary.attach_none = function(data, settings) { @@ -150,7 +162,7 @@ // Update element count Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); - var snippet = '<p class="dnd-dropped-wrapper">' + Drupal.settings.dndEditorRepresentations[representation_id].body + '</p>'; + var snippet = '<p class="dnd-drop-wrapper">' + Drupal.settings.dndEditorRepresentations[representation_id].body + '</p>'; $target.replaceSelection(snippet, true); } }, settings); @@ -190,13 +202,24 @@ settings = $.extend({ targets: $('#'+ data.field +'-wrapper iframe'), + processTargets: function(targets) { + return targets.each(function() { + var target = this + // Decrement counter on delete + $(target).bind('dnd_delete', function(e, data) { + Drupal.behaviors.dndLibrary.countElements(target, $(data.node).attr('dnd_id'), true); + }); + $('head', $(this).contents()).append('<style type="text/css">img { display: none; } img.dnd-dropped {display: block; }</style>'); + return this; + }); + }, processIframeDrop: function(drop, id_selector) { var representation_id = id_selector.call(this, drop); var representation = Drupal.settings.dndEditorRepresentations[representation_id].body; var target = this, $target = $(target), $drop = $(drop), block; // Update element count - //Drupal.behaviors.dndLibrary.countElements.call(target, representation_id); + Drupal.behaviors.dndLibrary.countElements(target, representation_id); // Search through block level parents $drop.parents().each(function() { @@ -211,7 +234,7 @@ $drop.remove(); // Create an element to insert - var insert = dom.create('p', {'class' : 'dnd-dropped-wrapper', 'id' : 'dnd-inserted'}, representation); + var insert = dom.create('p', {'class' : 'dnd-drop-wrapper', 'id' : 'dnd-inserted'}, representation); // The no-parent case if ($(block).is('body')) { @@ -223,17 +246,17 @@ $block = $('#target-block', $target.contents()); // @TODO is finding the parent broken in safari?? - $block.after('<p class="dnd-dropped-wrapper" id="dnd-inserted">' + representation + '</p>'); + $block.after('<p class="dnd-drop-wrapper" id="dnd-inserted">' + representation + '</p>'); // The active target block should be empty - if ($('#target-block:empty', $target.contents()).length > 0) { + if ($('#target-block:dnd_empty', $target.contents()).length > 0) { $('#target-block', $target.contents()).remove(); } else if (old_id) { block.id = old_id; } else { $block.removeAttr('id'); } - } + } var $inserted = $('#dnd-inserted', $target.contents()); var inserted = $inserted.get(0); @@ -243,12 +266,8 @@ // If the previous element is also an editor representation, we need to // put a dummy paragraph between the elements to prevent editor errors. - if (previous && $(previous).hasClass('dnd-dropped-wrapper')) { - $inserted.before('<p><span id="__spacer">_</span></p>'); - c = dom.get('__spacer'); - s.select(c); - ed.execCommand('Delete', false, null); - dom.remove(c); + if (previous ) { + $inserted.before('<p></p>'); } // Look ahead in the DOM @@ -257,22 +276,28 @@ // If the next item exists and isn't an editor representation, drop the // caret at the beginning of the element, otherwise make a new paragraph // to advance the caret to. - if (next && !$(next).hasClass('dnd-dropped-wrapper')) { + if (next && !$(next).hasClass('dnd-drop-wrapper')) { $(next).prepend('<span id="__caret">_</span>'); } - else { + else if (!$(next).hasClass('dnd-drop-wrapper')) { var after = dom.create('p', {}, '<span id="__caret">_</span>'); dom.insertAfter(after, 'dnd-inserted'); } - - // Clear the ID for the next drop - $inserted.removeAttr('id'); - + // Force selection to reset the caret var c = dom.get('__caret'); - s.select(c); - ed.execCommand('Delete', false, null); - dom.remove(c); + if (c) { + s.select(c); + ed.execCommand('Delete', false, null); + dom.remove(c); + } + + // Unset id for next drop and add special dnd attribute for counting + // purposes + $inserted + .removeAttr('id') + .attr('dnd_id', representation_id); + } }, settings); @@ -281,18 +306,16 @@ // Keep a counter of times a representation ID has been used -Drupal.behaviors.dndLibrary.countElements = function(representation_id) { - // We need to track element usage betwen all editors, so we look for the - // parent form item - $target = $(this).parents('.form-item'); - var counter = $target.data('representation_counter'); +Drupal.behaviors.dndLibrary.countElements = function(target, representation_id, decrement) { + var counter = $(target).data('dnd_representation_counter'); if (!counter) { counter = {} counter[representation_id] = 1; } else if (counter && !counter[representation_id]) { counter[representation_id] = 1; } else { - counter[representation_id] = counter[representation_id] + 1; + counter[representation_id] = counter[representation_id] + ((decrement) ? -1 : 1); } - $target.data('representation_counter', counter); + $(target).data('dnd_representation_counter', counter); + return counter[representation_id]; }
--- a/js/tinymce/atomic/editor_plugin.js Tue Mar 17 21:29:00 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -(function(){var Event=tinymce.dom.Event;var direction=0;var hasClicked=false;var atStart=null;var atEnd=null;tinymce.create('tinymce.plugins.AtomicPlugin',{init:function(ed,url){var t=this,atomicClass;t.editor=ed;atomicClass=ed.getParam("atomic_atomic_class","mceAtomic");ed.onNodeChange.addToTop(function(ed,cm,n){var sc,ec,wasEnd=false;sc=ed.dom.getParent(ed.selection.getStart(),function(n){return ed.dom.hasClass(n,atomicClass);});ec=ed.dom.getParent(ed.selection.getEnd(),function(n){return ed.dom.hasClass(n,atomicClass);});if(atEnd){wasEnd=true;}atStart=null;atEnd=null;if(sc||ec){var s=ed.selection.getSel();var r=ed.selection.getRng();var select=s.type?(s.type!="None"):(s.anchorNode!=s.focusNode||s.anchorOffset!=s.focusOffset);var move=false;if(!select){if(direction==-1){if(r.setStart){var lc=t._leftNeighbour(ed,sc);if(lc){var offset=lc.nodeType==3?lc.length:1;r.setStart(lc,offset);r.setEnd(lc,offset);}else{r.setStart(sc,0);r.setEnd(sc,0);}atStart=sc;move=true;}else{var r2=ed.getBody().createTextRange();r2.moveToElementText(ec);r2.collapse(false);if(!r2.isEqual(r)){r.moveToElementText(sc);r.collapse();atStart=sc;move=true;}else{var tempEl=ed.dom.create("span",null,"­");ec.insertAdjacentElement("afterEnd",tempEl);r.moveToElementText(tempEl);r.collapse(false);ed.selection.setRng(r);ed.dom.remove(tempEl);atEnd=ec;}}}else if(direction==1){if(r.setStartAfter){r.setStartAfter(ec);r.setEndAfter(ec);move=true;}else{if(wasEnd){r.move('character');ed.selection.setRng(r);ec=null;}else{var tempEl=ed.dom.create("span",null,"­");ec.insertAdjacentElement("afterEnd",tempEl);r.moveToElementText(tempEl);r.collapse(false);ed.selection.setRng(r);ed.dom.remove(tempEl);}}atEnd=ec;}else if(hasClicked){if(r.setStart){r.setStart(sc,0);r.setEnd(ec,ec.childNodes.length);}else{r.moveToElementText(sc);}move=true;}else if(wasEnd&&!r.setStart){var tempEl=ed.dom.create("span",null,"­");ec.insertAdjacentElement("afterEnd",tempEl);r.moveToElementText(tempEl);r.collapse(false);ed.selection.setRng(r);ed.dom.remove(tempEl);atEnd=ec;}}else{var reverse=false;if(s.anchorNode&&s.anchorNode==r.endContainer&&s.anchorOffset==r.endOffset){reverse=true;}var moveStart=false;var moveEnd=false;var rsc,rec;if(sc){if(s.anchorNode){if(sc!=r.startContainer||(r.startOffset!=0&&r.startOffset!=sc.childNodes.length)){moveStart=true;}}else{rsc=ed.getBody().createTextRange();rsc.moveToElementText(sc);moveStart=(r.compareEndPoints("StartToStart",rsc)!=0);}}if(ec){if(s.anchorNode){if(ec!=r.endContainer||(r.endOffset!=0&&r.endOffset!=ec.childNodes.length)){moveEnd=true;}}else{rec=ed.getBody().createTextRange();rec.moveToElementText(ec);moveEnd=(r.compareEndPoints("EndToEnd",rec)!=0);}}if(reverse){if(moveEnd){if(direction==-1){r.setEnd(ec,0);}else{r.setEnd(ec,ec.childNodes.length);}move=true;}if(moveStart){if(direction==1){s.extend(sc,sc.childNodes.length);move=false;}else{s.extend(sc,0);move=false;}}}else{if(moveStart){if(direction==1){if(r.setStart){r.setStart(sc,sc.childNodes.length);}else{r.setEndPoint("StartToEnd",rsc);}}else{if(r.setStart){r.setStart(sc,0);}else{r.setEndPoint("StartToStart",rsc);}}move=true;}if(moveEnd){if(direction==-1){if(r.setEnd){s.extend(ec,0);}else{r.setEndPoint("EndToStart",rec);move=true;}}else{if(r.setEnd){s.extend(ec,ec.childNodes.length);}else{r.setEndPoint("EndToEnd",rec);move=true;}}}}}if(move){ed.selection.setRng(r);}}});ed.onKeyDown.addToTop(function(ed,e){var k=e.keyCode,atomicClass;atomicClass=ed.getParam("atomic_atomic_class","mceAtomic");hasClicked=false;direction=0;if(k==37||k==38){direction=-1;}else if(k==39||k==40){direction=1;}else if(k==8){if(!atEnd){var s=ed.selection.getSel();var select=s.type?(s.type!="None"):(s.anchorNode!=s.focusNode||s.anchorOffset!=s.focusOffset);if(!select){var ep=s.focusNode?((s.focusNode.nodeType==3&&s.focusOffset!=0)?null:s.focusNode):ed.selection.getStart();if(ed.dom.hasClass(ep,atomicClass)){atEnd=ep;}else if(ep){var rc;if(!s.focusNode){var r=ed.selection.getRng();var r2=ed.getBody().createTextRange();r2.moveToElementText(ep);if(r2.compareEndPoints("StartToStart",r)==0){rc=t._leftNeighbour(ed,ep);}else{rc=ep.firstChild;while(rc){if(rc.nodeType==1){r2.moveToElementText(rc);if(r.compareEndPoints("EndToEnd",r2)==0){break;}}rc=rc.nextSibling;}}}else{if(ep.nodeType==1&&s.focusOffset!=0){rc=ep.childNodes[s.focusOffset-1];}else{rc=t._leftNeighbour(ed,ep);}}if(rc){while(rc&&!ed.dom.hasClass(rc,atomicClass)){rc=rc.lastChild;}if(rc){atEnd=rc;}}}}}if(atEnd){ed.dom.remove(atEnd);atEnd=null;return Event.cancel(e);}}else if(k==46){if(!atStart){var s=ed.selection.getSel();var select=s.type?(s.type!="None"):(s.anchorNode!=s.focusNode||s.anchorOffset!=s.focusOffset);if(!select){var ep=s.focusNode?((s.focusNode.nodeType==3&&s.focusOffset!=s.focusNode.length)?null:s.focusNode):ed.selection.getEnd();if(ed.dom.hasClass(ep,atomicClass)){atStart=ep;}else if(ep){var lc;if(!s.focusNode){var r=ed.selection.getRng();var r2=ed.getBody().createTextRange();r2.moveToElementText(ep);if(r2.compareEndPoints("EndToEnd",r)==0){lc=t._rightNeighbour(ed,ep);}else{lc=ep.firstChild;while(lc){if(lc.nodeType==1){r2.moveToElementText(lc);if(r.compareEndPoints("StartToStart",r2)==0){break;}}lc=lc.nextSibling;}}}else{if(ep.nodeType==1&&s.focusOffset!=ep.childNodes.length){lc=ep.childNodes[s.focusOffset];}else{lc=t._rightNeighbour(ed,ep);}}if(lc){while(lc&&!ed.dom.hasClass(lc,atomicClass)){lc=lc.firstChild;}if(lc){atStart=lc;}}}}}if(atStart){ed.dom.remove(atStart);atStart=null;return Event.cancel(e);}}});ed.onMouseDown.addToTop(t._onClick);ed.onMouseUp.addToTop(t._onClick);},getInfo:function(){return{longname:'Atomic elements',author:'Sander Kruger',authorurl:'http://www.3gsp.eu',infourl:'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/atomic',version:tinymce.majorVersion+"."+tinymce.minorVersion};},_onClick:function(ed,e){direction=0;hasClicked=true;atStart=null;atEnd=null;},_leftNeighbour:function(ed,e){var l=ed.dom.getParent(e,function(n){return e.previousSibling!=null;});if(l){return l.previousSibling;}return null;},_rightNeighbour:function(ed,e){var r=ed.dom.getParent(e,function(n){return e.nextSibling!=null;});if(r){return r.nextSibling;}return null;}});tinymce.PluginManager.add('atomic',tinymce.plugins.AtomicPlugin);})(); \ No newline at end of file
--- a/js/tinymce/atomic/editor_plugin_src.js Tue Mar 17 21:29:00 2009 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,558 +0,0 @@ -/** - * - * @author Sander Kruger - * @copyright Copyright © 2009, Sander Kruger, All rights reserved. - */ - -(function() { - var Event = tinymce.dom.Event; - var direction = 0; // Track cursor direction for skipping - var hasClicked = false; // Track mouseclicks for selecting - var atStart = null; // Cache if the cursor is at the start or at the end of - var atEnd = null; // an atom. - - tinymce.create('tinymce.plugins.AtomicPlugin', { - init : function(ed, url) { - var t = this, atomicClass; - - t.editor = ed; - atomicClass = ed.getParam("atomic_atomic_class", "mceAtomic"); - - ed.onNodeChange.addToTop(function(ed, cm, n) { - var sc, ec, wasEnd=false; - - // Check if the start or end of the selection is in an atom - sc = ed.dom.getParent(ed.selection.getStart(), function(n) { - return ed.dom.hasClass(n, atomicClass); - }); - - ec = ed.dom.getParent(ed.selection.getEnd(), function(n) { - return ed.dom.hasClass(n, atomicClass); - }); - if (atEnd) - { - wasEnd = true; - } - atStart = null; - atEnd = null; - // Check situation to move the cursor or selection. - if (sc || ec) - { - var s = ed.selection.getSel(); - var r = ed.selection.getRng(); - var select = s.type?(s.type!="None"):(s.anchorNode != s.focusNode || s.anchorOffset != s.focusOffset); - var move = false; - if (!select) - { - // The user has not selected anything but is moving the - // cursor or clicked on an atom. - if (direction == -1) - { - if (r.setStart) - { - var lc = t._leftNeighbour( ed, sc ); - if (lc) - { - // Make sure that when skipping to the left, - // the next input doesn't go into the atom. - var offset = lc.nodeType==3?lc.length:1; - r.setStart( lc, offset ); - r.setEnd( lc, offset ); - } - else - { - r.setStart( sc, 0 ); - r.setEnd( sc, 0 ); - } - atStart = sc; - move = true; - } - else - { - // ie (check if the caret is not just after the - // atom) - var r2 = ed.getBody().createTextRange(); - r2.moveToElementText( ec ); - r2.collapse(false); - if (!r2.isEqual( r )) - { - r.moveToElementText( sc ); - r.collapse(); - atStart = sc; - move = true; - } - else - { - var tempEl = ed.dom.create("span", null, "­"); - ec.insertAdjacentElement("afterEnd", tempEl); - - r.moveToElementText(tempEl); - r.collapse(false); - ed.selection.setRng(r); - - ed.dom.remove(tempEl); - atEnd = ec; - } - } - } - else if (direction == 1) - { - if (r.setStartAfter) - { - r.setStartAfter( ec ); - r.setEndAfter( ec ); - move = true; - } - else - { - if (wasEnd) - { - r.move( 'character' ); - ed.selection.setRng(r); - ec = null; - } - else - { - var tempEl = ed.dom.create("span", null, "­"); - ec.insertAdjacentElement("afterEnd", tempEl); - - r.moveToElementText(tempEl); - r.collapse(false); - ed.selection.setRng(r); - - ed.dom.remove(tempEl); - } - } - atEnd = ec; - } - else if (hasClicked) - { - if (r.setStart) - { - r.setStart( sc, 0 ); - r.setEnd( ec, ec.childNodes.length ); - } - else - { - // ie - r.moveToElementText( sc ); - } - move = true; - } - else if (wasEnd && !r.setStart) - { - // ie - var tempEl = ed.dom.create("span", null, "­"); - ec.insertAdjacentElement("afterEnd", tempEl); - - r.moveToElementText(tempEl); - r.collapse(false); - ed.selection.setRng(r); - - ed.dom.remove(tempEl); - atEnd = ec; - } - } - else - { - // Reverse selections have the anchor at the end and the - // focus at the start. This happens when selecting - // backwards. Make sure the extended selection keeps the - // same direction. - // *** Issue: ie doesn't support backward selections - // programmatically (or at least I have not found how to - // do this). - var reverse = false; - if (s.anchorNode && s.anchorNode == r.endContainer && s.anchorOffset == r.endOffset) - { - reverse = true; - } - var moveStart = false; - var moveEnd = false; - var rsc, rec; - if (sc) - { - if (s.anchorNode) - { - if (sc != r.startContainer || (r.startOffset != 0 && r.startOffset != sc.childNodes.length)) - { - // The start pos is not where it should be. - moveStart = true; - } - } - else - { - // ie - rsc = ed.getBody().createTextRange(); - rsc.moveToElementText( sc ); - moveStart = (r.compareEndPoints( "StartToStart", rsc ) != 0); - } - } - if (ec) - { - if (s.anchorNode) - { - if (ec != r.endContainer || (r.endOffset != 0 && r.endOffset != ec.childNodes.length)) - { - // The end pos is not where it should be - moveEnd = true; - } - } - else - { - // ie - rec = ed.getBody().createTextRange(); - rec.moveToElementText( ec ); - moveEnd = (r.compareEndPoints( "EndToEnd", rec ) != 0); - } - } - if (reverse) - { - // Start with the end. Since 'selection direction detection' - // doesn't work on IE, don't bother with the IE code here. - if (moveEnd) - { - // If both start and end move and they move leftwards, set start first and then extend instead of starting with setend - if (direction == -1) - { - r.setEnd( ec, 0 ); - } - else - { - r.setEnd( ec, ec.childNodes.length ); - } - move = true; - } - if (moveStart) - { - // Use the extend method to advance the focus - // position of the selection. - if (direction == 1) - { - s.extend( sc, sc.childNodes.length ); - move = false; - } - else - { - s.extend( sc, 0 ); - move = false; - } - } - } - else - { - if (moveStart) - { - if (direction == 1) - { - if (r.setStart) - { - r.setStart( sc, sc.childNodes.length ); - } - else - { - // ie - r.setEndPoint( "StartToEnd", rsc ); - } - } - else - { - if (r.setStart) - { - r.setStart( sc, 0 ); - } - else - { - // ie - r.setEndPoint( "StartToStart", rsc ); - } - } - move = true; - } - if (moveEnd) - { - if (direction == -1) - { - if (r.setEnd) - { - s.extend( ec, 0 ); - } - else - { - // ie - r.setEndPoint( "EndToStart", rec ); - move = true; - } - } - else - { - if (r.setEnd) - { - s.extend( ec, ec.childNodes.length ); - } - else - { - // ie - r.setEndPoint( "EndToEnd", rec ); - move = true; - } - } -// move = false; - } - } - } - if (move) - { - // Make sure other modules can react to this event. - ed.selection.setRng( r ); - } - } - }); - - ed.onKeyDown.addToTop( function(ed, e) { - var k = e.keyCode, atomicClass; - atomicClass = ed.getParam("atomic_atomic_class", "mceAtomic"); - hasClicked = false; - direction = 0; - if (k == 37 || k == 38) - { - direction = -1; - } - else if (k == 39 || k == 40) - { - direction = 1; - } - else if (k == 8) - { - // Check if an atom is being 'backspace'd - if (!atEnd) - { - var s = ed.selection.getSel(); - var select = s.type?(s.type!="None"):(s.anchorNode != s.focusNode || s.anchorOffset != s.focusOffset); - if (!select) - { - var ep = s.focusNode?((s.focusNode.nodeType == 3 && s.focusOffset != 0)?null:s.focusNode):ed.selection.getStart(); - if (ed.dom.hasClass(ep, atomicClass)) - { - atEnd = ep; - } - else if (ep) - { - var rc; - if (!s.focusNode) - { - // ie - // focusOffset is not given. So, we need to find - // it by walking through the children and comparing - // end points. - var r = ed.selection.getRng(); - var r2 = ed.getBody().createTextRange(); - r2.moveToElementText( ep ); - if (r2.compareEndPoints( "StartToStart", r ) == 0) - { - // The caret is at the start of this element. - // Find the left neighbouring element - rc = t._leftNeighbour( ed, ep ); - } - else - { - // The focus is not at the start of the parent. - // Find the child left of the caret. - rc = ep.firstChild; - while (rc) - { - // We are not interested in text nodes - // because they cannot be atoms (all by - // themselves) - if (rc.nodeType == 1) - { - r2.moveToElementText( rc ); - if (r.compareEndPoints( "EndToEnd", r2 ) == 0) - { - break; - } - } - rc = rc.nextSibling; - } - } - } - else - { - if (ep.nodeType == 1 && s.focusOffset != 0) - { - // ep encloses the current caret position. - // select the child to the left of the caret. - rc = ep.childNodes[s.focusOffset-1]; - } - else - { - // The caret is on the left-most end of ep. - // Find it's left neighbour. - rc = t._leftNeighbour( ed, ep ); - } - } - if (rc) - { - // Now see if this element has a right-most - // descendent that is an atom - while (rc && !ed.dom.hasClass(rc, atomicClass)) - { - rc = rc.lastChild; - } - if (rc) - { - atEnd = rc; - } - } - } - } - } - if (atEnd) - { - ed.dom.remove( atEnd ); - atEnd = null; - return Event.cancel(e); - } - } - else if (k == 46) - { - // Check if an atom is being 'delete'd - if (!atStart) - { - var s = ed.selection.getSel(); - var select = s.type?(s.type!="None"):(s.anchorNode != s.focusNode || s.anchorOffset != s.focusOffset); - if (!select) - { - var ep = s.focusNode?((s.focusNode.nodeType == 3 && s.focusOffset != s.focusNode.length)?null:s.focusNode):ed.selection.getEnd(); - if (ed.dom.hasClass(ep, atomicClass)) - { - atStart = ep; - } - else if (ep) - { - var lc; - if (!s.focusNode) - { - // ie - // focusOffset is not given. So, we need to find - // it by walking through the children and comparing - // end points. - var r = ed.selection.getRng(); - var r2 = ed.getBody().createTextRange(); - r2.moveToElementText( ep ); - if (r2.compareEndPoints( "EndToEnd", r ) == 0) - { - // The caret is at the start of this element. - // Find the right neighbouring element - lc = t._rightNeighbour( ed, ep ); - } - else - { - // The focus is not at the end of the parent. - // Find the child right of the caret. - lc = ep.firstChild; - while (lc) - { - // We are not interested in text nodes - // because they cannot be atoms (all by - // themselves) - if (lc.nodeType == 1) - { - r2.moveToElementText( lc ); - if (r.compareEndPoints( "StartToStart", r2 ) == 0) - { - break; - } - } - lc = lc.nextSibling; - } - } - } - else - { - if (ep.nodeType == 1 && s.focusOffset != ep.childNodes.length) - { - // ep encloses the current caret position. - // select the child to the right of the caret. - lc = ep.childNodes[s.focusOffset]; - } - else - { - // The caret is on the right-most end of ep. - // Find it's right neighbour. - lc = t._rightNeighbour( ed, ep ); - } - } - if (lc) - { - // Now see if this element has a left-most - // descendent that is an atom - while (lc && !ed.dom.hasClass(lc, atomicClass)) - { - lc = lc.firstChild; - } - if (lc) - { - atStart = lc; - } - } - } - } - } - if (atStart) - { - ed.dom.remove( atStart ); - atStart = null; - return Event.cancel(e); - } - } - }); - - ed.onMouseDown.addToTop(t._onClick); - ed.onMouseUp.addToTop(t._onClick); - }, - - getInfo : function() { - return { - longname : 'Atomic elements', - author : 'Sander Kruger', - authorurl : 'http://www.3gsp.eu', - infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/atomic', - version : tinymce.majorVersion + "." + tinymce.minorVersion - }; - }, - - _onClick : function(ed, e) { - direction = 0; - hasClicked = true; - atStart = null; - atEnd = null; - }, - - // Helper methods - _leftNeighbour : function( ed, e ) { - var l = ed.dom.getParent(e, function(n) { - return e.previousSibling != null; - }); - if (l) - { - return l.previousSibling; - } - return null; - }, - - _rightNeighbour : function( ed, e ) { - var r = ed.dom.getParent(e, function(n) { - return e.nextSibling != null; - }); - if (r) - { - return r.nextSibling; - } - return null; - } - }); - - // Register plugin - tinymce.PluginManager.add('atomic', tinymce.plugins.AtomicPlugin); -})(); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/tinymce/forcecontainer/editor_plugin_src.js Thu Mar 19 15:58:36 2009 -0500 @@ -0,0 +1,144 @@ +/** + * $Id$ + * + * A plugin to handle forcing a selection to an outer container with a given + * class. + * + * Options + * + * forcecontainer_class: + * + * Elements with this class will be forced to the outer container on certain + * events. + * + * forcecontainer_trigger_dnd: + * + * Custom option -- enables triggering of a custom event via jQuery for the + * Drag and Drop Library. + */ + +(function() { + var Event = tinymce.dom.Event; + + tinymce.create('tinymce.plugins.ForceContainerPlugin', { + getInfo : function() { + return { + longname : 'Force an element to its outer container', + author : 'David Eads', + authorurl : 'http://invisibleinstitute.com/eads', + infourl : '', + version : tinymce.majorVersion + "." + tinymce.minorVersion + }; + }, + + init : function(ed, url) { + var t = this, forceContainerClass; + + t.editor = ed; + forceContainerClass = ed.getParam("forcecontainer_class", "mceForceContainer"); + + ed.onNodeChange.addToTop(function(ed, cm, n) { + var sc, ec, c; + + // Block if start or end is inside a non editable element + sc = ed.dom.getParent(ed.selection.getStart(), function(n) { + return ed.dom.hasClass(n, forceContainerClass) && !ed.dom.hasClass(n, 'force-container-processed'); + }); + + ec = ed.dom.getParent(ed.selection.getEnd(), function(n) { + return ed.dom.hasClass(n, forceContainerClass) && !ed.dom.hasClass(n, 'force-container-processed'); + }); + + // Block or unblock + if (sc || ec) { + c = sc ? sc : ec; + ed.selection.select(c); + t._setDisabled(1); + return false; + } else + t._setDisabled(0); + }); + }, + + _block : function(ed, e) { + var k = e.keyCode, s = ed.selection, n = s.getNode(), reparent, forceContainerClass; + + // Reparent node + forceContainerClass = ed.getParam("forcecontainer_class", "mceForceContainer"); + + // Block if start or end is inside a non editable element + sc = ed.dom.getParent(ed.selection.getStart(), function(n) { + return ed.dom.hasClass(n, forceContainerClass) && !ed.dom.hasClass(n, 'force-container-processed'); + }); + + ec = ed.dom.getParent(ed.selection.getEnd(), function(n) { + return ed.dom.hasClass(n, forceContainerClass) && !ed.dom.hasClass(n, 'force-container-processed'); + }); + + if (sc || ec) { + n = (sc) ? sc : ec; + } + + // Pass F1-F12, alt, ctrl, shift, page up, page down, arrow keys + if ((k > 111 && k < 124) || k == 16 || k == 17 || k == 18 || k == 27 || (k > 32 && k < 41)) { + return; + } + + // Step out to parent and delete + if (k == 8 || k == 46) { + if (ed.getParam("forcecontainer_trigger_dnd", false)) { + // @TODO -- this is getting called twice!!! + $('#' + ed.id + '-wrapper iframe').trigger('dnd_delete', { 'node' : n }); + } + ed.execCommand('Delete', false, null); + } + + // Typing some common characters + if (k == 13 || (k > 47 && k < 91) || (k > 95 && k < 112) || (k > 185 && k < 223)) { + var c = ed.dom.get('__caret'), p; + if (!c) { + p = ed.dom.create('p', {}, ((k != 13) ? String.fromCharCode(k) : '') + '<span id="__caret">_</span>'); + ed.dom.insertAfter(p, n); + s.select(c); + } else { + s.select(c); + ed.execCommand('Delete', false, null); + } + } + + return Event.cancel(e); + }, + + + _setDisabled : function(s) { + var t = this, ed = t.editor, n = t.container; + + tinymce.each(ed.controlManager.controls, function(c) { + c.setDisabled(s); + }); + + if (s !== t.disabled) { + if (s) { + ed.onClick.addToTop(t._block); + ed.onMouseDown.addToTop(t._block); + ed.onKeyDown.addToTop(t._block); + ed.onKeyPress.addToTop(t._block); + ed.onKeyUp.addToTop(t._block); + ed.onPaste.addToTop(t._block); + } else { + ed.onClick.remove(t._block); // @TODO causing buggy behavior if you click twice + ed.onMouseDown.remove(t._block); + ed.onKeyDown.remove(t._block); + ed.onKeyPress.remove(t._block); + ed.onKeyUp.remove(t._block); + ed.onPaste.remove(t._block); + } + + t.disabled = s; + } + } + }); + + // Register plugin + tinymce.PluginManager.add('forcecontainer', tinymce.plugins.ForceContainerPlugin); +})();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/dnd_test/dnd-library-footer.tpl.php Thu Mar 19 15:58:36 2009 -0500 @@ -0,0 +1,13 @@ +<div class="footer"> + <div class="pager"> + <ul> + <?php for ($i=1; $i < 6; $i++) { + $opts = array('query' => array('page' => $i)); + if ($page == $i) { + $opts['class'] = 'active'; + }; + print l($i, 'dnd-test/library', $opts) .' '; + } ?> + </ul> + </div> +</div>
--- a/modules/dnd_test/dnd_test.module Tue Mar 17 21:29:00 2009 -0500 +++ b/modules/dnd_test/dnd_test.module Thu Mar 19 15:58:36 2009 -0500 @@ -53,6 +53,14 @@ 'arguments' => array('i' => NULL), 'template' => 'dnd-library-preview', ), + 'dnd_library_header' => array( + 'arguments' => array('page' => NULL), + 'template' => 'dnd-library-header', + ), + 'dnd_library_footer' => array( + 'arguments' => array('page' => NULL), + 'template' => 'dnd-library-footer', + ), ); } @@ -80,6 +88,8 @@ $editor_representations += dnd_editor_items($i); $library_previews['dnd-test-'. $i] = theme('dnd_library_preview', $i); } + $library .= theme('dnd_library_footer', $page); + return array( 'library' => $library, 'editor_representations' => $editor_representations, @@ -162,3 +172,5 @@ } function template_preprocess_dnd_library_header(&$variables) {} + +function template_preprocess_dnd_library_footer(&$variables) {}