Trying to get TinyMCE to work in 2.3 (through AJAX calls)

Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -
Number of replies: 10

Hi!

I've created a function that creates a moodle editor instance, and it works beautifully.

However, I also have this structure:

<form>
<a href="#" id="add_answer_ajax">Add an answer</a>
</form>

When I click on the link "Add an answer", an AJAX call is made and is supposed to load a TinyMCE textarea under the link. However, I only get a plain text textarea!

The same code is used to initialise the code and the AJAX call, the only difference is that I do not include a header, a footer and provide a layout in the AJAX call - instead I only print out the relevant HTML, which makes the code much faster. If I put the same code with header and footer everything works without a hitch.

So, my guess is that there is some sort of initialization code run in the footer that I cannot access. I guess I could create a new layout specificly for AJAX with empty headers and footers, and that would work, but then I'd probably include 30-40kb of JS with each AJAX call, which is far from ideal...

Any hints to solve this?

Average of ratings: -
In reply to Per Ekström

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

The only hint I can give you is that ForumNG does this somehow. I don't know how though, nor how easy it is to identify the relevant code: https://github.com/moodleou/moodle-mod_forumng

In reply to Tim Hunt

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Damyon Wiese -

I played with this on 2.5 - I have a solution (requires core changes and I'm not 100% happy with the changes yet).

Branch:

https://github.com/damyon/moodle/branches/WIP_MFORM_DIALOGUE

(Adds a local plugin with a demo form).

No warranty - just for comments!

Average of ratings: Useful (1)
In reply to Damyon Wiese

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -

Ok, I will look at this and come back if I find a solution. Thanks!

This would be a very nice feature to have in future Moodles, given that AJAX is more or less universally used these days...

BTW, I have lately been using my own little trick I "invented", which I think would be a great addition to Moodle. Consider the following code (jQuery, but I'm sure you can YUIfy it):

<a class="ajaxlink" href="example.php#content">Link to content</a>

<div id="content">Load content here</div>

<script type="text/javascript">
    jQuery('a.ajaxlink').click(function() {
        var url = this.href.split('#')[0];
        var target = this.href.split('#')[1];

        jQuery('#'+target).load(url+'&isajax=true',function() {
//init a few JS things here, like tinyMCE and other neccessary stuff
init_js(this.id);
});
return false;
    }
</script>

The advantages to this code are several:

  • Built-in ajax functionality (so no need to write messy scripts)
  • Fallback to regular pages in case you don't have JS activated, for whatever reason
  • Non-ajax and AJAX code share the same pathways, making it a whole lot easier to maintain
  • Can easily be extended to include forms, as well
  • Possible to write a crawler for both search and automatic testing for HTML/JS/CSS compliance
  • Doesn't break middle click/right click->open as new tab/windows
  • Should you for some reason be logged out, you could still use the regular login form and redirect as normal

And two drawbacks, as far as I can see:

  • If one does middle-click, page scrolls down to #section - which isn't neccessarily a bad thing in most cases
  • Performance might be slower than just throwing the pages out there all at once, but then, if you remove the neccessary checks-and-balances like require_login(), you get potentially massive security holes...

Anyhow, just thought you might be interested in this. smile I don't suppose Moodle has anything like an init function for all common JS, which you can then target on a specific id/subsection? Because that would *really* help out, tremendously.

In reply to Per Ekström

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Hi Per,

As it so happens, I was looking at this kind of thing last night. I didn't get very far with it because I had to do some other bits. YUI actually has a module for this called Pjax (http://yuilibrary.com/yui/docs/api/classes/Pjax.html).

I believe tha it also addresses the first drawback that you mention.

It's certainly something we could consider, but at present it won't work well with the way that we do things. As an example, we would have to replace the entire <body> tag and all of it's children, so we don't actually remove much on the whole at present.

I think it would be interesting to try and look at this in more isolated instances though. One place I'm currently looking at this kind of thing is for expanding course categories on the front page as part of the course category rewrite. I'm not using Pjax for this though - I'm using Y.io and a node replacement.

I'm not entirely sure what you mean with your last question... Moodle utilises the YUI loader, so just putting a script tag in your code with appropriate loader syntax will load any dependencies, and then execute your code. I would think that this will be done by the browser as it loads the DOM...

Andrew

In reply to Andrew Lyons

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -

Hi Andrew!

Didn't know that YUI had something similar built-in, though not quite sure how that works. The biggest advantage to my solution is that there are no inline JS, no script tags or anything - just a linked in JavaScript that Makes Stuff Work(tm). But then again, I'm a very huge fan of using JS where needed by assigning HTML classes. Need a date picker? <input type="text" class="date"/>. Need a required field? <input type="text" class="required"/>. Required AND date picker? class="required date" solves that. I find having a bunch of behavior defined by classes is not only convenient, it's easier to both develop for and create. But as always, YMMV.

That said, the main problem here, is, really, a classic AJAX problem. If you load some page content through AJAX, and you wish to apply some extra, nonstandard behaviour like TinyMCE, you need some way of telling the page to convert that specific textarea box to TinyMCE or whatever else you like. This can be done in two ways:

1. Embedded scripts in the content you load
2. By defining behaviour through classes and/or ids and then initialise this behaviour on page load

I like to do 2 whenever possible, since embedded scripts often tend to get unwieldy and cumbersome. But embedded scripts are great if you just want one specific behavior for one specific place, so it's a question of using the right tool at the right place. smile

So, what I need, basicly:

1. Click link, page content loads with AJAX2. Page content contains one or more "extra" (non-HTML) behaviours
3. Page content loads
4. ???
5. Page is fully operational

Where ??? Should be some sort of initialisation script that doesn't include everything under the sun, but instead a few lines using the already-included libraries to initialise a new instance. It'd be great if there were standard scripts in Moodle that could initialise these sort of things on a per-need basis (and perhaps with class based behaviour), but it's not a big deal if there isn't, as long as the hooks are there - which they seem to be.

Anyway, sorry for rambling, I hope I've made my point clearer. If not, well back to the workshop :D

In reply to Per Ekström

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Andrew Lyons -
Picture of Core developers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Testers

Whatever you do, you will need some JavaScript and some script tag somewhere. In Moodle we recommend placing your JS content in a YUI module in a single file within your plugin, and then include it using $PAGE->requires->yui_module(). This adds minimal JS to the footer of the page to load your file and any of it's dependencies, then to execute the function you specify.

The YUI Pjax approach also targets classes and uses event delegation (this is a fairly recognised way of doing things). There is very little in the way of inline javascript (e.g. onclick attributes) in Moodle as a whole and they are gradually disappearing.

In Moodle, you would create a YUI module with a dependency on pjax, and add the following code:

new Y.Pjax({container: '#content'});

This will define a new Pjax object listening to any link with the yui3-pjax class on it (this is a setting), and will apply the content it returns to the node identified by #content. The Pjax userguide provides a fair amount of detail: http://yuilibrary.com/yui/docs/pjax/. It is also possible to use the events that pjax fires to add additional information to the page during loading (e.g. a spinner + message), select a section of the target content, change scroll, force a title change, maintain history, and a URL parameter to allow for different content return.

There's also Y.App which handles routing and transitions too. See http://yuilibrary.com/yui/docs/app/app-contributors.html for an example and http://yuilibrary.com/yui/docs/app/ for docs.

Andrew

In reply to Andrew Lyons

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -

Ok. I think I understand.

The only thing I don't quite see is how you would be able to designate three different contents on the same page, but I assume there's a setting for that as well?

That is, say I have three different forum posts - post1, post2 and post3 - and I want the reply button on each forum post give me a separate content load, e.g.

Post 1 -> reply button quotes post 1 and loads a form in content1
Post 2 -> reply button quotes post 2 and loads a form in content2
Post 3 -> reply button quotes post 3 and loads a form in content3

Probably only slightly trickier, but oh well. I understand the basics, thanks for the explanation. smile

In reply to Tim Hunt

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by sam marshall -
Picture of Core developers Picture of Peer reviewers Picture of Plugin developers

Yes, ForumNG does this. There were two approaches:

  • Prior to 2.3, I used some small (but ugly) JavaScript hacks to include it using AJAX, like you're trying to do.
  • In 2.3 I came to my senses and changed it to use an iframe.

Getting this to work in AJAX is really tricky - it's vital to remember it's not just the actual editor but also the filemanager for the editor that has to work. This is basically a nightmare and requires core code changes or horrible hacks that will be fragile and break with each release.

For ForumNG, making it work in an iframe was simple because this is done for the 'Reply' feature which is basically a self-contained form anyway. I just made a script output a page with just the form in (and suitable styling) and added this in an iframe. There's then a JavaScript event handler that runs after the form (in the iframe) is submitted, to process the results and remove the iframe again.

Because of the way the file manager works you currently have to make a round-trip php request to server when you init the editor anyway, so it doesn't even make it too much slower to do it the iframe way.

The iframe approach might not work so well when it's part of a form rather than a self-contained form that you just want to appear via AJAX, but it might well still be possible to use it that way and I would recommend attempting that rather than trying to subvert the filemanager and editor JS to actually load it in AJAX. (Of course, if there is an official way to do it, this might become feasible.)

--sam

Average of ratings: Useful (1)
In reply to sam marshall

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -

Hello Sam,

Ok, thanks. That's not quite the news I was hoping for, but atleast it saves me a bunch of time. I'll ask with the other developers and see what they say about it, worst-case we'll just roll our own TinyMCE for the parts that we've designed while waiting for a more standard "moodle way". smile

In reply to sam marshall

Re: Trying to get TinyMCE to work in 2.3 (through AJAX calls)

by Per Ekström -

Another thing struck me - what if I create an "ajax" layout in the theme, and that layout returns a mostly empty header and footer? Couldn't that work?