Hi Sun,
I'm guessing this is not currently possible as I have already jumped into the script and had a look around, but I am hoping you might have some ideas on how this could be achieved.
I'm trying to add support for the WYSIWYG module in my Drag'n'Drop Uploads module, and I need to register a dropzone to allow this happen. The problem is that there is no guarantee that the WYSIWYG will have been loaded/enabled on load, meaning that it can't be targeted to have a dropzone registered.
Is there any Drupal.wysiwyg behavior I can attach that will be run when the WYSIWYG(s) are attached so I can register dropzones as required?
Any thoughts you have would be greatly appreciated.
Cheers,
Deciphered.
Comments
Comment #1
twodI know you directed question to sun, but I hope you don't mind me answering it.
Wysiwyg module does not have a callback or similar method to notify 'outside' code that an editor has been fully attached, partially because Wysiwyg module itself does not have this information. The editors load asynchronously and Wysiwyg module doesn't listen to their 'I'm loaded' events (Might change in 3.x). It does however register plugins, which do get these events, given that the current editor supports plugins and the implementation layer does too. But none of the solutions I can currently think of are pretty or practical...
You could implement native plugins for each editor, implement a wysiwyg module hook to tell the editors about these plugins and get the element(s) you need to register a dropzone.
Pros: You can act on any event the editor triggers. It's relatively easy to figure out which element to set as your dropzone and use the editor API to get it.
Cons: Not all editors support native plugins, and not all of our editor implementations are yet able to register them in the editor. Lots of APIs to learn and plugins to write. Different code for each editor added to the module.
You could implement a 'Drupal plugin' (editor independant plugin), which will be called each time content is retrieved from or sent to the editor.
Pros: One piece of plugin code to write, only against the Wysiwyg API. In theory at least...
Cons: Even fewer editor implementions support Drupal plugins yet. The Wysiwyg API does not react to/relay the exact events you need. You only know that content is grabbed from or sent to the editor, which can happen at any time. No direct access to the editor API via Wysiwyg module. You can still figure out the name of the editor, but after that you're on your own and it's back to customized code for each editor.
You could use your own code to check for the existence of each editor's API and manually register some event handlers/callbacks for each available API and work from there.
Pros: You know when each editor instance is done loading. You'll also know when an editor is unloaded.
Cons: Not all editors have an API which makes this possible. Once again, lots of APIs to learn and much code to write. The global API might not even exist before an editor instance does (FCKeditor).
It would help immensely if you could tell more about the info you need from the editor and how these dropzones work. Then we might be able to work out a solution and get the 'right' features into the 3.x API.
Comment #2
decipheredHi TwoD, of course I don't mind you answering, in fact I thank you for the extremely prompt response.
So I'll try to explain exactly what the "Drag'n'Drop Uploads" module does, how it works and what I'm thinking for WYSIWYG support:
The module allows users to Drag'n'Drop files directly into 'dropzones' (currently all textareas and upload widgets) and have them automatically uploaded. If the 'dropzone' is a textarea (Node body, etc), a CCK Formatted (if applicable) reference to the file will be injected into the dropzone.
There are three different methods this currently works, a hidden cloned file field underneath the mouse cursor on "dragover" for Safari/Chrome, native on "Drop" support handling via the File API for Firefox 3.6 and a very similar Google Gears method for any other Google Gears supported browsers.
What I need to do to integrate the module with the WYSIWYG module is define a 'dropzone', an object with bound events to trigger the Drag'n'Drop upload event, which can only be done when the object exists and has been identified.
The easiest way to do this (that I can think of at this time) would be if the WYSIWYG module itself where to record a reference to the ID of the wrapper around the WYSIWYG, and triggered some sort of hook upon attaching the WYSIWYG.
I don't actually need to talk to any of the WYSIWYGs themselves, except for inserting the content, but that can be done via your existing API, so the current plug-in systems don't look to be a viable solution.
Thoughts:
Drupal.wysiwyg.activeIdtells me the ID of the replaced textarea is edit-body, but there's nothing (that I have found) to tell me that the ID of the highest level DOM object of the WYSIWYG is edit-body___Frame.This is probably as easy as using a sibling selector, but I don't know if the WYSIWYG will always be a sibling of the replaced textarea.
At the end of
Drupal.wysiwygAttach, there could be a simple hook such as:This would then allow other modules to register to be notified when a WYSIWYG has been attached and act accordingly.
Let me know what you think.
Cheers,
Deciphered.
Comment #3
twodI just saw you pinged me on IRC, was out at the time...
A hook at the end of
Drupal.wysiwygAttachprobably won't help much if you need to get reference to editor controlled elements, like its iframe. The editors are still not loaded when that function exits so the state of their markup can be anything. Most of them have gotten as far as creating the iframe for the editing area, but the contents in the iframe load asynchronously. Scripts in the iframe won't run until all other script execution on the main page is over (or at least temporarily halted if there are unexecuted setTimeouts, setIntervals etc) and those scripts usually change things in the main document as well. What the top element in the editor's 'domain' looks like and where it was placed is entirely up to the editor. Wysiwyg module figures out which editor instance to talk to based on the original textarea id and tries to avoid poking around into whatever markup the editor has created.The hook would only tell you that Wysiwyg module itself is done, but any code running in the hook will further delay the editor loading so it's not safe to access any API to get more info about an editor at that time. We also do not know if the editor's markup in the main document is final.
It's correct that the "instance id" set to
Drupal.wysiwyg.activeIdis the id of a replaced textarea. It changes as soon as an editor is attached or gets focus (in practice, is clicked or tabbed to), but I can't guarantee that it has a chance to update before the drag/drop event fires so it might not actually point to the field you're interested in.If you can set the dropzone to be the field's wrapper element ('#[field-id]-wrapper') you could use your regular Drupal behavior to make the entire area around a field a dropzone. That element is never changed by Wysiwyg module or any editor as all the 'action' happens inside it. Events will only have to be attached once, but events fired inside the editing area can not bubble up high enough if there is an iframe barrier. We'd need the editor to support the relevant event and register a listener through it. (That's why I had the native plugin thing in mind.) But I think I might be able to get around that, need to play around with your module a bit to be sure though.
Once some relevant element is designated a dropzone and a successful drop is detected it should not be so hard to figure out what to do. In the actual drop handling code, you check with
Drupal.wysiwyg.instances[field_id]to see if Wysiwyg module controls a field. If an entry in the instances array exists, you can useDrupal.wysiwyg.instances[field_id].insert('[Reference to uploaded file]')insert the upload reference at the cursor position. If no instance for that field exists, Wysiwyg module does not control it and you can fall back to manually inserting the upload reference into the regular textarea (note that some other WYSYWIYG module might still have replaced the element with an editor).Comment #4
decipheredHi TwoD,
No probs about IRC, the timezone difference makes it difficult to chat with many devs (being Australian and all).
I did actually make quite a bit of headway last night, and would be happy to share that with you except that I won't have access to my latest development version for another 8 hours due to forgetfulness, but I will be sure to send you a copy later tonight if you are still interested.
I did realize after I wrote that last post that being an asynchronous load, what I had proposed wouldn't be possible, and that is the area I am still stuck. Currently I am just running the WYSIWYG dropzone attachment function manually to get past this hurdle.
I had also considered using the wrapper as the dropzone and I even tested it, but it leaves too many ways that the dropzone could be skipped over as the IFRAME(s) blocks the wrappers dropzone, so that's out of the question.
However, I did actually manage to get all but two of the available WYSIWYGs working as dropzones; one because I couldn't even get the WYSIWYG module to recognize the WYSIWYG was installed (open wysiwyg?) and the other I am unsure about.
One thing I did notice is that a large number of WYSIWYGs don't have an insert function, so while they act as a dropzone to upload the files, they can't receive the html output.
Also, I did use the
Drupal.wysiwyg.activeIdfor use in selector, but I agree with you that it will cause issues, especially if there are multiple WYSIWYGs active on a page, so I do intend to re-write that particular section.The section where I define all the selectors could possibly be re-written to be part of the WYSIWYG module itself as all it is is a
switch()that provides the path back to the necessary Editing Area object, but that is entirely you're call. You can have a look at it when I send you the code.Either way, enough rambling for the moment, will get in contact with you again when I finish up for the day and get back to my files.
Cheers,
Deciphered.
Comment #5
twodI'm in Sweden so I have some problems getting in touch with other devs too sometimes. Especially sun, which is a bit strange since he's from the same timezone as me hehe.
I'd be happy to take a look at your code. You can use my contact form to send a link if the code is online somewhere, otherwise I'll reply ASAP so you can send it as an email attachment.
Note that the code to attach dropzones would have to run each time an editor is attached, as editors are completely destroyed when detached.
There are a couple of issues (and patches) about adding the insert method to more editors. And now that you mention it, I really should get around to commit those soon and add it for more editors... A couple of other modules are also waiting for those methods
I was going to write something more but I forgot what. Probably just as good since I tend to write too much. ;)
Comment #6
decipheredAttached is the latest internal version of the Drag'n'Drop Uploads module with the initial WYSIWYG support.
I have only tested the new functionality with Firefox 3.6 beta 3 so far, so I can't say how reliable the Google Gears or Safari/Chrome support is.
To test it you need to do the following:
- Have a content type with an upload widget (Upload.module, FileField.module or ImageField.module) enabled/attached.
- Edit the content type and select the upload widget and formatter style (if applicable) under the "Drag'n'Drop Uploads settings", then save the the content type.
- Create a node of the content type, and once a WYSIWYG is attached (FCKeditor or TinyMCE probably best), run the following code in Firebug:
Drupal.wysiwygAttachDropzones()- Lastly, Drag and Drop an image/file from your local filesystem onto the WYSIWYG and after a brief upload the output should be inserted into the WYSIWYG.
Hopefully those instructions aren't too brief, didn't want to hold your hand too much, but if you have any problems getting it to work let me know.
Still trying to come up with a good idea for triggering the function, I'm hoping to avoid the use of a setTimeout loop.
Comment #7
decipheredHey TwoD,
Just thought I should let you know I ended up going with a setTimeout loop for the short term, until I can come up with an alternative. The general functionality works with a few minor issues.
I have committed the code to the module and updated the demonstration site with two WYSIWYG tests (http://demo.stuar.tc/dragndrop_uploads).
Thanks for the help, and if you do come up with an ideas that may help improve the integration I would be happy to hear them.
Cheers,
Deciphered.
Comment #8
kenorb commentedSimilar issue.
#859232: activate button on load - how to execute TinyMCE command on load
I'm trying to execute some javascript code on WYSIWYG load.
I don't know where to find the solution.
Comment #9
kenorb commentedsetTimeout is not the solution. For sure there should be something 'onload'.
Comment #10
decipheredMy latest internal development build of DnDU is doing things differently that allows me to bypass the setTimeout method (that I never like anyway), but there definitely needs to to be a trigger, whether it's a custom trigger, or something that just forces all Drupal.behaviors to run.
Might have another look at WYSIWYG for potential ideas in the near future.
Comment #11
kenorb commentedI've problem with setTimeout() solution, because when set to 5sec, after that time I lose focus of my current element and it's breaks what I type.
See: #861654: Load Spell Checker button on page load
Comment #12
kenorb commentedSome workaround as well:
http://drupal.org/node/135755#comment-2662178
Almost works after TinyMCE is rendered, but the content is empty.
Comment #13
kenorb commentedTrigger on add:
And this if you need some triggers when content is loaded:
This one is not a proper way, but it does work.
Probably we should have some feature which allow to define additional custom functions onPostRender and onLoadContent without overriding Drupal.wysiwyg.editor.attach.tinymce.
Comment #14
decipheredMy latest workaround is to go with a Mutation Event, however I haven't done extensive testing with this and I have serious doubts about it's cross browser compatibility, but it is still an option.
The problem with the on add trigger is that it doesn't guarantee that the WYSIWYG is ready to be manipulated. Still, it's definitely a step forward.
Comment #15
kenorb commentedI think above modification of Drupal.wysiwyg.editor.init.tinymce (onPostRender and onLoadContent events) could be extended that allow to execute some JS code from other modules (module_invoke_all, but in javascript).
Comment #16
twodActually, jQuery allows us to create custom events and fire them at will, which is how I indend for this to work in Wysiwyg 3.x.
Comment #17
kenorb commentedSo how to convert overridden version of Drupal.wysiwyg.editor.attach.tinymce in #13 to jQuery way which will run some code on onPostRender and onLoadContent events?
Comment #18
twodI can't promise this is the exact way it'll work in the end, as we have to compare capabilities and available editor events across all supported editors, and I'm writing this on my phone at work without references available, but it's a start.
In the event handlers in #13 (where stuff is now printed to the console), we'll call jQuery's .trigger method and pass it an event name and some useful parameters, like the editor instance, other modules can then react on these (global or perhaps per field) events by passing one of the event names and a callback function reference to jQuery's bind method.
To make this useful, all Wysiwyg events would have to be triggered from within all editor implementations in reaction to events generated by the implemented editor. In short; Wysiwyg will become a cross-editor event relay/proxy joining editor libraries and Drupal code.
Of course, not all editors support all the events triggered by the big editors like TinyMCE, FCKeditor, CKEditor or YUI Editor, so we'll have to be a bit clever in implementing this. Perhaps even "patch" editors at runtime, which we already have to do in some cases to be able to communicate with them at all.
Comment #19
kenorb commentedDrupal 6 is no longer officially supported. If you think this issue is still relevant for 8.x, feel free to re-open.