Dynamic menu button items in TinyMCE - dynamic

I have a custom menubutton in my tinyMCE editor that uses specific HTML elements elsewhere on the page as the menu items. I use a jQuery selector to get the list of elements and then add one each as a menu item:
c.onRenderMenu.add(function(c,m) {
m.add({ title: 'Pick One:', 'class': 'mceMenuItemTitle' }).setDisabled(1);
$('span[data-menuitem]').each(function() {
var val = $(this).html();
m.add({
title: $(this).attr("data-menuitem"),
onclick: function () { tinyMCE.activeEditor.execCommand('mceInsertContent', false, val) }
});
});
});
My problem is that this only happens once when the button is first clicked and the menu is first rendered. The HTML elements on the current page will change occasionally based on user clicks and some AJAX, so I need this selector code to run each time the menu is rendered to make sure the menu is fully up-to-date. Is that possible?
Failing that, is it possible to dynamically update the control from the end of my AJAX call elsewhere in the page? I'm not sure how to access the menu item and to update it. Something using tinyMCE.activeEditor.controlManager...?
Thanks!

I found a solution to this problem, though I'm not sure it's the best path.
It doesn't look like I can make tinyMCE re-render the menu, so instead I've added some code at the end of my AJAX call: after it has updated the DOM then it manually updates the tinymce drop menu.
The menu object is accessible using:
tinyMCE.activeEditor.controlManager.get('editor_mybutton_menu')
where mybutton is the name of my custom control. My quick-and-dirty solution is to call removeAll() on this menu object (to remove all the current menu items) and then to re-execute my selector code to find the matching elements in the (new) DOM and to add the menu items back based on the new state.
It seems to work just fine, though tweaks & ideas are always welcome!

Related

Safari Extension: window.open(...) doesn't work sometimes?

I have an injected stylesheet that calls a popup with window...open() on two occasions. One when the user clicks an HTML button, and two, when a user clicks on a context menu item. To listen for the context menu item, I need to add a listener on the injected script like so
safari.self.addEventListener("message", messageCallBack, false); // Message comes from global.html when context menu item is clicked
And the following callback
function messageCallBack(msgEvent) {
...
window.open(...)
...
}
For some reason, the popup works when the button calls window.open, but NOT when the message callback calls window.open. I'm assuming it maybe have something to do with the window object.
I suspect this is due to restrictions on window.open designed to combat pop-up ads. This means it will only work in response to a click event.
To get around this, I would recommend you open the new window from your global page using the safari.application API:
safari.application.openBrowserWindow();
safari.application.activeBrowserWindow.activeTab.url = '...';
You can also open new tabs with:
safari.application.activeBrowserWindow.openTab('foreground').url = '...';
To achieve this, you may need to send a message from your injected script to the global page.

Reloading a page in WinJS fails to attach event handlers

I have a WinJS application (a Windows 8.1 app using HTML/JS, not C#/XAML).
I've implemented a custom navbar in my default.html, with some buttons that have click event listeners attached to them. Each handler calls nav.navigate() with the url of the page corresponding to the nav button.
One of my pages (call it /pages/myPage/myPage.html) has several buttons on it. Each button has a click event listener bound to it in the page's ready function. This works fine when navigating between several pages.
However, if I'm on myPage (with working button click handlers) and click the navbar button for myPage again, the page looks like it reloads. The ready function seems to be called (i.e. it console.log statements in it are executed), but the buttons on the page seem to completely lose their click handlers!
If I navigate to another page, then navigate back, the buttons work fine again. But no matter what I do, "reloading" the page by navigating to itself (nav.navigate("/pages/myPage/myPage.html") while on myPage) causes my click handlers to be lost.
Why does this happen? My ready function is called, but somehow the click handlers are never re-attached.
Here's what the ready function for myPage looks like:
ready: function (element, options) {
document.getElementById("myButton").addEventListener("click", this.myButtonClicked);
},
Here's what the click event listener for the myPage nav button looks like (this code is in default.js):
myPageNavButton.addEventListener("click", function () {
nav.navigate('/pages/myPage/myPage.html');
});
Page nav in WinJS is just a matter of DOM replacement. When you do a nav, the target “page” contents are loaded into the DOM and then the previous “page’s” contents are unloaded. You can see this in navigator.js in the _navigating method. It creates a new element for the page being loaded, renders that fragment therein, and then unloads the old page.
The ready method for the new page, however, is called before the old page is unloaded (this was a change in WinJS 2.0, as WinJS 1.0 unloaded the old page before calling ready). The upshot of this is that when you navigate to the same page that’s already loaded, myPage.html(A) is in the DOM when you load myPage.html(B). When you execute the code in your ready method, then, getElementById will find the buttons in myPage.html(A) and so you're attaching handlers to that element. But then after you return from ready, myPage.html(A) is unloaded, so you lose the handlers. And because you never attached handlers to the buttons in myPage.html(B), they're just inert.
So what can you do about it? The best solution, in my mind, is to avoid navigating to the same page in the first place, because it's just fraught with other peril in the long run. To do this, wrap your call to nav.navigate with a check for whether you're already on the target page. Here's an implementation of a function that does that:
function navigateIfDifferent(target) {
var page = document.getElementById("contenthost").winControl.pageControl;
var fullTarget = "ms-appx://" + Windows.ApplicationModel.Package.current.id.name + target;
if (fullTarget !== page.uri) {
WinJS.Navigation.navigate(target);
}
}
This assumes that the PageControlNavigator control that you're using is in a div called "contenthost" in default.html (which is what the VS template gives you). What I'm then doing is building the full in-package URI for the target page and comparing that to the uri of the current page control, which is also a full in-package URI. You could also strip off the ms-appx:// part from the current page URI and compare to the target URI. Either way.
Anyway, with this function, replace your calls to nav.navigate with navigateIfDifferent, and you should be good.

Durandal view no more displayed if user click quickly on menus

I use Durandal 2.0 & Breeze in my SPA.
I have a sidebar menu for my drivers (Chauffeurs) where user can click on submenus (Récents, Disponibles, Indisponibles) for calling my view with different parameters. This will fill a koGrid with data. The data is fetched in the activate call and the binding of the koGrid is done in the compositionComplete.
Everything goes well most of the time. Things goes wrong when I click very quickly on submenus (calling the same view). Example: I click on 'Récents' and immediately (without waiting for the view to display) I click on 'Disponibles'.
I have the following for the activate:
var activate = function (filterParam) {
filter(filterParam);
pagedDataSource.getDataFunction = getData;
pagedDataSource.getPredicatesFunction = getPredicates;
return pagedDataSource.reload();
};
And I have the following code for the compositionComplete:
var compositionComplete = function (view) {
bindEventToList(view, '.kgCellText', gotoDetails);
$('#mySearchGrid').attr('data-bind', 'koGrid: gridOptions');
ko.applyBindings(vm, document.getElementById('mySearchGrid'));
};
When I trace the activity, I noted that if user click quickly on submenus, the activate does not have the time to finish and is called again (for the second click of the user) and the compositionComplete does not execute. Then after that, nothing more happened visually. It seems blocked.
Any idea how can I prevent this problem?
Thanks.
The migration to the latest Durandal version 2.0.1 fixed the problem.

how to attach an event to dojox.mobile.heading 'back' button

In addition to the 'back' button functioning as expected, I need to asynchronously invoke a function to update some db tables and refresh the UI.
Prior to making this post, I did some research and tried the following on this...
<h1 data-dojo-type="dojox.mobile.Heading" id="hdgSettings" data-dojo-props="label:'Settings',back:'Done',moveTo:'svStart',fixed:'top'"></h1>
dojo.connect(dijit.registry.byId("hdgSettings"), "onclick",
function() {
if (gblLoggerOn) WL.Logger.debug(">> hdgSettings(onclick) fired...");
loadTopLvlStats();
});
Since my heading doesn't have any other widgets than the 'back' button, I thought that attaching this event to it would solve my problem... it did nothing. So I changed it to this...
dojo.connect(dijit.registry.byId("hdgSettings")._body, "onclick",
function() {
if (gblLoggerOn) WL.Logger.debug(">> hdgSettings(onclick) fired...");
loadTopLvlStats();
});
As it turns out, the '._body' attribute must be shared by the Accordion widget that I just happen to use as my app's main UI component, and any attempt to interact w the Accordion rendered my entire app useless.
As a last resort, I guess I could simply forgo using the built-in 'back' button, and simply place my own tabBarButton on the heading to control my app's transition and event processing.
If the community suggests that I use my own tabBarButton, then so be it, however there has to be a way to cleanly attach an event to the built-in 'back' button support.
Thoughts?
The following should do the trick:
var backButton = dijit.registry.byId("hdgSettings").backButton;
if (backButton) {
dojo.connect(backButton, "onClick", function() { ... });
}
Remarks:
The code above should be executed via a dojo/ready call, to avoid using dijit's widget registry before it gets filled. See http://dojotoolkit.org/reference-guide/1.9/dojo/ready.html.
Note the capitalization of the event name: "onClick" (not "onclick").
Not knowing what Dojo version you use (please always include the Dojo version information when asking questions), I kept your pre-AMD syntax, which is not recommended with recent Dojo versions (1.8, 1.9). See http://dojotoolkit.org/documentation/tutorials/1.9/modern_dojo/ for details.

DojoX Mobile ListItem load HTML via AJAX and then remove from DOM

Let's say in a view I have a DojoX Mobile ListItem that is pulling an HTML view fragment into the DOM via AJAX and then transitioning to that view. Assume this is all working fine.
Now, I go back to the initial view that had that ListItem on it and click some other button that destroys that view node from the DOM. If I now click on that ListItem that previously loaded that view node into the DOM (which has now been removed), it will try to transition to a view that doesn't exist. It doesn't know that it has been removed.
Is there some type of way to tell a ListItem that it needs to fetch the HTML again because what was previously fetched no longer exists? I am not seeing anything about doing this in any documentation anywhere. I don't think a code sample is really necessary here, but I can provide a minimal one if necessary.
I went a different route and left the view exist in the DOM, and simply made a function that clears all sensitive data out of the view.
Okay, in this case, i guess you could hook the onShow function of your ListItem container(or any other onchange event). Create a listener for said handle to evaluate if your item needs reloading. Following is under the assumtion that it is the item.onclick contents showing - and not the label of your item which contains these informations
Or better yet, do all this during initialization so that your ListItem container will be an extended with custom onClick code.
Seems simple but may introduce some quirks, where/when/if you programatically change to this item, however here goes:
function checkItem() {
// figure out if DOM is present and if it should be
if( isLoggedIn() ) {
this.getChildren().forEach(function(listitem) {
if( dojo.query("#ID_TO_LOOK_FOR", listitem.domNode).length == 0 ) {
// this references the listItem, refresh contents.
// Note: this expects the listitem to be stateful, have no testing environment at time being but it should be
listitem.set("url", listitem.url);
}
});
}
}
Preferably, set this in your construct of the container for your ListItems
var listItemParent = new dojox.mobile.RoundRectList({
onShow : checkItem,
...
});
Or create listener
var listItemParent = dijit.byId('itemRegistryId');
// override onClick - calling inheritance chain once done
dojo.connect(listItemParent, "onClick", listItemParent, checkItem);