I have successfully created a ExtensionSidebarPane which displays some data I have in stored in local storage:
saving the data:
localStorage.setItem('savedDataForReview',JSON.stringify(savedDataObject));
creating and updating the panel:
chrome.devtools.panels.elements.createSidebarPane("Data Reviewing Panel",
function() {
chrome.devtools.inspectedWindow.eval(
'localStorage.getItem("savedDataForReview")',
function (result) {
pane.setObject(JSON.parse(result));
}
);
});
This works nicely. However I now want to improve the pane to have some tables and buttons, so I need to use HTML instead of just setting a JSON object. So I need to use
pane.setPage('html/my_reviewing_pane.html');
in place of the setObject line.
This works, however I can't seem to get access to local storage from within that pane HTML page.
If I include some javascript to access my saved data localStorage.getItem('savedDataForReview') then it returns null.
How can I pass data into the HTML page on an ExtensionSidebarPane? I imagine this is a scoping issue but I am not sure how I can get around it. I can't even easily build the HTML from within the function where the data is in scope, because setPage takes a local file path rather than an HTML string.
Hope someone can help.
In the end, I went with using a background script and messaging passing. Thank you #wOxxOm for the pointers.
I added a background script background.js, adding to the manifest.json:
...
"background": {
"scripts":["background.js"],
"persistent":false
},
...
In my background.js script I added the following code so that it can receive messages from anywhere in the extension (content scripts, panel JS or pane JS):
chrome.runtime.onMessage.addListener(function(rq, sender, sendResponse) {
if (rq.action=="getData") {
sendResponse(getData());
return true;
} else if (rq.action=="deleteData") {
sendResponse(deleteData());
} else if (rq.action=="storeData") {
sendResponse(storeData(rq.data));
} else {
console.log('received unrecognized request:');
console.dir(rq);
sendResponse(null);
}
return true;
});
function storeData(object) {
localStorage.setItem("savedDataForReview",JSON.stringify(object));
return true;
}
function getData() {
return JSON.parse(localStorage.getItem('savedDataForReview'));
}
function deleteData() {
localStorage.removeItem('savedDataForReview');
return true;
}
Then within my content script I send storage messages to the background script like this:
function getData(callback) {
chrome.extension.sendMessage({action: "getData"}, function(resp) {
callback(resp);
});
}
function storeData(callback, data) {
chrome.extension.sendMessage({action: "storeData", data: data}, function(resp) {
callback(resp);
})
}
function deleteData(callback) {
chrome.extension.sendMessage({action: "deleteData", data: data}, function(resp) {
callback(resp);
})
}
storeData(function(response) {
console.log(`response from storing data was: ${response}`);
},data);
And in my pane's HTML page my_reviewing_pane.html I include a javascript file:
<h2>Reviewing Pane</h2>
<div id="review_table">
</div>
<script src="my_reviewing_pane.js"></script>
In that javascript file my_reviewing_pane.js I access the stored data by sending a message to the background script like this:
function getData(callback) {
chrome.extension.sendMessage({action: "getData"}, function(resp) {
callback(resp);
});
}
getData(function(savedDataFromStorage) {
let tableDiv = document.getElementById("review_table");
console.dir(savedDataFromStorage);
// now set HTML content into tableDiv.innerHTML using data from savedDataFromStorage...
});
Finally I create and set the pane from within the javascript of my devtools page:
chrome.devtools.panels.elements.createSidebarPane("Reviewing Pane",
function (pane) {
pane.setPage('my_reviewing_pane.html');
}
);
I decided to use HTML5 Local Storage rather than Chrome storage because I didn't need syncing and didn't want the size limits - but I think Chrome storage looks more powerful for a lot of use cases. See this article.
Hope this is useful to someone else.
Related
I am using ajaxComplete to run some functions after dynamic content is loaded to the DOM. I have two separate functions inside ajaxComplete which uses getJSON.
Running any of the functions once works fine
Running any of them a second time causes a loop cause they are using getJSON.
How do I get around this?
I'm attaching a small part of the code. If the user has voted, clicking the comments button will cause the comments box to open and close immediately.
$(document).ajaxComplete(function() {
// Lets user votes on a match
$('.btn-vote').click(function() {
......
$.getJSON(path + 'includes/ajax/update_votes.php', { id: gameID, vote: btnID }, function(data) {
......
});
});
// Connects a match with a disqus thread
$('.btn-comment').click(function() {
var parent = $(this).parents('.main-table-drop'), comments = parent.next(".main-table-comment");
if (comments.is(':hidden')) {
comments.fadeIn();
} else {
comments.fadeOut();
}
});
});
Solved the problem by checking the DOM loading ajax request URL
$(document).ajaxComplete(event,xhr,settings) {
var url = settings.url, checkAjax = 'list_matches';
if (url.indexOf(checkAjax) >= 0) { ... }
}
The case:
I use dojo to request a page and load it into a div ( view ).
The problem:
The content that gets loaded into a form contains a dojo form and relevant objects, textbox, etc... how can I controller these widgets? I believe the current way I am working around the issue is sloppy and could be more refined.
Comments are in the code to help explain the issue I have. Please let me know your thoughts.
function (parser, domAttr, util, ready, dom, on, request, domStyle, registry, TextBox) {
//This prepares the doc main html page We are looking for a click of a menu option to load in thats pages pages content
ready(function () {
//Look for click of menu option
on(dom.byId('steps'), "a:click", function(e) {
event.preventDefault();
//Get the div we are going to load the page into
var view = domAttr.get(this, "data-view");
// function that loads the page contents
load_page(view);
});
});
function load_page(view) {
//First I see if this page already has widgets and destroy them
//We do this so users can toggle between menu items
// If we do not we get id already registered
var widgets = dojo.query("[widgetId]", dom.byId('apply-view')).map(dijit.byNode);
dojo.forEach(widgets, function(w){
w.destroyRecursive();
});
//get the html page we are going to user for the menu item
request.post("/apply_steps/"+view, {
data: {
id: 2
}
}).then(
function(response){
//Add the content and parse the page
var parentNode = dom.byId('apply-view');
parentNode.innerHTML = response;
parser.parse(parentNode);
//This is where it is sloppy
//What I would prefer is to load a new js file the controlls the content that was just loaded
//What happens now is I create a traffic director to tell the code what main function to use
controller_director(view);
},
function(error){
util.myAlert(0, 'Page not found', 'system-alert');
});
}
function controller_director(view) {
//based on the view switch the function
switch(view) {
case 'screening_questions':
screening_questions();
break;
}
}
function screening_questions() {
//Now we are controlling the page and its widgets
// How would I get this info into a seperate js file that i would load along with the ajax call??
ready(function () {
on(dom.byId('loginForm'), "submit", function(e) {
event.preventDefault();
var formLogin = registry.byId('loginForm');
authenticate();
});
});
this.authenticate = function() {
var formLogin = registry.byId('loginForm');
if (formLogin.validate()) return;
}
}
});
in the jquery.get() function, the first parameter is URL, is that the url to the content I want to retrieve or to the Controller/action method.
The problem is I'm building an asp.net-mvc application and I'm not sure what to pass as this parameter. Right now I'm passing my partialview.cshtml but nothing is being returned, not sure if I'm doing this right or wrong.
Here's what I got
<div id="editor_box"></div>
<button id="add_button">add</button>
<script>
var inputHtml = null;
var appendInput = function () {
$("#add_button").click(function() {
if (!inputHtml) {
$.get('AddItem.cshtml', function (data) {
inputHtml = data;
$('#editor_box').append(inputHtml);
});
} else {
$('#editor_box').append(inputHtml);
}
})
};
</script>
also, what is the second parameter "function (data)" is this the action method?
You need to remove var appendInput = function () { from the script. You are defining a function but never calling it. Just use the following (update you action and controller) names
<script>
var inputHtml = null;
$("#add_button").click(function() {
if (!inputHtml) {
$.get('#Url.Action("SomeAction", "SomeController")'', function (data) {
inputHtml = data;
$('#editor_box').append(inputHtml);
});
} else {
$('#editor_box').append(inputHtml);
}
});
</script>
Edit
Based on your script you appear to be requiring the content only once (you then cache it and add it again on subsequent clicks. Another alternative would be to render the contents initially inside a hidden <div> element, then in the script clone the contents of the <div> and append it to the DOM
<div id="add style="display:none">#Html.Partial("AddItem")</div>
$("#add_button").click(function() {
$('#editor_box').append($('add').clone());
});
The first argument to $.get is the URL which will respond with the expected data. jQuery/JavaScript don't care what kind of server side architecture you have or the scripting language. Whether the URL looks like a file AddItem.cshtml or a friendly route like /User/Sandeep, it doesn't matter as far as the client side is concerned.
In the case of ASP.NET, your URL endpoint can be generated like so:
$.get('#Url.Action("SomeAction", "SomeController")', function (data) {
I have an activity stream for both users use, and site-wide view. Currently when a user posts an update, I have it displaying a default bootstrap success alert. I have seen other websites append the new post to the list by sliding down the existing items, and appending the newest post to the top of the list.
I am attempting to do just that, but I am not sure how to add it with all the proper styling. (code below). I am tried adding all the <div> tags that make up one activity item in my feed, but without success.
TL;DR - Is there a way to have ajax look at the current top activity item, clone it, and append it to the top? It would make the code more dynamic for my use, and avoid having to place CSS inside the .js file.
jQuery(document).ready(function ($) {
$('form#postActivity').submit(function (event) {
event.preventDefault();
$postActivityNow = (this);
var subject = $('#activity_subject').val();
var message = $('#activity_content').val();
var data = {
'action': 'postAnActivity',
'subject': subject,
'message': message,
}
$.ajax({
type: 'post',
url: postAnActivityAjax.ajaxurl,
data: data,
error: function (response, status) {
alert(response);
},
success: function (response) {
if (response.success) {
bootstrap_alert.success('Activity Added');
} else {
if (response.data.loggedIn == false) {
bootstrap_alert.warning('you are NOT logged in');
console.log('you are not logged in')
}
if (response.data.userExists == false) {
console.log(response);
bootstrap_alert.warning(response.data.alertMsg);
console.log(response.data.alertMsg)
}
}
}
});
});
});
you can also use .prependTo()
var newActivity = $( ".activity" ).first().clone();
newActivity.prependTo( ".parentDiv").hide().slideDown();
FIDDLE
To clone an element: jQuery.clone()
var newItem = $("#myDiv").clone();
To append it as first child: jQuery.prepend()
$("#parentDiv").prepend( newItem );
Regards,
hotzu
I have already done in the past using $.prepend()
Check this url for more information jquery append to front/top of list
i have some links in a web page ,what i want to do :
Trigger click event on every link
When the page of every link is loaded , do something with page's DOM(fillProducts here)
What i have tried :
function start(){
$('.category a').each(function(i){
$.when($(this).trigger('click')).done(function() {
fillProducts() ;
});
})
}
Thanks
What you want to do is much more complicated than you seem to be giving it credit for. If you could scrape webpages, including AJAX content, in 7 lines of js in the console of a web browser you'd put Google out of business.
I'm guessing at what you want a bit, but I think you want to look at using a headless browser, e.g. PhantomJs. You'll then be able to scrape the target pages and write the results to a JSON file (other formats exist) and use that to fillProducts - whatever that does.
Also, are you stealing data from someone else's website? Cause that isn't cool.
Here's a solution that may work for you if they are sending their ajax requests using jQuery. If they aren't you're going to need to get devilishly hacky to accomplish what you're asking (eg overriding the XMLHttpRequest object and creating a global observer queue for ajax requests). As you haven't specified how they're sending the ajax request I hope this approach works for you.
$.ajaxSetup({
complete: function(jQXHR) {
if(interested)
//do your work
}
});
The code below will click a link, wait for the ajax request to be sent and be completed, run you fillProducts function and then click the next link. Adapting it to run all the clicks wouldn't be difficult
function start(){
var links = $('.category a');
var i = 0;
var done = function() {
$.ajaxSetup({
complete: $.noop//remove your handler
});
}
var clickNext = function() {
$(links.get(i++)).click();//click current link then increment i
}
$.ajaxSetup({
complete: function(jQXHR) {
if(i < links.length) {
fillProducts();
clickNext();
} else {
done();
}
}
});
clickNext();
}
If this doesn't work for you try hooking into the other jqXHR events before hacking up the site too much.
Edit here's a more reliable method in case they override the complete setting
(function() {
var $ajax = $.ajax;
var $observer = $({});
//observer pattern from addyosmani.com/resources/essentialjsdesignpatterns/book/#observerpatternjquery
var obs = window.ajaxObserver = {
subscribe: function() {
$observer.on.apply($observer, arguments);
},
unsubscribe: function() {
$observer.off.apply($observer, arguments);
},
once: function() {
$observer.one.apply($observer, arguments);
},
publish: function() {
$observer.trigger.apply($observer, arguments);
}
};
$.ajax = function() {
var $promise = $ajax.apply(null, arguments);
obs.publish("start", $promise);
return $promise;
};
})();
Now you can hook into $.ajax calls via
ajaxObserver.on("start", function($xhr) {//whenever a $.ajax call is started
$xhr.done(function(data) {
//do stuff
})
});
So you can adapt the other snippet like
function start(){
var links = $('.category a');
var i = 0;
var clickNextLink = function() {
ajaxObserver.one("start", function($xhr) {
$xhr.done(function(data) {
if(i < links.length) {
fillProducts();
clickNextLink();
} else {
done();
}
});
})
$(links.get(i++)).click();//click current link then increment i
}
clickNextLink();
}
try this:
function start(){
$('.category a').each(function(i){
$(this).click();
fillProducts() ;
})
}
I get ya now. This is like say:
when facebook loads, I want to remove the adverts by targeting specific class, and then alter the view that i actually see.
https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/
Is a plugin for firefox, this will allow you to create a javascript file, will then allow you to target a specific element or elements within the html rendered content.
IN order to catch the ajax request traffic, you just need to catcher that within your console.
I can not give you a tutorial on greasemonkey, but you can get the greasemonkey script for facebook, and use that as a guide.
http://mashable.com/2008/12/25/facebook-greasemonkey-scripts/
hope this is it