I recently find a way to manage firefox tab in emacs. This sounds a little crazy. I use tree style tabs(firefox addon), Moz Repl, emacs, org-mode to do it.
For 10-15 tabs, my plan works fine. But 20+ tabs, My firefox hangs randomly. Maybe javascript stack overflow or something else? I don't know what's wrong with my code. I post the most import code here. Somesone help me to find some bugs?
It's a basic firefox chrome code below, you can run it in firefox without emacs and MozPepl.
I use tree style tabs api to get tabs and set each tab a cetain level. The output will be used in emacs with org-mode.
tree style tabs api: http://piro.sakura.ne.jp/xul/_treestyletab.html.en#api
The Code can run in many ways. I recommend "workspace addon". Copy My code, choose chrome context to run it.
https://addons.mozilla.org/en-US/firefox/addon/workspace/
// two helper function to get title and url of tab
function getTitle(tab)
{
var brower = gBrowser.getBrowserForTab(tab)
var url = brower.currentURI.spec
var title = brower.contentTitle
return title
}
function getUrl(tab)
{
var brower = gBrowser.getBrowserForTab(tab)
var url = brower.currentURI.spec
var title = brower.contentTitle
return ":PROPERTIES:\n:URL:"+url+"\n:END:\n"
}
var L = gBrowser.tabContainer.childNodes.length //firefox tabs length
var str = "" //global string for output
//parse tabs. If tab has child, parse it. It tab has no child, just output.
for(i = 0; i < L; i++){
level = "*"
tab = gBrowser.tabContainer.childNodes[i]
if ('TreeStyleTabService' in window){
if(TreeStyleTabService.hasChildTabs(tab))
{
str = [str, level, " [+] ", getTitle(tab), "\n", getUrl(tab)].join("") //output title and url. level used in org-mode
treeparse(TreeStyleTabService.getChildTabs(tab), "**") //if a tab has child tabs. parse it and level up
}
str = [str, level, " ", getTitle(tab), "\n", getUrl(tab)].join("")
}
function treeparse(tablist,level) //parse a list of tabs. If tab has not a child, output. If it has childs, parse again
{
for(i=0 ; i < tablist.length;i++) {
tab = tablist[i]
if ('TreeStyleTabService' in window){
if(TreeStyleTabService.hasChildTabs(tab))
{
str = [str, level, " [+] ", getTitle(tab), "\n", getUrl(tab)].join("")
newlevel = level + "*"
treeparse(TreeStyleTabService.getChildTabs(tab),newlevel)
}
} }
str = [str, level, " ", getTitle(tab), "\n", getUrl(tab)].join("")
}
}
alert(str) //alert to view result. You can also write the result into a file.
I'm not sure what's specifically causing the problem, as I couldn't reproduce it, but I see loads of issues with this code. I can't remember how MozRepl works, but this improved code should give you a nice org-mode friendly tab output. I hope this helps you, or whoever stumbles across this thread.
var bullet = "*"; // Org-mode bullet
// two helper function to get title and url of tab
function getTitle(tab) {
var brower = gBrowser.getBrowserForTab(tab);
var url = brower.currentURI.spec;
var title = brower.contentTitle;
return title;
}
function getUrl(tab) {
var brower = gBrowser.getBrowserForTab(tab);
var url = brower.currentURI.spec;
var title = brower.contentTitle;
return ":PROPERTIES:\n:URL:"+url+"\n:END:\n";
}
// NOTE: we factor these string-generation functions out,
// to make things a bit more clear
function makeParentNodeOutput(tab, level) {
return (Array(level+1).join(bullet) +
" [+] " +
getTitle(tab) +
"\n" +
getUrl(tab));
}
function makeLeafNodeOutput(tab, level) {
return (Array(level+1).join(bullet) +
" " +
getTitle(tab) +
"\n" +
getUrl(tab));
}
// NOTE: we only need to handle parsing a collection of tabs
// in once place, and we have a function for it here.
function parseTabCollection(tabs, level) {
var currentTab;
var outputString = "";
for(var i = 0; i < tabs.length; i++){
currentTab = tabs[i];
// For a parent node, we output the node and its children
if(TreeStyleTabService.hasChildTabs(currentTab)){
outputString += makeParentNodeOutput(currentTab, level);
outputString += parseTabCollection(
TreeStyleTabService.getChildTabs(currentTab),
level + 1
);
} else {
outputString += makeLeafNodeOutput(currentTab, level);
}
}
return outputString;
}
if ('TreeStyleTabService' in window){
//NOTE: Start with the rootTabs only. The old version started with
// *all* tabs, which isn't what we want
var orgModeOutput = parseTabCollection(TreeStyleTabService.rootTabs, 1);
alert(orgModeOutput);
}
I hope this helps somehow.
Related
I've created a number of JS scripts similar to below which generate a .tsv download of the webscraped data (this particular example assumes you're on the URL of a repo's Contributors page on Gitlab). Everything outputs fine when I open the .tsv in Microsoft Excel, except that the string 'undefined' appears prepended to every value after the header row in the first column only
How do I edit the script to omit undefined from appearing? Even if it's a simple fix, it will allow me to clean up a bunch of scripts' similar output scraping other websites.
javascript:(function(){
var arr = new Array, i, commitsemail, commitsnum, email, fullname, matchnum;
var regexnum = /.+?(?=commits)/g; var regexemail = /(?<=\().*(?=\))/g;
var glab = document.querySelectorAll('div.col-lg-6.col-12.gl-my-5');
var strings='Full name'+'\t'+'Email'+'\t'+'# of commits'+'\r\n';
var endurl = document.URL.split(/[/]+/).pop(); if (endurl != 'master') {
alert('You are not on the contributors page of a Gitlab repo. Press Esc key, go to URL ending with /master and re-run this bookmarklet'); } else {
for (i = 0; i<glab.length; i++) {
fullname = glab[i].getElementsByTagName('h4')[0].textContent;
commitsemail = glab[i].getElementsByTagName('p')[0].textContent;
commitsnum = [...commitsemail.match(regexnum)];
email = [...commitsemail.match(regexemail)];
arr[i] += fullname + '\t' + email + '\t' + commitsnum;
strings += arr[i]+'\r\n'; }
var pom = document.createElement('a');
var csvContent = strings; var blob = new Blob([csvContent],{type: 'text/tsv;charset=utf-8;'});
var url = URL.createObjectURL(blob); pom.href = url; pom.setAttribute('download','gitlab-contributors.tsv'); pom.click(); }
})();
It's because of the += on the line with arr[i] += fullname + '\t' + email + '\t' + commitsnum;. Change that to an = instead.
Before the assignment, arr[i] is undefined. Maybe you mixed up the syntax for assigning an array entry by index, with appending to an array (arr.push(...)), thinking += would push, but it doesn't. It appends the new value to the current value. And since that line is the first time arr[i] is assigned anything, the current value is undefined.
I am relatively new to JavaScript but I am creating a simple hangman web app in local host. The game runs perfectly, just as I want it. However, I realized that after loading the game at the same time in different tabs, the words change in all instances to the same word. For example, I can run the game in one tab, then another. Then when I go back to the first tab, the word I am trying to guess has changed to the one in the second tab. I don't know how else to describe my issue. Here's my JS and backend code:
JS
function runHangmanInit(){
$.get("/hangmanInit", {}, function(response){ //generates word
term.write("RUNNING HANGMAN.cpp\r\n");
guessedLetters = [];
let json = JSON.parse(response);
console.log(json);
term.write(json["state"] + "\r\n");
term.write("Category: " + json["category"] + "\t");
for(let i = 0; i < json["dashes"]; i++){
term.write(" _");
}
term.write("\r\nGuesses remiaining: "+ json["guesses"] + "\r\n");
term.write(json["message"]);
});
}
function checkLetterTerminal(letter){
$.get("/hangman/" + letter, {}, function(response){
let x = JSON.parse(response);
//console.log(x);
if(x["err"]){
term.write(x["err"]);
}
else{
term.write(x["state"] + "\r\n");
term.write("Category: " + x["category"] + "\t");
for(let i = 0; i < x.dashes.length; i++){
term.write(x.dashes[i] + " ");
}
term.write("\r\nGuesses remiaining: "+ x["guesses"] + "\r\n");
if(x["lettersGuessed"]){ //Incorrect guess
guessedLetters.push(x["lettersGuessed"]);
}
term.write("Letters Guessed: ");
for(let i = 0; i < guessedLetters.length; i++){
term.write(guessedLetters[i] + " ");
}
term.write("\r\n");
}
if(x["message"]){
term.write(x["message"] + "\r\n");
check1 = 0;
term.write("\r\n\x1B[1;3;31mdyaranon#DEKTOP-123:\x1B[0m/mnt/e$ ");
}
else{
term.write("\r\nEnter your guess: ");
}
});
}
Backend (Crow C++)
int main(int argc, char* argv[]){
crow::SimpleApp app;
Game game = Game(1);
CROW_ROUTE(app, "/hangmanInit")([](const request& req, response& res){
nlohmann::json x;
game.init();
x["word"] = game.getWord();
x["state"] = game.board.hangman;
x["category"] = game.categoryName;
x["dashes"] = game.board.dashes.size();
x["guesses"] = 6;
x["message"] = "Enter your guess: ";
res.sendJSON(x);
});
CROW_ROUTE(app, "/hangman/<string>")([](const request &req, response &res, string letter){
nlohmann::json x;
x = game.playGame(letter);
res.sendJSON(x);
});
I included all the relative parts of the code. Any help would be appreciated.
It looks like you're only creating one game instance? So yes, there's only once instance server-side and you're sharing it among all clients.
HTTP is stateless, so you first need a way to identify which client is which when they make requests. This is normally done by a session ID of some sort, which should be randomly generated.
It's common to store this session ID in cookies, but you'll actually want to just use a local variable in your JavaScript when the page loads, since you want to treat each tab differently.
From there, when you make your HTTP requests (which you're doing with $.get()), you need to include this session ID. I suggest putting it in the URL, something like:
/hangman/<some-game-id-here>/<some-route>
Finally, on the server side, you need to track multiple game instances... one for each game going on, and associate them with the session IDs.
I want to be able to link any word of my choice to a specific URL for example:
I want the word "goat" to link to "http://goat.com" across the entire website. So all "goat"/s will link to that URL right across the website.
I am using wordpress and I have not yet found a plugin to do this. If I can get a solution to this I would most likely create a plugin for this functionality.
I know how to target one word on a single page. But I would like it to be across all the pages and all the words in those pages( I used JavaScript for this).
Something like this may work for you.
function replaceWithUri(textToReplace, element){
element.innerHTML = element.innerHTML.replace(textToReplace, '<a href="http://www.' + textToReplace + '.com" >' + textToReplace + '</a>');
}
replaceWithUri('goat', document.getElementsByTagName('body')[0]);
Here's a crappy solution but it's better than nothing:
I found some code here which searches for a world across the whole page so I copy pasted that and modified it.
The replaceWord variable cannot contain the same string as word, otherwise it'll loop infinitely.
var word = " goat",
replaceWord = " <a href = 'http://goat.com'>goat</a>",
queue = [document.body],
curr
;
while (curr = queue.pop()) {
if (!curr.textContent.match(word)) continue;
for (var i = 0; i < curr.childNodes.length; ++i) {
switch (curr.childNodes[i].nodeType) {
case Node.TEXT_NODE : // 3
if (curr.childNodes[i].textContent.match(word)) {
curr.innerHTML = curr.innerHTML.replace(word,replaceWord);
}
break;
case Node.ELEMENT_NODE : // 1
queue.push(curr.childNodes[i]);
break;
}
}
}
Hello goat
<div>Look a goat</div>
This might be a bit resource intensive and replaceWord cannot contain the same string as word, otherwise it'll loop forever.
document.onload = function() {
var word = " goat",
replaceWord = " <a href = 'http://goat.com'>goat</a>";
while(document.body.innerHTML.indexOf(word) !== -1) {
document.body.innerHTML = document.body.innerHTML.replace(word,replaceWord);
}
}
Hello goat
<div>Look a goat</div>
I have the code below working well. It searches my xml file for all the "outcome_text" nodes of a particular branch (the 'str' branch), then writes the text to a table. Problem is, I would really like to get the value of the parent nodes attribute as well (the overall id value). That way my table row would be two columns: OVerall ID and Outcome.
I can do it no problem in VB/asp xpath, but trying to make a client side version for mobile.
here is a snippet of the xml:
<curriculum>
<strand id="Dance">
<strand_text>Dance</strand_text>
<overalls>
<overall id="A1">
<overall_text>Creating and Presenting: apply the creative process (see pages 19-22) to the composition of simple dance phrases, using the elements of dance to communicate feelings and ideas</overall_text>
<specifics>
<specific></specific>
</specifics>
</overall>
<overall id="A2">
<overall_text>Reflecting, Responding, and Analysing: apply the critical analysis process (see pages 23-28) to communicate their feelings, ideas, and understandings in response to a variety of dance pieces and experiences</overall_text>
<specifics>
<specific></specific>
</specifics>
</overall>
</overalls>
</strand>
<strand id="visual"> . . . </strand>
</curriculum>
and here is the code: (ignore the IE bits--it will be on Android and IOS only)
<script>
function popout() {
var gr = gradedd.options[gradedd.selectedIndex].value
var sb = subdd.options[subdd.selectedIndex].value
var str = stranddd.options[stranddd.selectedIndex].value
xml = loadXMLDoc("resources/ont/grade_" + gr + "_" + sb + ".xml");
path = "/curriculum/strand[#id='" + str + "']/overalls/overall/overall_text"
// code for IE
if (window.ActiveXObject) {
var nodes = xml.selectNodes(path);
for (i = 0; i < nodes.length; i++) {
// ddlist[0] = nodes[i].childNodes[0].nodeValue; ignore
// dropdown[dropdown.length] = new Option(ddlist[i], ddlist[i]); ignore
}
}
// code for Mozilla, Firefox, Opera, etc.
else if (document.implementation && document.implementation.createDocument) {
var nodes = xml.evaluate(path, xml, null, XPathResult.ANY_TYPE, null);
var result = nodes.iterateNext();
var txt = "<table border='1'><tr><th>Strand</th><th>OVERALL</th></tr>";
while (result) {
ind = str; // but would like the value of outcome node attribute
var over = result.childNodes[0].nodeValue
txt = txt + "<tr><td>" + ind + "</td><td>" + over + "</td></tr>"
result = nodes.iterateNext();
}
}
txt = txt + "</table>"
document.getElementById('outtable').innerHTML = txt;
}
Seems to me something like:
ind = result.parentNode.getAttribute("id")
should work.
I was interested in writing a twitter bot to help out some friends at a local ski resort. I found this tutorial from Amit Agarwal which gave me enough to get started (it did take me more than 5 minutes since I did a lot of modifying). I host the script on google docs.
FIRST I think this is javascript (my understanding is that google apps script uses javascript...) and when I have had problems with the code so far, google searches for javascript-such-and-such have been helpful, but if this is not actually javascript, please let me know so I can update the tag accordingly!
I have no prior experience with javascript, so I am pretty happy that it's actually working. But I want to see if I'm doing this right.
The start function initiates the trigger, which kicks off the fetchTweets() function every interval (30 minutes). In order to avoid duplicates (the first errors I encountered) & potentially being flagged as spam, I needed a way to ensure that I was not posting the same tweets over and over again. Within the start() function, the initial since_id value is assigned:
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
Within the fetchTweet() function, I think I am updating this property with the statement:
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID + '\n');
Is this a good way to do this? Or is there a better/more reliable way? And if so, how can I be sure it's updating the property? (I can check the log file and it seems to be doing it, so I probably just need to create a permanent text file for the logger).
Any help is greatly appreciated!!
/** A S I M P L E T W I T T E R B O T **/
/** ======================================= **/
/** Written by Amit Agarwal #labnol on 03/08/2013 **/
/** Modified by David Zemens #agnarchy on 11/21/2013 **/
/** Tutorial link: http://www.labnol.org/?p=27902 **/
/** Live demo at http://twitter.com/DearAssistant **/
/** Last updated on 09/07/2013 - Twitter API Fix **/
function start() {
Logger.log("start!" + '\n')
// REPLACE THESE DUMMY VALUES
// https://script.google.com/macros/d/18DGYaa-jbaAK9rEv0HZ2cMcWjFGgkvVcvr6TfksMNbbu2Brk3gZeZ46R/edit
var TWITTER_CONSUMER_KEY = "___REDACTED___";
var TWITTER_CONSUMER_SECRET = "___REDACTED___";
var TWITTER_HANDLE = "___REDACTED___";
var SEARCH_QUERY = "___REDACTED___" + TWITTER_HANDLE;
// Store variables
ScriptProperties.setProperty("TWITTER_CONSUMER_KEY", TWITTER_CONSUMER_KEY);
ScriptProperties.setProperty("TWITTER_CONSUMER_SECRET", TWITTER_CONSUMER_SECRET);
ScriptProperties.setProperty("TWITTER_HANDLE", TWITTER_HANDLE);
ScriptProperties.setProperty("SEARCH_QUERY", SEARCH_QUERY);
ScriptProperties.setProperty("SINCE_TWITTER_ID", "404251049889759234");
// Delete exiting triggers, if any
var triggers = ScriptApp.getScriptTriggers();
for(var i=0; i < triggers.length; i++) {
ScriptApp.deleteTrigger(triggers[i]);
}
// Setup trigger to read Tweets every 2 hours
ScriptApp.newTrigger("fetchTweets")
.timeBased()
.everyMinutes(30)
//.everyHours(2)
.create();
}
function oAuth() {
//Authentication
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(ScriptProperties.getProperty("TWITTER_CONSUMER_KEY"));
oauthConfig.setConsumerSecret(ScriptProperties.getProperty("TWITTER_CONSUMER_SECRET"));
}
function fetchTweets() {
oAuth();
// I put this line in to monitor whether the property is getting "stored" so as to avoid
// reading in duplicate tweets.
Logger.log("Getting tweets since " + ScriptProperties.getProperty("SINCE_TWITTER_ID"))
var twitter_handle = ScriptProperties.getProperty("TWITTER_HANDLE");
var search_query = ScriptProperties.getProperty("SEARCH_QUERY")
Logger.log("searching tweets to " + search_query + '\n');
// form the base URL
// restrict to a certain radius ---:
//var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&geocode=42.827934,-83.564306,75mi&include_entities=false&result_type=recent&q=";
// unrestricted radius:
var search = "https://api.twitter.com/1.1/search/tweets.json?count=5&include_entities=false&result_type=recent&q=";
search = search + encodeString(search_query) + "&since_id=" + ScriptProperties.getProperty("SINCE_TWITTER_ID");
var options =
{
"method": "get",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
try {
var result = UrlFetchApp.fetch(search, options);
var lastID = ScriptProperties.getProperty("SINCE_TWITTER_ID");
if (result.getResponseCode() === 200) {
var data = Utilities.jsonParse(result.getContentText());
if (data) {
var tweets = data.statuses;
//Logger.log(data.statuses);
for (var i=tweets.length-1; i>=0; i--) {
// Make sure this is a NEW tweet
if (tweets[i].id > ScriptProperties.getProperty("SINCE_TWITTER_ID")) {
lastID = (tweets[i].id_str);
var answer = tweets[i].text.replace(new RegExp("\#" + twitter_handle, "ig"), "").replace(twitter_handle, "");
// I find this TRY block may be necessary since a failure to send one of the tweets
// may abort the rest of the loop.
try {
Logger.log("found >> " + tweets[i].text)
Logger.log("converted >> " + answer + '\n');
sendTweet(tweets[i].user.screen_name, tweets[i].id_str, answer.substring(0,140));
// Update the script property to avoid duplicates.
ScriptProperties.setProperty("SINCE_TWITTER_ID", lastID);
Logger.log("sent to #" + tweets[i].user.screen_name + '\n');
} catch (e) {
Logger.log(e.toString() + '\n');
}
}
}
}
}
} catch (e) {
Logger.log(e.toString() + '\n');
}
Logger.log("Last used tweet.id: " + lastID + + "\n")
}
function sendTweet(user, reply_id, tweet) {
var options =
{
"method": "POST",
"oAuthServiceName":"twitter",
"oAuthUseToken":"always"
};
var status = "https://api.twitter.com/1.1/statuses/update.json";
status = status + "?status=" + encodeString("RT #" + user + " " + tweet + " - Thanks\!");
status = status + "&in_reply_to_status_id=" + reply_id;
try {
var result = UrlFetchApp.fetch(status, options);
Logger.log("JSON result = " + result.getContentText() + '\n');
}
catch (e) {
Logger.log(e.toString() + '\n');
}
}
// Thank you +Martin Hawksey - you are awesome
function encodeString (q) {
// Update: 09/06/2013
// Google Apps Script is having issues storing oAuth tokens with the Twitter API 1.1 due to some encoding issues.
// Henc this workaround to remove all the problematic characters from the status message.
var str = q.replace(/\(/g,'{').replace(/\)/g,'}').replace(/\[/g,'{').replace(/\]/g,'}').replace(/\!/g, '|').replace(/\*/g, 'x').replace(/\'/g, '');
return encodeURIComponent(str);
// var str = encodeURIComponent(q);
// str = str.replace(/!/g,'%21');
// str = str.replace(/\*/g,'%2A');
// str = str.replace(/\(/g,'%28');
// str = str.replace(/\)/g,'%29');
// str = str.replace(/'/g,'%27');
// return str;
}
When you use ScriptProperties.setProperty("KEY", "VALUE");, internally Script Properties will overwrite a duplicate key (i.e., if an old Property has the same key, your new one will replace it). So in your case, since you are using the same identifier for the key (SINCE_TWITTER_ID), it will replace any previous Script Property that is that key.
Furthermore, you can view Script Properties via File -> Project properties -> Project properties (tab). Imo Google didn't name that very well. User properties as specific to Google users. Script properties as specific to the Script Project you are working under.
Also, it probably isn't a good idea to include \n in your value when you set the property. That will lead to all sorts of bugs down the road, because you'll have to compare with something like the following:
var valToCompare = "My value\n";
instead of:
var valToCompare = "My value";
because the value in SINCE_TWITTER_ID will actually be "some value\n" after you call your fetchTweet() function.
Of course, one seems more logical I think, unless you really need the line breaks (in which case you should be using them somewhere else, for this application).
Its ok like that thou I dont know why you are adding \n at fhe end. Might confuse other code. You can see script properties in the script's file menu+ properties