views:

367

answers:

2

I am having a Javascript kerfuffle that goes beyond my knowledge of the language.

I am creating a custom plugin for CKEditor. The plugin is an IFrame that opens in a DIV generated by CKEditor.

Inside the IFrame, I am displaying a number of images. If the user selects one of those images, the HTML code necessary to display that image is inserted into the CKEditor.

Dynamically doing this is where I'm stuck. I am connecting to the CKEditor instance from within the Iframe like so:

var CKEDITOR = window.parent.CKEDITOR;

CKEditor offers an "OK listener" that triggers when the OK button (rendered by CKEDitor) is clicked. That OK listener is outside the IFrame.

Defining an OK listener that works with static values works:

var okListener = function(ev) {
   this._.editor.insertHtml('<img class="symbol" src="my_static_symbol.gif">');
   CKEDITOR.dialog.getCurrent().removeListener("ok", okListener);
};

// Assign OK listener
CKEDITOR.dialog.getCurrent().on("ok", okListener);

But I don't know my return value yet when I assign the OK listener, so I would need to do something like this:

var okListener = function(ev) {
   this._.editor.insertHtml('<img class="symbol" src="'+my_dynamic_value()+'">');
   CKEDITOR.dialog.getCurrent().removeListener("ok", okListener);
};

// Assign OK listener
CKEDITOR.dialog.getCurrent().on("ok", okListener);

But this doesn't work because my_dynamic_value is outside the function's scope at the time it is fired by CKEditor's "OK" button.

I could of course, every time the user selects a different image in the list, update the okListener function and hard-code the current value using eval into it. But that feels like a horrible waste of resources to me.

Is there some scope trick I can use so I can access stuff from my Iframe from within okListener()?

I hope this is clear enough. If it isn't, comment and I will clarify.

+1  A: 

You can write a function that creates the listener, passing in the dynamic value (as a function or a string, depending on how the rest of the script is written).

function createOKListener(imgURLFunc) {
   return function(ev) {
      this._.editor.insertHtml('<img class="symbol" src="'+imgURLFunc()+'">');
      CKEDITOR.dialog.getCurrent().removeListener("ok", okListener);
   };
};

// Assign OK listener
CKEDITOR.dialog.getCurrent().on("ok", createOKListener(function() {iframeRef.path.to.imageURL}));

Or:

function createOKListener(imgURL) {
   return function(ev) {
      this._.editor.insertHtml('<img class="symbol" src="'+imgURL+'">');
      CKEDITOR.dialog.getCurrent().removeListener("ok", okListener);
   };
};

// Assign OK listener
CKEDITOR.dialog.getCurrent().on("ok", createOKListener(path.to.imageURL));

I suspect what's going on is that the function is executing in the context of the top-level window; whether this is true or not depends on CKEditor's event implementation. If this is the case, then you can try accessing the iframe's global variables via the iframe window (as outlined in the imgURLFunc version) or binding the value to a local variable (as done with createOKListener).

What happens is that global variables (other than window) are resolved as properties of window (imagine there's a with (window) {...} around code blocks). When CKEditor fires an event, it does so in the context of CKEDITOR, which is window.parent (from the iframe).

outis
Cheers outis and thanks for your time. Turns out it was my mistake. See my comment to the answer above.
Pekka
+4  A: 

But this doesn't work because my_dynamic_value is outside the function's scope at the time it is fired by CKEditor's "OK" button.

OK. Maybe it really doesn't work, but the explanation is wrong. In JavaScript, variables have lexical scope. This means that if my_dynamic_value is in the scope when your function (handler) is declared, then it remains there forever. What error message are you getting?

thorn
Aaaaargh. You're right. The mistake I made was not declaring the variable *before* I declared the function, thinking that it doesn't matter *when* I declare it as long as it happens in the same scope. It works now. Thanks.
Pekka
Strange. Order really doesn't matter.For example, this code outputs "5":(function(){setTimeout(function() { alert(a);}, 2000);var a = 5;})();
thorn