Modifying chrome extension for new Twitter UI - javascript

I have a chrome extension for Twitter where I add a button to all those tweets which have images in them. The code in the content script is something like this:
$(".tweet, .js-tweet").each(function() {
var username = $(this).attr('data-screen-name');
var tweetid = $(this).attr('data-tweet-id');
if (username != null) {
var mediaLinkContainer = $(this).find(".card2")
addButtonToMediaLinkTweets(tweetid, mediaLinkContainer)
// addOverlayToMediaLinks()
var outerContainer = $(this).find(".AdaptiveMediaOuterContainer")
var mediaContainer = $(this).find(".AdaptiveMedia-photoContainer")
if (mediaContainer.length) {
console.log("Photo found in link")
addButtonToPosts(tweetid, outerContainer)
// addOverlayToPosts(outerContainer)
}
}
});
This worked perfectly with the older UI of Twitter, but this doesn't work after the new UI was rolled out. All the class names have changed and the view hierarchy is different as well. I'm trying to navigate through all of that to make my extension work again.
The following class name appears a lot of times - css-1dbjc4n I tried the following to iterate over all the tweets:
$('.css-1dbjc4n.r-1ila09b.r-qklmqi.r-1adg3ll').each(function() {
console.log($(this).html())
})
.css-1dbjc4n r-1ila09b r-qklmqi r-1adg3ll are the classes assigned to the div that is at the second to top most level of a tweet (the topmost div does not have a class or id). However, console.log does not print anything in this case. I need to iterate over the tweets to be able to add my UI element.
Edit:
It seems that jQuery has issues with the new Twitter UI. When I wrote the following in vanilla JavaScript:
var tweetDivs = document.getElementsByClassName("css-1dbjc4n r-1ila09b r-qklmqi r-1adg3ll")
console.log(tweetDivs)
//get images inside the tweets and add button on top of these images
$(tweetDivs).each(function(tweet) {
console.log(tweet)
})
I get a HTML collection in tweetDivs. However, I'm unable to iterate over its elements as its length is 0. The elements show up in console though, but that's probably because the DOM hasn't loaded when this gets called. That's weird because I'm calling the above code from document.ready. I tried replacing document.ready with document.addEventListener("DOMContentLoaded", function(){}) and changed run_at in manifest.json file to document_start but it did not make a difference.

Related

My code executed only in console but not working in .js file

I have tried the following code to replace text character to the img tag inside div message.
However the code is working fine while i do in console. But when I try the same in my sticker.js is not working. Can anyone tell me the reason?
The idea to make sticker box for chat app codded with Javascript and jQuery.
User 1 will send :sticker1: to public room and replaced with img src inside the #div
Now I'm trying document to call the script when div is showing but still not working.
$(document).ready(function() is not working
And $(function() is not working either.
My chat app using template folder /template/*.php and web-cache folder to load the design. I am searching every where to find some solution but no luck.
I need help please
$("#chat-sticker-container").ready(function stickers() {
var emoCodes = [
':sticker1:',
];
var $this = $("body");
emoCodes.forEach(function(code) {
var image = '<img src="https://sevendays.co.in/wp-content/uploads/2019/02/' + code.replace(/:/g, "") + '.jpg">';
$this.find('p.chat-sticker').html(function(index, html) {
return html.replace(new RegExp(code, "g"), image);
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="js/sticker.js"></script>
<dive id="chat-sticker-container">
<p id="chat-sticker" class="chat-sticker">:sticker1:</p>
</dive>
Make sure your JS is executing AFTER the html is in the DOM, this includes putting your script at the end of your body tag.
Since the script is called before the html is defined its essentially working with nothing, putting the after the html should fix the issues.
By looking at your JSFiddle example I think you need to use the MutationObserver API. This way you can check if an element, or the children of an element have been changed, added or removed.
I'm not able to figure out what the container is where the chat divs are added to the page, so I can only instruct you. But based on your fiddle I'm selecting the chat-main-scroll-chatroom div.
// List of emoCodes
var emoCodes = [
':sticker1:',
];
// Select chat container. Change if necessary.
var chatContainer = document.getElementById('chat-main-scroll-chatroom');
// Create a new observer.
var observer = new MutationObserver(function(entries) {
// Check for emocodes whenever something has changed.
emoCodes.forEach(function(code) {
var image = '<img src="https://sevendays.co.in/wp-content/uploads/2019/02/' + code.replace(/:/g, "") + '.jpg">';
$('p.chat-sticker').html(function(index, html) {
return html.replace(new RegExp(code, "g"), image);
});
});
});
// Check if children are changed
var options = { childList: true }
// Observer the chat container and check if itself or children are changing.
observer.observe(chatContainer, options);
So I really hope that this will help you out. In the best case scenario you would be able to filter the text that has been sent before the message is sent. If the API of your chat app provides such controls, then please use them.
Best of luck!

HTML5 / JS - preProcess of uploaded html page and simulate pagebreaks

I'm making a html-5 based report generator. I created a button to upload a [HTML] page containing multiple paragraphs and tables, which is continuous.
Now my task is to display the whole contents into separated a4-sized pages, just like in Microsoft Word.]
This is the sketch: >>>LINK<<<
Here are part of my codes.
function xx (){
var fi = document.getElementById('fi').files[0];
reader.onload = function (e){
var reader = new FileReader();
var inner ="";
inner += this.result;
inn.innerHTML ="<center><div class='bg' id='0'><div id='testmain'>"+inner+"</div></div></center>";
}
reader.onerror = function (e){
dd.innerHTML = "error<br>";
}
reader.readAsText(fi);
}
After displaying the result of pages, users can click a specific part of the paper, just like a paragraph, then a pagebreak is created and the pages changes, the remaining content are pushed starting from top of next page.
Could you please give me some ideas about how to realize it?
Instead of using comments as chat to present my suggestion, here's my answer:
I once tried to do such a thing, back in html4. Here's the logic I was using. Create a div that has the exact size of your page CONTENT (after margins and all) put all your content in it and cycle through its direct children. If the current child's bottom is lower than his parent, take it and all the following children and put them in a new div CONTENT. Rinse and repeat.
For this, you will need to calculate the height of the container and cross-check it against the offset+height of the elements. My vanillaJS is a bit rusty as for browser specifics and all... So I will display the logic using jQuery but most of it can easily be made in pure JS. The code will assume that we have a div.page that has the right CSS to make it exactly the size of a content page, and that will not resize to content (overflow:hidden) and the document will contain one of those div with all the content of what should be in the pages...
$(document).ready(function(){
var $page = $('div.page');
var newPage = true;//To track if we loop
while(newPage){
newPage = false;
$page.children().each(function(){
if($(this).offset().top+$(this).outerHeight() > $page.offset().top+$page.height()){
$page = $('<div>').addClass('page').appendTo('body');
$(this).nextAll().appendTo($page);
$(this).prependTo($page);//Don't forget the element too.
newPage = true;
}
});
}
});

ZeroClipboard user script adding in mouse over, working in firefox, but not chrome

I am using zeroclipboard to add a "copy" link to each row in a fairly large list, within a user script. To accomplish that, I using a method similar to the one listed on this page, where the ZeroClipboard.Client() element for each row is created when the user mouses over the row. This is working great in FireFox, but not in Chrome.
Also as a note: I copied the contents of the ZeroClipboard.js file into the user script itself instead of including it in an external file.
Here is the markup that creates the copy button for each element
<span style="color:blue; text-decoration:underline; cursor:pointer" id="copy_'+id+'" class="CopyLink" link="'+url+'" onmouseover="clipboard.add(this)">Copy</span>
Here is the code segment that adds the clipboard's client object:
function main(){
window.clipboard = {
load: function (){
if(!clipboard.initialized){
ZeroClipboard.setMoviePath("http://www.swfcabin.com/swf-files/1343927328.swf");
clipboard.initialized=true;
console.log("Clipboard intialized");
}
},
add: function(element){
clipboard.load();
var clip = new ZeroClipboard.Client();
console.log('Clipboard client loaded: ' + element.id);
clip.glue(element, element.parentNode);
console.log('Clipboard glued: ' + element.id);
clip.setText(element.getAttribute('link'));
console.log('Clipboard text set: ' + element.getAttribute('link'));
clip.addEventListener('complete',function(client,text) {
console.log('Clipboard copied: ' + text);//doesn't fire in chrome
});
clip.addEventListener('load',function(client) {
console.log('Clipboard loaded: ' + element.getAttribute('link'));
});
}
}
//other code in user script including injecting above markup
//as well as contents of ZeroClipboard.js
window.ZeroClipboard = { ... }
}
var script = document.createElement("script");
script.appendChild(document.createTextNode('('+main+')()'));
(document.head || document.body || document.documentElement).appendChild(script);
In this block, every console.log fires in FireFox when I mouse over and click the copy span, but in chrome, all except the 'complete' listener fire. I was able to verify that ZeroClipboard is working in my Chrome by using the example on this page. I am also able to verify that the flash object is being added to the page in the correct location, but it is simply not responding to a click.
Since the zeroclipboard code is no longer being maintained according to the site, I'm hoping someone out there can help me out. I'm thinking there is possibly some issue with dynamically adding the embedded flash objects in chrome on mouseover, or perhaps some difference between user scripts in chrome vs firefox with greasemonkey? Any help would be greatly appreciated, thanks
I'm not sure the reason behind it but I have been running into this on Chrome as well. I had two zeroclipboard implementations, one that was visible on page load, and one that was only visible when the user opened a dialog. The one that was visible on page load worked as expected, but the other one didn't. In order to "solve" the issue, I had to render the zeroclipboard link, set its absolute position to be off the screen (-500 px), then add some javascript to move the link into place when the dialog opens. This is an ugly solution but I think is the only way to get it to work in Chrome. Your case is particularly hairy since you have lots of dynamic zeroclipboards on your page whereas I only had one, but it seems to me that there's no reason this won't work for you.
<!-- <script type="text/javascript" src="http://davidwalsh.name/demo/ZeroClipboard.js"></script> -->
function copyText(fieldName,buttonName){
var fieldNameTemp =fieldName;
var buttonNameTemp =buttonName;
var val = "";
try{
val = navigator.userAgent.toLowerCase();
}catch(e){}
var swfurl = "js/ZeroClipboard.swf";
setTimeout(function () {
ZeroClipboard.setMoviePath(swfurl);
var clip = new ZeroClipboard.Client();
clip.addEventListener('mousedown', function () {
clip.setText(document.getElementById(fieldNameTemp).value);
});
clip.addEventListener('complete', function (client, text) {
try{
if(val.indexOf("opera") > -1 || val.indexOf("msie") > -1 || val.indexOf("safari") > -1 || val.indexOf("chrome") > -1){
alert('Your text has been copied');
}
}catch(e){
alert('Please alert not use on fireFox');
}
});
clip.glue(buttonNameTemp);
}, 2000);
}

windows onload not firing from external script

Okay i know this has been asked a lot but it seems that none of the solutions actually work for me. Here's the situation. I have a webpage that i need to add to an existing site, this site uses a master page which i can not touch. This limits me to using javascripts window.onload because i do not have access to the body tag.
On my page i have linked my external .js file in the head beneath every other external file. Here is an example of what my .js file looks like.
var myobj = null;
(5 or so functions that work properly. Mainly just toggles that show and hide divs. none touch the onload)
function load(){
myobj = document.getElementById("my_element");
alert("test");
}
window.onload = load;
Tried this and the load function never fires as i never get the alert. I've tried commenting out the first line in the load function and only haven't the alert and still nothing. Looking around i also found another way to do it that i tried without success. Everything else is the same except i removed the window.load and added this.
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function () {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(load);
addLoadEvent(function () {
/* more code to run on page load */
alert("hello2");
});
In this function, neither of the alerts fire. I have also tried placing an alert outside of a function, that one does work.
The browsers i am using are IE 8.0.7600.16385 and Chrome 21.0.1180.60
Let me know if you need more information.
Oh and as a side note, i cannot use jquery or any other javascript library as we are trying to keep this as light as possible. This page also must work in IE8, that is the businesses only supported browser at the moment. It would be nice if it worked in Chrome as well.
EDIT
In case i'm going about this completely the wrong way, what i am trying to do is keep track of the last element i have. I essentially have a page with 2 columns, left side is a menu and right is content. The content is all placed in div's with only one category showing at a time. The way it is supposed to work is when clicking on the menu category it hides the previous content and shows the new content.
I have set my content class to display: none; and have the start_content ID set to display: block;. What the Javascript is supposed to do is initialize my global with the start_content object. When clicking in the menu it calls a function that does an obj.style.display = 'none' and then sets the new obj to display = 'block'. It then takes the new obj and places it in my global variable to be changed on the next menu click.
here's an example without any of the onload functions
var prevContent = document.getElementById("start_content");
function toggle(id) {
prevContent.style.display = "none";
var content = document.getElementById(id);
content.style.display = "block";
prevContent = content;
}
The problem with this is that prevContent is unidentified when it enters the toggle function. I had assumed this was because i am linking this .js file in the head and so the page hasn't loaded my start_content yet which is why i had changed it to declaring the global as a null and then setting up a window.onload to set the appropriate value after it is created.
Adding "Defer" to the script tag in the head ended up doing the trick.

Resize a dynamically generated iframe

I have a report generated by Oracle Apex (A UI tool operating against the Oracle database). I have customized it to have a hyperlink on each record, which when clicked opens a detail report in an iframe right under the current record. This, I am doing by using the Javascript insertRow method on the html table element (Condensed Javascript code below. Oracle APEX allows use of JS/Jquery)
var pTable= html_CascadeUpTill(t,'TABLE');
var myNewRow = pTable.insertRow(pTR.rowIndex+1);
var myNewCell = myNewRow.insertCell(0);
myNewCell.innerHTML = '<iframe src="detail report url" height="0"></iframe>';
In order to resize the height of the iFrame that is different for different detail records, I have the following code in the document).ready(function() of the page
$('iframe').load(function()
{
setTimeout(iResize, 1000);
}
function iResize()
{
// Iterate through all iframes in the page.
for (var i = 0, j = iFrames.length; i < j; i++)
{
var y=(iFrames[i].contentWindow || iFrames[i].contentDocument);
if (y.document)y=y.document;
var docHt = getDocHeight(y);
if (docHt) iFrames[i].height = docHt + "px";
}
}
);
Without the setTimeout call to iResize function, the iframe resize is not happening. But this setTimeout is adding a delay in the resized iframe to appear which I want to avoid. Is there a way to do this? All the related posts/articles I have seen online deal with iframes that are built into the page but not generated on-the-fly as in my case.
Let me know if you need more information. Please help. Thank you.
You should consider putting the details in a <div> block, then showing or hiding the <div> with JQuery. You can set dimensions for your block with CSS, or just let the content flow normally inside of the block. Sounds like a much simpler way to achieve the same effect.
The issue is that if you perform the resize too soon it will get the dimensions of the child document before it has been fully rendered, hence the use of a timer.
If your detail reports are other APEX pages that you control, then you could call the iResize function from the "Execute when page loads" section of the detail page:
parent.iResize();
That seems to work for me.
It sounds to me like the iframes don't even exist when the page first loads.
Instead of calling the iResize function on page load and then every second you could place the call to iResize in the code that creates the iframe.

Categories

Resources