TinyMCE update Toolbar (after init) when you have Editor on method - javascript

I'm working on a Google Fonts plugin for WordPress and I try to have the same effect as the core WYSIWYG editor. Basically when you click on element (inside the Editor) with font class I want to get the class and then based on that reload the font family/style listbox in the Toolbar.
(I found couple of hacks here on SO like this one Proper Way Of Modifying Toolbar After Init in TinyMCE but nothing that works like the WP core example)
There is the same functionality when you click on P, H1, H3, H3 ... How they do it? Can you point me at least to the JS file in WordPress distro; I think I can figure it out if see the code.
Here is GIF that demonstrates what I'm talking about. Thanks in advance.

I found the solution and because it's not a hack, like the other ones I found on SO, I will post it in here and hopes it will help anyone else that's trying to do something similar.
First to access the button/listbox need to use onpostrender with a callback function.
editor.addButton( 'developry_google_font_family_button', {
type : 'listbox',
onpostrender : fontFamilyNodeChange,
value : '',
...
Next the callback function should look something like this:
function fontFamilyNodeChange() {
var listbox = this;
editor.on('NodeChange', function( e ) {
// This next part is specific for my needs but I will post it as an example.
var selected = [];
if ( $( e.element ).hasClass( 'mce-ga' ) ) { // this a class I add to all elements that have google fonts format
// Then I strip the classes from classList that I don't need and add the rest into an array (e.g ['roboto', '100'])
var gfont_options = $( e.element ).attr('class')
.replace('mce-ga', '')
.replace('mce-family-', '')
.replace('mce-weight-', '')
.trim()
.split(' ');
selected.push( gfont_options );
// At end I add the new value to listbox select[0][0] (e.g. 'roboto')
listbox.value(selected[0][0]);
}
});
}
And here is an example:

Related

Manually force trigger a change event on select in nested form using stimulusjs

Using Haml, ruby 2.7.2, rails 6.1.2.1, stimulus ^2.0.0
So, I am using a nested form that shows a select type. Based on the value of the select type, I will either show or hide a div. I'm converting my coffeescript to stimulus, and I'm not sure how to force trigger a change event, or instead just run the code that checks the value of the select to decide if I should hide or show the div. In the past, Inside my html.haml view I would call this:
:javascript
$('select.answer_type').change()
Based on another post in here: https://discuss.hotwire.dev/t/triggering-turbo-frame-with-js/1622, I tried to change it to say:
:javascript
q_typeTarget.dispatchEvent(new Event('change'));
And that didn't work. Maybe there is another way to do this, or I'm using the wrong vocabulary to look up the example I want to do? I'd rather just manually execute the Controller#action and give it the select object to work off of right away, but I'm not sure how to do this in html/script tag. Thanks for any help!
Here is my relevant code that should help:
_question_fields.html.haml
.nested-fields.question
.form_group.grid-x
.fields.cell.shrink
= f.select :answer_type,
options_for_select(["String","Option","Checkbox"],
f.object.answer_type),{},
class: 'answer_type',
data: {nested_form_target: 'q_type',
action: 'nested-form#update_q_type'}
.cell.answers_group{ style: 'visibility: hidden; display: none'}
= render partial: 'answers', locals: { f: f, q_level: q_level }
:javascript
q_typeTarget.dispatchEvent(new Event('change'));
nested_form_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = ["q_type", "answers"]
update_q_type(event) {
event.preventDefault()
console.log('You have selected ' + this.q_type )
let d_question = event.target.closest(".question")
let d_select = d_question.querySelector("select")
let d_answer = d_question.querySelector(".answers_group")
console.log('Select: ' + d_select.value )
switch(d_select.value) {
case 'Option':
case 'Checkbox':
console.log(' Show Answers ')
d_answer.style.visibility = "visible"
d_answer.style.display = null
return
default:
console.log(' Hide Answers ')
d_answer.style.visibility = 'hidden'
d_answer.style.display = 'none'
return
}
}
Here is an example .gif of the nested forms, shown below:
I add a Question
I select an option from the select tag, which triggers the select → change event: nested-form#update_q_type
If I select either Option or Checkbox, then I show the div, otherwise I hide the div
Now, here is why I'm trying to trigger the select → change event. If I start to edit this record again, it loads the data just fine. However, it does not know when to show the answers div or not. That's why I need to trigger the select → change event so that it can properly set up the answers div to either hide or show. Otherwise, it's defaulted to always 'hide'.
I have another .gif below. As you can see, it loads the select with Option selected. if I try to select Option again, it doesn't do anything...because it hasn't really changed. If I change it to Checkbox, it of course updates. So basically, I need to do this onLoad, you can say.
Maybe there is a different way to do this? Thank you for your help!
Update: I didn't realize my html tags were being interpreted as html. Oops. Changed all tags to italicized words. Ex: '/</select/>/' is now just select. Also I expanded on the question for more clarification.
StimulusJS has an initialize function that essentially runs when the page loads. You would use that for this purpose. The only other issue here is that you're passing in an event to update_q_type and you won't have an event on initialize. So what I find works is to extract most of the code to a separate function that my initialize and click function can both call. This helps you keep your code DRY.
export default class extends Controller {
static targets = ["q_type", "answers"]
initialize() {
this.switch(this.q_typeTarget)
}
update_q_type(event) {
event.preventDefault
this.switch(this.q_typeTarget)
}
switch(q_type) {
...
}
}
One thing to note, it's not clear if you have one stimulus controller running here or one for every select box combo. You could do the 2nd and not have to use the closest method. It's meant to be very modular.

Jquery script not working to alter CSS on change

I've added some custom elements to be included with my WooCommerce account page to be seen with the order history. Unfortunately the page is setup with tabs to only display the information pertaining to the active tab.
I'm not very familiar with jquery, but I thought it would be simple enough to use Jquery to hide the divs I added when the order history has a display of none.
I added the following script to my theme's main.js file:
$(document).ready(function(){
var display = $('.my_account_orders');
if(display.css("display") == "none") {
$('.paging-nav').css("display","none");
}
});
When the class .my_account_orders has a display of none it should change the div I added (.paging-nav) to have a display of none. But it just doesn't work.
Is there something wrong with this script or do I need to do something special to initiate it? Since it's in my theme's main.js file and I used $(document).ready(function() I figured it would just load with the page.
Any help with this would be greatly appreciated.
Instead of using:
var display = $('.my_account_orders');
Implement it into the if statement like this:
if($('.my_account_orders').css("display") == "none") {
Because originally it is trying to find a variable called $display, so it would return a syntax error of undefined.
You've got an errant $ in your if statement. This should work instead:
$(document).ready(function(){
var display = $('.my_account_orders');
if(display.css("display") == "none") {
$('.paging-nav').css("display","none");
}
});
Also keep in mind that your var display is only going to match the first element that has a class of my_account_orders, so if there are multiple elements with that class, and they don't all have the same display, you could get unexpected results.
Try this:
$(document).ready(function(){
var display = $('.my_account_orders');
if(display.css("display") == "none") {
$('.paging-nav').css("display","none");
}
});
I believe it's a very lame way to check for a css property such as display to determine if an element is hidden or not. With jquery, you can make use of :hidden selector which determines whether an element is hidden and return a bool value.
$(document).ready(function(){
if($('.my_account_orders').eq(0).is(":hidden")) // eq(0) is optional - it basically targets the 1st occurring element with class 'my_account_orders'
{
$('.paging-nav').css("display","none");
}
});
Example : https://jsfiddle.net/DinoMyte/sgcrupm8/2/

Replacing Specific Text in a page

I am trying to replace a specific snippet of text inside a paragraph. I am just trying to replace the phone number. The code inside the body i am unable to access due to it being pushed to us via our partner.
To see what options are available please contact a lodging specialist
via calling 866.264.1842.
I have tried
$("body").html(
$("body").html().replace(/866.264.1842/g,'888.888.4754') );
This works however it breaks other code on our page and makes our calendar picker not work. Is there a way just to do this with css. I have access to the CSS but not the JS that is running on the page.
Let's assume the phone number is in a div which ID is "contact". Then you have to do that:
$("#contact").html($("#contact").html().replace(/866.264.1842/g,'888.888.4754'))
That way you don't remove the DOM modifications made by your calendar picker javascript code.
I have rewritten a function for this, hopefully this should work for you
function replaceDOMText(str, replaceWith) {
$('body:contains(' + str + ')').contents().each(function() {
if (this.nodeType == 3) {
$(this).parent().html(function(_, oldValue) {
return oldValue.replace(RegExp(str, "g"), replaceWith);
})
}
});
}
// Call like this
replaceDOMText("866.264.1842", "888.888.4754" );
Play with this fiddle

Title attribute in Firefox

The title attribute on a particular HTML element is not displayed in my application if viewed in Firefox. There are multiple topics explaining this problem. I was unable to find a sollution that would fit my needs. So I ask if you can please help.
I have a number of divs lined up. On mouseover each of the div's should display a different value(title). The title attribute works fine in Chrome but I need something simillar for Firefox.
The title attribute is set dynamically from Javascript!
My Javascript:
dojo.connect(div, 'mousemove',rasterTimeDisplay);
function rasterTimeDisplay() {
dojo.attr(evt.target, 'title', "some new title");
}
Why not to store the data to be displayed in another attribute ? Because you use javascript to feed your data-storage attribute, you can whatever you want.
For example, with jQuery :
//I feed my data-storage attribute
$("#my_div").attr('my-data', '<h1>my content to be displayed</h1>');
//And then i bind the hover event to toggle displaying of this data
$("#my_div").hover(
function(){
$(this).html($(this).attr('my-data'));
},
function(){
$(this).html('');
}
);
Or with standard JavaScript :
document.getElementById('my_div').my_data = '<h1>my content to be displayed</h1>';
document.getElementById('my_div').onmouseover = function(){
this.innerHTML = this.my_data;
};
document.getElementById('my_div').onmouseoout = function(){
this.innerHTML = '';
};
Sorry if i missunderstood you. However, you are trying to trigger the title attribute on hover? But the title attribute is already triggered by hover on default:
So if you just add the attribute to your desired element, you will get the extra information on hover.
var titles = document.getElementsByClassName('title');
for(var i = 0; i < titles.length; i++)
{
titles[i].title = 'Hover information ' + i;
}
jsFiddle
I you're interesting in the jQuery way to do this:
$('.title').attr('title', 'Hover information');
Still doesn't work?
Step-1: First try to run your firefox client in safe-mode. Your problem might be solved now. If this is the case proceed to step-2. Else... Well i would suggest you to update your grapical driver or install a newer version.
Step-2: Disable your Hardware Acceleration(AH).
Check another answer about this here: https://support.mozilla.org/nl/questions/860902
Now if you just want a work around, to even let the oldest Firefox browsers support this. You can find one here: Tooltips (title="...") won't show in Firefox
I hope this solved your problem.

jQuery - what does the newHeader property/field do?

I admit to being somewhat of a copy and paste JavaScript developer (with a strong background in other languages). I'm using the jQuery accordion, and using cookies to save the selected accordion section. I found some code which I integrated into my code. The key section is as follows.
change: function (event, ui) {
var index = $(this).find("h3").index(ui.newHeader[0]);
$.cookie(accordion, index);
}
This works, but I hate using code I don't understand. I understand that the index is discovered by using the find method (which makes the assumption that I don't have any h3s within the content), but what I don't understand is what the ui.newHeader[0] is doing. What is the newHeader array, and what is its purpose here?
Thanks,
Erick
Looking at the source for jquery.ui.accordion.js, it's just an object that contains the newly selected element.
You can see for yourself if you just check out the source:
// find elements to show and hide
var toShow = clicked.next(),
toHide = this.active.next(),
data = {
options: options,
newHeader: clickedIsActive && options.collapsible ? $([]) : clicked,
oldHeader: this.active,
newContent: clickedIsActive && options.collapsible ? $([]) : toShow,
oldContent: toHide
},
down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] );
this.active = clickedIsActive ? $([]) : clicked;
this._toggle( toShow, toHide, data, clickedIsActive, down );
return;
},
newHeader is not an array, it's an object that represents the newly selected element. The code you posted finds all h3 elements in the accordion element, and then it takes the index of the newHeader. The element that newHeader represents changes each time the accordion changes.
It's exposed by the accordion widget. The newheader property holds the header of the activated element that the accordion opened.
See also the doc for the change event.

Categories