Automatically install toolbarbutton to firefox nav-bar, not working with insertItem - javascript

So I've read just about everything I could find on MDN, stackoverflow, etc, and it all seems to be outdated and/or not working. Here is the issue:
I want to automatically put my extension's "toolbarbutton" on the nav-bar when it is installed, similar to Chrome. What the user does after that point is up to them, although if you remove the button (with this particular extension) you might as well just remove the extension as it's useless without the button. ANYWAY...
There seems to be two methods for doing this. You can append it to the "currentSet" and make it persist, like this:
var currentset = document.getElementById("nav-bar").currentSet;
currentset=currentset + ",MYBUTTON_ID";
document.getElementById("nav-bar").setAttribute("currentset",currentset);
document.getElementById("nav-bar").currentSet = currentset;
document.persist("nav-bar","currentset");
Or, you can use "insertItem" like this:
var toolbar = document.getElementById("nav-bar");
toolbar.insertItem("MYBUTTON_ID", null);
toolbar.setAttribute("currentset", toolbar.currentSet);
document.persist(toolbar.id, "currentset");
Now, if I use the first method, it works, but it completely erases everything else on the nav bar for some reason. "currentSet" doesn't seem to have the other nav-bar buttons on it when it goes to overwrite it, and so I just end up with the default nav-bar plus my icon. Wiping out all of a user's other buttons is no good...
The second option doesn't wipe out the other options, but for whatever reason, the "insertItem" way of doing it doesn't work at all. My button never appears in the nav-bar, period.
Any ideas?

Alright, like I thought, there was nothing wrong with my code, and the problem was exactly as I described.
When the extension is loaded and the script to install the button is executed, it's done too early. At the time of execution, "currentSet" only contains the default buttons. No other extension buttons are loaded yet. As a result, if you modify the currentSet and save (persist) it, you wipe out all other buttons.
The solution (for me) was to force my "install" script to wait longer. I found that once the page had been loaded, all of the other buttons had enough time to appear. So, I simply did this:
function installButton() {
var navbar = document.getElementById("nav-bar");
var newset = navbar.currentSet + ",MYBUTTONID";
navbar.currentSet = newset;
navbar.setAttribute("currentset", newset );
document.persist("nav-bar", "currentset");
}
window.addEventListener("load", function () { installButton(); }, false);

well I use this code to add toolbar button in navbar, but this works only for the first time for the fresh installation and not for the next installation i.e. upgrading of the addon as user can move/drag the icon to different location. So, you need to try this in the new firefox profile. Here is the code:
//plcae toolbar icon
var navbar = document.getElementById("nav-bar");
var newset = navbar.currentSet + ",MYBUTTON_ID";
navbar.currentSet = newset;
navbar.setAttribute("currentset", newset );
document.persist("nav-bar", "currentset");
and here is the code for XUL Overlay:
<toolbarpalette id="BrowserToolbarPalette">
<toolbarbutton id="MYBUTTON_ID" inserbefore="searchBar" class="toolbarbutton-1 chromeclass-toolbar-additional"
label="MYBUTTON_ID" tooltiptext="MYBUTTON_ID"
onclick="MYBUTTON_ID()"/>
</toolbarpalette>
or you can force the icon to display in nav bar against user will, this will take effect after each firefox restart, however not recommended
var navbar = document.getElementById("nav-bar");
var newset = navbar.currentSet;
if (newset.indexOf("MYBUTTON_ID") == -1)
{
if (newset.indexOf("reload-button,stop-button,") > -1)
newset = newset.replace("reload-button,stop-button,", "reload-button,stop-button,MYBUTTON_ID,");
else
newset = newset + ",MYBUTTON_ID";
navbar.currentSet = newset;
navbar.setAttribute("currentset", newset );
document.persist("nav-bar", "currentset");
}

Related

addeventlistener function argument is null on one webpage, but functions properly on others

I'm doing the frontend on my clients website and have come across a strange issue. On certain pages (maybe 5% of the total pages on the site), the simple hamburger icon javascript doesn't function at all.
Here is the JS (which, again, works on 95% of the pages):
const userBox = document.querySelector(".wdgt-user-box");
const userBtn = document.querySelector(".wdgt-login");
function toggleUserBox() {
if (userBox.style.display === "block") {
userBox.style.display = "none";
}
else {
userBox.style.display = "block";
}
}
userBtn.addEventListener("click", toggleUserBox);
Some things to rule out:
Z-index is not an issue. I can click on the icon (I tried adding an href and it worked).
The HTML and JS (and how the JS is linked) is exactly the same on all pages. The JS is included at the bottom of the HTML in all cases
The queryselector appears to be working in all cases when inspecting with Chrome dev tools
The one difference I noticed when setting up breakpoints is that when hovering over "toggleUserBox" on the pages where it doesn't work, Arguments: null. Here is the difference between the two:
Of course, please let me know if there is more useful info I can find. I'm new to Chrome dev tools.
Since you are using a class as the query selector, you could try checking if there is more than one object with the same class name on the page

Caching problem with Firefox CSS set via javascript

I use a WordPress plugin to add via shortcodes one, two or three 'fontsampler' objects per specific page that each incorporate a text box and a font select box per fontsampler object. The JavaScript hides all select boxes via CSS except the last one.
In Chrome and Edge there is never any problem but in Firefox mostly all the select boxes show and sometimes just the last (F5 always shows all select boxes, ctrl F5 gives variable results).
The webserver also uses Litespeed cache and goes via Cloudfare CDN but I can't see this is relevant since only a Firefox issue.
NB I also tried the code and just the plugin on a vanilla wordpress test server (so no server caching) and didn't see the issue so I'm not sure how to proceed.
Here is the code if this helps:
jQuery(document).ready(function( $ ){
$("body").on("fontsampler.event.afterinit",".fontsampler-wrapper" , function () {
// ‘this’ is div.fontsampler-wrapper.initialized NB all 3x fontsamplers are initialised on first event but not drawn on page e.g. fontpickers (event fires up to three times)
//first count the number of fontsamplers on the page
var fsCount = $($(document).find(".fontsampler-interface")).length;
//check to see if last fontsampler has fully initialised (painted elements on page) else end event and wait for next
var fsStub=".fontsampler-id-";
var fsLast=fsStub+fsCount;
if ($($(document).find(fsLast)[0]).find(".fontsampler-ui-block.column.fontsampler-ui-block-fontpicker")[0] !== undefined) {
var fontsampler2Exists = $(document).find(".fontsampler-id-2").val();
var fontsampler3Exists = $(document).find(".fontsampler-id-3").val();
if (fontsampler3Exists !== undefined) {
//Three lines
// $(fs).find(".fontsampler-ui-block.column.fontsampler-ui-block-fontpicker")[0].style.display='none';
$($(document).find(fsStub+1)[0]).find(".fontsampler-ui-block.column.fontsampler-ui-block-fontpicker")[0].style.display='none';
$($(document).find(fsStub+2)[0]).find(".fontsampler-ui-block.column.fontsampler-ui-block-fontpicker")[0].style.display='none';
}
else if (fontsampler2Exists !== undefined) {
// Two lines
$($(document).find(fsStub+1)[0]).find(".fontsampler-ui-block.column.fontsampler-ui-block-fontpicker")[0].style.display='none';
}
else {
// One line so don't hide any font picker
}
}

JavaScript only being called once in Squarespace

I have some custom JavaScript on my SquareSpace site that manipulates Product titles beyond what you can do with SquareSpace's default style editor. It works when initially loading the page (https://www.manilva.co/catalogue-accessories/) but if you click on any of the categories on the left, the styling resets to the default.
I'm assuming the JavaScript is being overwritten by the SquareSpace style, but I can't figure out why. Perhaps I'm calling the function in the wrong place?
Any suggestions would be helpful.
Thanks!
Current code:
document.querySelectorAll(".ProductList-filter-list-item-link".forEach(i=>i.addEventListener("click", function()
{
var prodList = document.querySelectorAll("h1.ProductList-title");
for (i = 0, len = prodList.length; i < len; i++)
{
var text = prodList[i].innerText;
var index = text.indexOf('-');
var lower = text.substring(0, index);
var higher = text.substring(index + 2);
prodList[i].innerHTML = lower.bold() + "<br>" + higher;
});
The source of your problem is that your template has AJAX loading enabled. There are currently a couple generally-accepted ways to deal with this as a Squarespace developer:
Disable AJAX loading
Write your javascript functions in a
manner that will run on initial site load and whenever an "AJAX load" takes place.
Option 1 - Disable AJAX:
In the Home Menu, click Design, and then click Site Styles.
Scroll down to Site: Loading.
Uncheck Enable Ajax Loading.
Option 2 - Account for AJAX in Your JS
There are a number of ways that developers approach this, including the following, added via sitewide code injection:
<script>
window.Squarespace.onInitialize(Y, function() {
// do stuff
});
</script>
or
<script>
(function() {
// Establish a function that does stuff.
var myFunction = function() {
// Do stuff here.
};
// Initialize the fn on site load.
myFunction();
// myFunction2(); , etc...
// Reinit. the fn on each new AJAX-loaded page.
window.addEventListener("mercury:load", myFunction);
})();
</script>
or
<script>
(function() {
// Establish a function that does stuff.
var myFunction = function() {
// Do stuff here.
};
// Initialize the fn on site load.
myFunction();
// Reinit. the fn on each new AJAX-loaded page.
new MutationObserver(function() {
myFunction();
// myFunction2(); , etc...
}).observe(document.body, {attributes:true, attributeFilter:["id"]});
})();
</script>
Each of those works for most of the latest (at time of writing) templates most of the time. Each of those have their advantages and disadvantages, and contexts where they do not work as one might expect (for example, on the /cart/ page or other "system" pages). By adding your code within the context of one of the methods above, and ensuring that the code is of course working in the desired contexts and without its own bugs/issues, you will have your code run on initial site load and on each AJAX page load (with some exceptions, depending on the method you use).
Your problem is the page does not reload when clicking a button on the left, just some elements are removed, added and replaced. The changed elements will not be restyled. You will need to re-run your JavaScript after one of those buttons is clicked. Perhaps something like this:
document.querySelectorAll(
".ProductList-filter-list-item"
).forEach(
i=>i.addEventListener(
"click", ()=>console.log("hello")
)
)
where you replace console.log("hello") with whatever resets your formatting.

How do I know when a new (Spry) accordion tab has been selected (similar to "on click")? I want to attach my own behaviors

I have an accordion set up to handle registration. I am wanting to validate the data entered on each panel when the user clicks on a different panel tab. I have a continue button on each panel, and am able to validate to my heart's content when the user uses that to go to the next panel.
My problem is that they can also click independently on the accordion tabs (and I want them to be able to skip around for editing purposes), but I would like to validate on those events too.
I've done a bunch of searching, but have not found a satisfactory answer. I am fairly new to Javascript and super-brand-new to jQuery, so please, if you have code snippets for me, be thorough in explaining them.
This should be a straightforward problem (similar to on-click, etc.). I'm quite surprised and frustrated that I haven't found an answer yet.
Edit:
Eric, I couldn't get this to work. Here is my version. I put it in the head section. I have some test code in there that has worked reliably for me in the past (changing the label on one of the tabs). I'm assuming this code has worked for you? Anyway, thanks for your help and I hope we've understood each other sufficiently.
// add capability to detect when accordion tab has been clicked
RegFormAccordion.addEventListener('click', function(e){
var btnElement;
(function findAccordionButton(el){
//e.target is the original element actually clicked on
//the event bubbles up to ancestor/parent nodes which is why you can listen at
//the container
if(!btnElement){ btnElement = e.target; }
else { btnElement = el; }
if(e.target.className !== 'accordionBtn')
{
findAccordionButton(btnElement.parentNode);
}
else
{
var curr_panel_index = RegFormAccordion.getCurrentPanelIndex();
document.getElementById("verify-reg-panel-label").innerHTML = "Index = " + curr_panel_index; // test code to see if it's even getting here
if (curr_panel_index == 1) // contact section
{
ValidateContact();
}
else if (curr_panel_index == 2) // payment section
{
ValidatePayment();
}
UpdateVerifyPanel(); // update contents of verification panel
}
})()
} );
Event delegation.
someAccordianContainer.addEventListener('click', function(e){
var btnElement;
(function findAccordionButton(el){
//e.target is the original element actually clicked on
//the event bubbles up to ancestor/parent nodes which is why you can listen at
//the container
if(!btnElement){ btnElement = e.target; }
else { btnElement = el; }
if(e.target.className !== 'accordionBtn'){
findAccordionButton(btnElement.parentNode);
}
else { doSomething(btnElement); }
})()
} );
You will have to normalize for IE<=8 however if you're supporting older browsers, since it uses a proprietary attachEvent method. Hit quirksmode.org for the details or just use something like jQuery or MooTools.
OK. I found the function that SpryAccordion.js uses to open a new panel and added my own code. Simple and elegant. It's not what I would normally do (usually I leave "libraries" alone). But if you make it editable without giving me another way to take needed control, then the hack is gonna happen.
If I need to use another accordion somewhere else on my website, I will have to double check that I have the correct accordion before invoking the hack. A trade-off I'm willing to make. It works perfectly now. Here is the code:
Spry.Widget.Accordion.prototype.openPanel = function(elementOrIndex)
{
var panelA = this.currentPanel;
var panelB;
if (typeof elementOrIndex == "number")
panelB = this.getPanels()[elementOrIndex];
else
panelB = this.getElement(elementOrIndex);
if (!panelB || panelA == panelB)
return null;
// Start Becca's code
var panelIndex = this.getPanelIndex(panelA);
if (panelIndex == 1) // contact info panel
{
if (ValidateContact())
UpdateVerifyPanel();
else
return null;
}
else if (panelIndex == 2) // payment info panel
{
if (ValidatePayment())
UpdateVerifyPanel();
else
return null;
}
// End Becca's code
var contentA = panelA ? this.getPanelContent(panelA) : null;
var contentB = this.getPanelContent(panelB);
...
...
...
};
Yes, all I wanted was the same control over the panel tabs as I have over my own user-defined buttons, to make sure I could both validate before moving on, and to update my verification screen after any edit the user makes, not just the ones where they happen to hit my continue button. I'm a happy camper. So glad I took a couple of days off.
I hope this helps someone get more control over their own accordions. So glad I don't have to do a crash-course on jQuery when all I want right now is to get my blasted website up.

jquery thickbox reference problem

Completely restated my question:
Problem: Losing reference to an iFrame with Mozilla firefox 3.6 and 4.0
More info:
- Works fine in internet explorer 8 64-bit and 32-bit version.
How to reproduce? In Mozilla: Open the editor accordion menu. Click the 'editor openen' link, in the editor fill in some random text, then click 'bestand opslaan'. Fill in a name and click on 'save'. The content of the editor will be downloaded in HTML format.
Close the save file dialog box by clickin outside of it or on the specified buttons. Click on the 'bestand opslaan' button again and try to save your content to a file. You'll see nothing happening.
The problem isn't there in IE8. Try opening it in there.
Firebug tells me this the second time you open the save dialog:
iFrame.document is null
Example Link: http://www.newsletter.c-tz.nl/
More info:
- switched from thickbox to colorbox to try and resolve this issue and because thickbox isn't supported for a long time now.
- colorbox gives me the same problem so I don't think it is this.
- tried googling for iframe reference error and like, found nothing.
- tried putting the iframe code outside of the div that is called by the colorbox script, it retains it reference but not when I put it back inside that div.
Thanks to: JohnP who offered to open a 'hunt' on this.
Edit:
I thought maybe the saveFile.php file was causing trouble to the parent of the iframe but after removing it from the action variable in the editor.php script it still fails with the same error after you open the dialog for a second time.
Can someone maybe write a script that iterates through iframes by name and when the rignt iframe is found reference it to a var? I want to try it but don't know how..
I can't explain why it's work the first time for Firefox, but in Firefox the function to used for get iframe is different of IE : How to get the body's content of an iframe in Javascript?.
So, replace your JavaScript function "saveToFile" to this :
function saveToFile() {
var saveAsFileName = document.getElementById('saveAs_filename').value;
var currentContent = tinyMCE.editors["editor_textarea"].getContent();
var editorFileName = document.getElementById('editor_filename');
var iFrameTag = document.getElementById('saveAs_Iframe');
var iFrame;
if ( iFrameTag.contentDocument )
{ // FF
iFrame = iFrameTag.contentDocument;
}
else if ( iFrame.contentWindow )
{ // IE
iFrame = iFrameTag.contentWindow.document;
}
var inframeEditorFileName = iFrame.getElementById('editor_filename');
var inframeEditorContent = iFrame.getElementById('editor_textarea');
editorFileName.value = saveAsFileName;
inframeEditorFileName.value = saveAsFileName;
inframeEditorContent.value = currentContent;
iFrame.editor_self.submit();
}
I replace the function with Firebug and it's works for me.
Update :
You can also used a crossbrowser solution, more simple, thanks to jQuery :
function saveToFile() {
var saveAsFileName = document.getElementById('saveAs_filename').value;
var currentContent = tinyMCE.editors["editor_textarea"].getContent();
var editorFileName = document.getElementById('editor_filename');
editorFileName.value = saveAsFileName;
$("#saveAs_Iframe").contents().find("#editor_filename").val(saveAsFileName)
$("#saveAs_Iframe").contents().find("#editor_textarea").val(currentContent)
$("#saveAs_Iframe").contents().find("form[name=editor_self]").submit();
}

Categories