Parse values from HTML element using Google App Script? - javascript

I am trying to parse HTML element by class on Google Sites, my code is:
function doGet(){
var html = UrlFetchApp.fetch ('http://indicadoresdeldia.cl/').getContentText();
var doc = XmlService.parse(html);
var html = doc.getRootElement();
var menu = getElementsByClassName(html, 'span3 utm')[0];
var output = XmlService.getRawFormat().format(menu);
return HtmlService.createHtmlOutput(output);
}
Ween i run the code appear the nexte error message ReferenceError: "getElementsByClassName" is not defined.
i am trying to deploy the example for the next page: https://sites.google.com/site/scriptsexamples/learn-by-example/parsing-html
Any ideas?
THanks in advance for your help.

According to that site, you should directly copy those functions to your project (source code available there) and then call them. That would alleviate each and every one of your problems.

Source: https://sites.google.com/site/scriptsexamples/learn-by-example/parsing-html
function getElementsByClassName(element, classToFind) {
var data = [];
var descendants = element.getDescendants();
descendants.push(element);
for(i in descendants) {
var elt = descendants[i].asElement();
if(elt != null) {
var classes = elt.getAttribute('class');
if(classes != null) {
classes = classes.getValue();
if(classes == classToFind) data.push(elt);
else {
classes = classes.split(' ');
for(j in classes) {
if(classes[j] == classToFind) {
data.push(elt);
break;
}
}
}
}
}
}
return data;
}

Related

serializing dynamically created html with indentation

Having created a bunch of elements in an html document with appendChild(), I am trying to to save the modified page on the client. Sending it off to the server seems a bit unnecessary, so I've opted for :
var save = document.createElement("a");
save.classList.add("button");
save.textContent = "save";
save.download = "layout-save.html"
save.onclick = function(event) {
var output = [];
// serialize document to output
var file = new window.Blob(output,{type:"text/html"});
save.href = window.URL.createObjectURL(file);
}
document.body.appendChild(save);
However, the newly created elements aren't indented of course. I've been looking at js-beautify but I also noticed that the mozilla page on parsing and serializing claims that you can use treewalker.
Would anyone know how I might go about doing such a thing? Or failing that, would there be a way to serialize a node without it's children in order to run a recursive loop like this :
var output = [];
var serializer = new XMLSerializer();
function indent(node) {
var ancestor = node;
while (ancestor != document.documentElement) {
output.push(" ");
ancestor = ancestor.parentNode;
}
output.push(/* serialize node tagname + attributes */);
output.push("\n");
for (let child of node.children) {
indent(child);
}
output.push(/* node closing tag*/);
}
indent(document.documentElement);
Don't hesitate tell me if I'm barking up the wrong tree, and thank you for your time.
By way of a reply to my own question, you can serialize a shallow clone to get the opening and closing tags of a node :
var save = document.createElement("a");
save.classList.add("button");
save.textContent = "save";
save.download = "layout.html"
save.onclick = function(event) {
document.body.removeChild(save);
var output = [];
var serializer = new XMLSerializer();
function indent(node) {
function offset(node) {
var count = 0;
var ancestor = node;
while (ancestor != document.documentElement) {
count++;
ancestor = ancestor.parentNode;
}
return "\t".repeat(count);
}
var buffer = offset(node);
var nodeClone = serializer.serializeToString(node.cloneNode(false)).replace(' xmlns="http://www.w3.org/1999/xhtml"',"");
if (node.children.length) {
let tagSplit = nodeClone.replace(/(<.+>)(<\/.+>)/,"$1<!--children-->$2").split("<!--children-->");
output.push(buffer + tagSplit[0] + "\n");
for (let child of node.children) {
indent(child);
}
output.push(buffer + tagSplit[1] + "\n");
} else {
output.push(buffer + nodeClone + "\n");
}
}
indent(document.documentElement);
var file = new window.Blob(output,{type:"text/html"});
save.href = window.URL.createObjectURL(file);
}
document.body.appendChild(save);
manually removing the xhtml namespace is a bit of a shame but since it's XMLSerializer I couldn't see any way around that.

Find MenuItem recursively and click it once it found

Hi I am planning to find menu item by traversing recursively all the sub menus .
I have written script but I could not able to debug this console script. I am new to Firebug. Can some one enlighten me on either how to debug this script or a fix to the written script is also welcome.Thanks in advance.
var QuickActionFinder = function(component, searchString)
{
console.log(component.id);
if(component.hasOwnProperty('text'))
{
if(component.text == searchString)
{
if(component.hasOwnProperty('textEl'))
{
component.textEl.dom.click();
return;
}
}
}
if(component.hasOwnProperty('menu'))
{
var count = component.menu.items.length;
for(var i=0;i < count;count++)
{
var comp = component.menu.items.itemAt(i);
QuickActionFinder(comp,searchString)
}
}
else
{
return;
}
}
var comp = window.frames[2].Ext.getCmp('ext-comp-2515'); QuickActionFinder(comp,'Mobile')

Print plugin phonegap

Hi i'm quite confused on some parts of the Print Plugin or the Phonegap plugin. See i was able to implement the code even created my own plugin but i was no returning values from objective-c (xcode) back to javascript so it was safe to say that it was easy to understand.
On this code:
https://github.com/phonegap/phonegap-plugins/blob/master/iPhone/PrintPlugin/PrintPlugin.js
On this block of code:
PrintPlugin.prototype.callbackMap = {};
PrintPlugin.prototype.callbackIdx = 0;
PrintPlugin.prototype.print = function(printHTML, success, fail, options) {
if (typeof printHTML != 'string'){
console.log("Print function requires an HTML string. Not an object");
return;
}
//var printHTML = "";
var dialogLeftPos = 0;
var dialogTopPos = 0;
if (options){
if (options.dialogOffset){
if (options.dialogOffset.left){
dialogLeftPos = options.dialogOffset.left;
if (isNaN(dialogLeftPos)){
dialogLeftPos = 0;
}
}
if (options.dialogOffset.top){
dialogTopPos = options.dialogOffset.top;
if (isNaN(dialogTopPos)){
dialogTopPos = 0;
}
}
}
}
var key = 'print' + this.callbackIdx++;
window.plugins.printPlugin.callbackMap[key] = {
success: function(result) {
delete window.plugins.printPlugin.callbackMap[key];
success(result);
},
fail: function(result) {
delete window.plugins.printPlugin.callbackMap[key];
fail(result);
},
};
var callbackPrefix = 'window.plugins.printPlugin.callbackMap.' + key;
return PhoneGap.exec("PrintPlugin.print", printHTML, callbackPrefix + '.success', callbackPrefix + '.fail', dialogLeftPos, dialogTopPos);
};
Especially this lines of code:
PrintPlugin.prototype.callbackMap = {};
PrintPlugin.prototype.callbackIdx = 0;
I'm confused by what that two lines of code does and why it is somehow important to incorporate or follow when you want to return values from xcode to javascript (NOTE: by me saying why it is somehow important to incorporate or follow when you want to return values from xcode to javascript i'm saying this based on what I've understood so far)
Can somebody explain how the two lines of code works and what are their purpose? Thank you.

How to Get XML file with Jquery and Display Elements in Random Order ONCE per load?

I have been trying to build a question and answer app with Ajax. I need help creating a particular function. I have created XML files with different questions and answers. The basic idea is to use the "get" function to (1) load an XML questions file and (2) use the "display" and "math.random" functions to display a random "question" element (the corresponding "answer" element will be shown at the same time, but hidden from view by Javascript.) This is the format of the XML files I am using. These nodes are enclosed by a parent node, Words..
<WordQuestions>
<Question>Question1</Question>
<Answer>Answer1</Answer>
</WordQuestions>
<WordQuestions>
<Question>Question2</Question>
<Answer>Answer2</Answer>
</WordQuestions>
I need to create a function that can choose a question & answer element at random from the XML file, show it to the user, but NOT show it again on subsequent clicks by the user. So, once a question is shown to the user, it needs to be removed from the list of questions to be shown to the user on the next click. Does anybody know how to do this?
I have created a similar function that works like a charm, but it is limited in that it is too random - a questions & answer element may never be selected to show to the user, or it could be selected a disproportionate number of times. The user needs to practice with all of the questions. Here is a stripped-down version of this function.
<script language = "javascript">
function getCategory()
{
var XMLHttpRequestObject = false;
if (window.XMLHttpRequest) {
XMLHttpRequestObject = new XMLHttpRequest();
XMLHttpRequestObject.overrideMimeType("text/xml");
} else if (window.ActiveXObject) {
XMLHttpRequestObject = new
ActiveXObject("Microsoft.XMLHTTP");
}
if(XMLHttpRequestObject) {
var P = document.LoadCategory.Load.value;
if (P == "Category1") {XMLHttpRequestObject.open("GET", "Catgory1.xml", true)}
if (P == "Category2") {XMLHttpRequestObject.open("GET", "Category2.xml", true)}
if (P == "Category3") {XMLHttpRequestObject.open("GET", "Category3.xml", true)}
XMLHttpRequestObject.onreadystatechange = function()
{
if (XMLHttpRequestObject.readyState == 4 &&
XMLHttpRequestObject.status == 200) {
var xmlDocument = XMLHttpRequestObject.responseXML;
displayCategory(xmlDocument);
}
}
XMLHttpRequestObject.send(null);
}
}
function displayCategory (xmldoc)
{
Questionnodes = xmldoc.getElementsByTagName("Question");
Answernodes = xmldoc.getElementsByTagName("Answer");
var i = Math.floor((Math.random()*1000)%Questionnodes.length);
var i = Math.floor((Math.random()*1000)%Answernodes.length);
var displayText1 =
Questionnodes[i].firstChild.nodeValue;
var target = document.getElementById("targetDiv1");
target.innerHTML=displayText1;
var displayText2 =
Answernodes[i].firstChild.nodeValue;
var target = document.getElementById("targetDiv2");
target.innerHTML=displayText2;
}
</script>
Right now, I do not know if I am able to alter this code to get the function I want. I have tried parsing an XML file into a javascript array (and then randomly select and remove an element) but have gotten nowhere atm. If anyone has a few suggestions, I would be most grateful. Once again, I want a function that can randomly select a question & answer element from an XML file, but only show it to the user ONCE.
Cheers guys. (sorry this was so long-winded).
write a class with a var hasbeenshown, hasbeenanswered, useranswer, function getquestion, function getanswer.
the instanciated classes, filled with values from your xml file on load you can add to an array and use your random number to choose a random question.
here is a link to an example of how I would do what you're trying to : http://jsfiddle.net/dievardump/xL5mg/4/
I commented some parts of the code and I 'simulate' your getCategory method.
Note : From my code, I think what didn't manage to do is the 'pickRandom' method. I think you have almost all you need to do other parts.
What I do in my code :
I have a collection of Question and Answers
I have a QuestionAndAnswer constructor
When the server result come, I 'parse' the xml file and fill the collection with QuestionAndAnswer objects.
In the HTML is a 'load a question' button. When you click on it, it call the displayQuestion method.
This method picks (get and remove from) a random QandA object from the collection, then display the question and a button to see the associated answer.
Like i said in the comments, i decided to add question the one after the other, but you can change that by having only one question and response handler and modify its content.
Here is the code if one day jsfiddle decide to not work anymore :
Javascript
(function() {
// Question and Answer collection
var oQandACollection = {
collection : [],
length : 0,
// get a QandA object
get : function(i) {
if (i < this.length) {
return this.collection[i];
}
return null;
},
// add a QandA object
add : function(qanda) {
this.collection.push(qanda);
this.length++;
},
// remove a QandA object
remove : function(i) {
if (typeof this.collection[i] !== 'undefined') {
this.collection.splice(i, 1);
this.length--;
}
},
// randomly pick an object from the collection
pickRandom : function() {
if (this.length === 0) return null; // return null if there is no object in the collection
var i = Math.floor(Math.random() * this.length);
var qanda = this.get(i);
this.remove(i);
return qanda;
}
};
// Question and Answer Object
var QandA = function(xmlNode) {
var question = xmlNode.getElementsByTagName('Question')[0] || null;
var answer = xmlNode.getElementsByTagName('Answer')[0] || null;
if (question && answer) {
this.question = question.textContent;
this.answer = answer.textContent;
} else {
return null;
}
};
// function to use as ajax request handler
function fillQandA(xmlDoc) {
// get all WordQuestions elements
var wrappers = xmlDoc.getElementsByTagName('WordQuestions');
for(var i = 0, l = wrappers.length, qanda = null; i < l; i++) {
// create a new question from the current wrapper
// we could have perfom the getElementsByTagName('Question') here
// but since the code is really specific to your example i putted it in the constructor of QandA
// You can change it
qanda = new QandA(wrappers[i]);
if (qanda) {
oQandACollection.add(qanda);
}
}
};
var eList = document.getElementById('qa-list'),
eLoadQuestion = document.getElementById('load-qanda');
// functions to display a question
// i choosed to add question the one after the others,
// so i re-create html elements at every display
// but you also can modify it to have just one question at a time
// matter of choice
function displayQuestion() {
var qanda = oQandACollection.pickRandom(); // get a question
if (qanda) { // if there is a question
// create elements
var eQuestion = document.createElement('p'),
eAnswer = document.createElement('p'),
eBtn = document.createElement('button');
eQuestion.textContent = qanda.question;
eAnswer.textContent = qanda.answer;
eQuestion.classNAme += ' question';
eAnswer.className += ' answer';
eBtn.textContent = 'Show Answer';
// add click event listener to the button to show the answer
eBtn.addEventListener('click', function() {
eAnswer.style.display = 'block';
eList.removeChild(eBtn);
}, false);
eList.appendChild(eQuestion);
eList.appendChild(eAnswer);
eList.appendChild(eBtn);
}
};
// add click event handler on display
eLoadQuestion.addEventListener('click', displayQuestion, false);
// simulate xhr request
function getCategory() {
// fill fake Q&A xml document
var xmlDoc = document.createElement('root');
for(var i = 0, wrapper = null, question = null, answer = null; i < 10; i++) {
wrapper = document.createElement('WordQuestions');
question = document.createElement('Question');
question.textContent = 'Question ' + (i+1);
answer = document.createElement('Answer');
answer.textContent = 'Answer ' + (i+1);
wrapper.appendChild(question);
wrapper.appendChild(answer);
xmlDoc.appendChild(wrapper);
}
// us as xhr request handler like : fillQandA(XMLHttpRequestObject.responseXML);
fillQandA(xmlDoc);
}
getCategory();
// test function
function test() {
for(var i = oQandACollection.length; i--; ) {
displayQuestion();
}
}
//test();
})();
HTML
<div class="wrapper">
<div id="qa-list">
</div>
<button id="load-qanda" value="Load new question">Load new question</button>
</div>
CSS
.wrapper {
width : 500px;
}
.wrapper > div {
position : relative;
width : 100%
}
.answer {
display : none;
}

How to parse a text like XML to a javascript object

I created a function to do this.
var text="adsf [name]Victor[/name] dummytext [name]Elliot[/name] asdf [name]Jake[/name] asdf [foo]bar[/foo]";
alert( readTags(text,'name') ); //Victor,Elliot,Jake
alert( readTags(text,'foo') ); //bar
but now I like to implement a function that receive a string like this
[person]
[name]jake[/name]
[age]12[/age]
[/person]
and return a object like this
var object={};
object['person']={};
object['name']='jake';
object['age']='12';
return(object);
but I don't know how to loop through the text. How to deal with starting and ending tags?
like
[tag] [tag]value[/tag] [/tag]
I thought to find starting tag from left and ending tag from the right using indexOf('[tag]') and lastindexOf('[/tag]')
but doesn't work in this situation
[tag]value[/tag] [tag]value[/tag]
this is the previous function
function readTags(str,property){
var beginTag='['+property+']';
var endTag='[/'+property+']';
var values=new Array(0);
while(str.indexOf(beginTag)!=-1){
values[values.length]=strBetween(str,beginTag,endTag);
str=str.substring(str.indexOf(endTag)+endTag.length);
}
return(values);
}
function strBetween(string,strBegin,strEnd){ //StrBetween("abcdef","b","e") //return "cd"
var posBegin, posEnd;
posBegin=string.indexOf(strBegin);
string=string.substring(posBegin + strBegin.length);
posEnd=string.indexOf(strEnd);
string=string.substring(0,posEnd);
if ((posBegin==-1)||(posEnd==-1)){
return(null);
}else{
return(string);
}
}
Unless you have a good reason not to use JSON, don't do this. JSON handles all of those problems very well and can be floated around from server to client and vice versa quite easily.
But since this seems fun, I'll try and see if I can whip up an answer.
Since your structure resembles XML, just replace the brackets with < and > and parse it like XML:
text = text.replace('[', '<').replace(']', '>');
if (typeof DOMParser != "undefined") {
var parser = new DOMParser();
var xml = parser.parseFromString(text, 'text/xml');
} else {
var xml = new ActiveXObject('Microsoft.XMLDOM');
xml.async = 'false';
xml.loadXML(text);
}
Now xml holds a DOMDocument that you can parse:
xml.getElementsByTagName('person').childnodes;
Try this possibly-working code (didn't test):
function createObject(element) {
var object = {};
if (element.childNodes.length > 0) {
for (child in element.childnodes) {
object[element.tagName] = createObject(child);
}
return object;
} else {
return element.nodeValue;
}
}
I thought this would be interesting to do without a third-party parser, so I built me a simple one:
function parse(code)
{
var obj = {},
cur = obj,
stack = [];
code.replace(/\[([^\]]+)\]|([^\[]*)/g, function (match, tagName, text) {
if (tagName)
{
if (tagName.charAt(0) == "/")
{
/* end tag */
cur = stack.pop();
}
else
{
/* start tag */
stack.push(cur);
cur = cur[tagName] = {};
}
}
else
{
cur["#text"] = text;
}
});
return obj;
}
var obj = parse(text);
JSON <=> XML http://code.google.com/p/x2js/

Categories