add collapsible into another collapsibles (nested collapsibles) in JQM - jquery-mobile

I want to get this:
But I need to create the HTML dynamically because it's loaded via AJAX from an external ressource.
I already asked this question here (JQM: Dynamic nested collapsibles - $(collapsible).collapsible() & $(collapsible).trigger('create')) but I got it completly wrong with lists and collapsibles, so I figured a new question would be better.
Here is what I got so far:
function loadTipps() {
console.log("Lade Tipps..");
var categoriesURL = tippsURL+"?type=kategorien&callback=?"; // url for the categories
$.getJSON(categoriesURL,function(data) {
console.log("Lade Kategorien..");
var DIV_tipps_komplett = $("#tipps_komplett");
$.each(data, function(key,value){
var kategorie_ID = value.id;
var kategorie_NAME = value.name;
var collapsible_HTML = $('<div data-role="collapsible"></div>');
var kategorie_Ueberschrift_HTML = $('<h3>'+kategorie_NAME+'</h3>');
var tipps_kategorie_HTML = $('<div id="tipps_kategorie'+kategorie_ID+'" data-role="collapsible-set"></div>');
var tippURL = tippsURL+"?type=tipp&kat_id="+value.id+"&callback=?"; // url for the tipps of the current category
$.getJSON(tippURL,function(data2) {
$.each(data2, function(key2,value2){
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><b>'+value2.name+'</b><p>'+value2.text+'</p><br></div>';
tipps_kategorie_HTML.append(tipp_Ueberschrift_Text_HTML);
}); //<--each
});//<--getJSON
collapsible_HTML.append(kategorie_Ueberschrift_HTML);
collapsible_HTML.append(tipps_kategorie_HTML);
DIV_tipps_komplett.append(collapsible_HTML);
});//<--each
DIV_tipps_komplett.trigger('create');
});//<--getJSON
}
This results in:
As you can see, the items in the first collapsible set are not in another collapsible set. Any ideas why?

Try changing the line:
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><b>'+value2.name+'</b><p>'+value2.text+'</p><br></div>';
to
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><h3>'+value2.name+'</h3><p>'+value2.text+'</p><br></div>';
The Collapsible needs the <h3> element instead of <b> to render.
Here is a demo of your exact code with the AJAX calls removed: http://jsfiddle.net/ezanker/RkLuV/
By the way, this is pretty much the answer I gave you here: collapsible list with collapsed items in jqm, it would probably have been better to continue the conversation there.

Related

Extract visual text from Google Classic Site page using Apps Script in Google Sheets

I have about 5,000 Classic Google Sites pages that I need to have a Google Apps script under Google Sheets examine one by one, extract the data, and enter that data into the Google Sheet row by row.
I wrote an apps script to use one of the sheets called "Pages" that contains the exactly URL of each page row by row, to run down while doing the extraction.
That in return would get the HTML contents and I would then use regex to extract the data I want which is the values to the right of each of the following...
Job name
Domain owner
Urgency/Impact
ISOC instructions
Which would then write that date under the proper columns in the Google Sheet.
This worked except for one big problem. The HTML is not consistent. Also, ID's and tags were not used so really it makes trying to do this through SitesApp.getPageByUrl not possible.
Here is the code I came up with for that attempt.
function startCollection () {
var masterList = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Pages");
var startRow = 1;
var lastRow = masterList.getLastRow();
for(var i = startRow; i <= lastRow; i++) {
var target = masterList.getRange("A"+i).getValue();
sniff(target)
};
}
function sniff (target) {
var pageURL = target;
var pageContent = SitesApp.getPageByUrl(pageURL).getHtmlContent();
Logger.log("Scraping: ", target);
// Extract the job name
var JobNameRegExp = new RegExp(/(Job name:<\/b><\/td><td style='text-align:left;width:738px'>)(.*?)(\<\/td>)/m);
var JobNameValue = JobNameRegExp.exec(pageContent);
var JobMatch = JobNameValue[2];
if (JobMatch == null){
JobMatch = "NOTE FOUND: " + pageURL;
}
// Extract domain owner
var DomainRegExp = new RegExp(/(Domain owner:<\/b><\/td><td style='text-align:left;width:738px'><span style='font-family:arial,sans,sans-serif;font-size:13px'>)(.*?)(<\/span>)/m);
var DomainValue = DomainRegExp.exec(pageContent);
Logger.log("DUMP1:",SitesApp.getPageByUrl(pageURL).getHtmlContent());
var DomainMatch = DomainValue[2];
if (JobMatch == null){
DomainMatch = "N/A";
}
// Extract Urgency & Impact
var UrgRegExp = new RegExp(/(Urgency\/Impact:<\/b><\/td><td style='text-align:left;width:738px'>)(.*?)(<\/td>)/m);
var UrgValue = UrgRegExp.exec(pageContent);
var UrgMatch = UrgValue[2];
if (JobMatch == null){
UrgMatch = "N/A";
}
// Extract ISOC Instructions
var ISOCRegExp = new RegExp(/(ISOC instructions:<\/b><\/td><td style='text-align:left;width:738px'>)(.*?)(<\/td>)/m);
var ISOCValue = ISOCRegExp.exec(pageContent);
var ISOCMatch = ISOCValue[2];
if (JobMatch == null){
ISOCMatch = "N/A";
}
// Add record to sheet
var row_data = {
Job_Name:JobMatch,
Domain_Owner:DomainMatch,
Urgency_Impact:UrgMatch,
ISOC_Instructions:ISOCMatch,
};
insertRowInTracker(row_data)
}
function insertRowInTracker(rowData) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Jobs");
var rowValues = [];
var columnHeaders = sheet.getDataRange().offset(0, 0, 1).getValues()[0];
Logger.log("Writing to the sheet: ", sheet.getName());
Logger.log("Writing Row Data: ", rowData);
columnHeaders.forEach((header) => {
rowValues.push(rowData[header]);
});
sheet.appendRow(rowValues);
}
So for my next idea, I have thought about using UrlFetchApp.fetch. The one problem I have though is that these pages on that Classics Google Site sit behind a non-shared with the public domain. While using SitesApp.getPageByUrl has the script ask for authorization and works, SitesApp.getPageByUrl does not meaning when it tries to call the direct page, it just gets the Google login page.
I might be able to work around this and turn them public, but I am still working on that.
I am running out of ideas fast on this one and hoping there is another way I have not thought of or seen. What I would really like to do is not even mess with the HTML content. I would like to use apps script under the Google Sheet to just look at the actual data presented on the page and then match a text and capture the value to the right of it.
For example have it go down the list of URLS on sheet called "Pages" and do the following for each page:
Find the following values:
Find the text "Job name:", capture the text to the right of it.
Find the text "Domain owner:", capture the text to the right of it.
Find the text "Urgency/Impact:", capture the text to the right of it.
Find the text "ISOC instructions:", capture the text to the right of it.
Write those values to a new row in sheet called "Jobs" as seen below.
Then move on the the next URL in the sheet called "Pages" and repeat until all rows in the sheet "Pages" have been completed.
Example of the data I want to capture
I have created an exact copy of one of the pages for testing and is public.
https://sites.google.com/site/2020dump/test
An inspect example
The raw HTML of the table which contains all the data I am after.
<tr>
<td style="width:190px"><b>Domain owner:</b></td>
<td style="text-align:left;width:738px">IT.FinanceHRCore </td>
</tr>
<tr>
<td style="width:190px"> <b>Urgency/Impact:</b></td>
<td style="text-align:left;width:738px">Medium (3 - Urgency, 3 - Impact) </td>
</tr>
<tr>
<td style="width:190px"><b>ISOC instructions:</b></td>
<td style="text-align:left;width:738px">None </td>
</tr>
<tr>
<td style="width:190px"></td>
<td style="text-align:left;width:738px"> </td>
</tr>
</tbody>
</table>
Any examples of how I can accomplish this? I am not sure how from an apps script perspective to go about not looking at HTML and only looking at the actual data displayed on the page. For example looking for the text "Job name:" and then grabbing the text to the right of it.
The goal at the end of the day is to transfer the data from each page into one big Google Sheet so we can kill off the Google Classic Site.
I have been scraping data with apps script using regular expressions for a while, but I will say that the formatting of this page does make it difficult.
A lot of the pages that I scrape have tables in them so I made a helper script that will go through and clean them up and turn them into arrays. Copy and paste the script below into a new google script:
function scrapetables(html,startingtable,extractlinksTF) {
var totaltables = /<table.*?>/g
var total = html.match(totaltables)
var tableregex = /<table[\s\S]*?<\/table>/g;
var tables = html.match(tableregex);
var arrays = []
var i = startingtable || 0;
while (tables[i]) {
var thistable = []
var rows = tables[i].match(/<tr[\s\S]*?<\/tr>/g);
if(rows) {
var j = 0;
while (rows[j]) {
var thisrow = tablerow(rows[j])
if(thisrow.length > 2) {
thistable.push(tablerow(rows[j]))
} else {thistable.push(thisrow)}
j++
}
arrays.push(thistable);
}
i++
}
return arrays;
}
function removespaces(string) {
var newstring = string.trim().replace(/[\r\n\t]/g,'').replace(/ /g,' ');
return newstring
}
function tablerow(row,extractlinksTF) {
var cells = row.match(/<t[dh][\s\S]*?<\/t[dh]>/g);
var i = 0;
var thisrow = [];
while (cells[i]) {
thisrow.push(removehtmlmarkup(cells[i],extractlinksTF))
i++
}
return thisrow
}
function removehtmlmarkup(string,extractlinksTF) {
var string2 = removespaces(string.replace(/<\/?[A-Za-z].*?>/g,''))
var obj = {string: string2}
//check for link
if(/<a href=.*?<\/a>/.test(string)) {
obj['link'] = /<a href="(.*?)"/.exec(string)[1]
}
if(extractlinksTF) {
return obj;
} else {return string2}
}
Running this got close, but at the moment, this doesn't handle nested tables well so I cleaned up the input by sending only the table that we want by isolating it with a regular expression:
var tablehtml = /(<table[\s\S]{200,1000}Job Name[\s\S]*?<\/table>)/im.exec(html)[1]
Your parent function will then look like this:
function sniff(pageURL) {
var html= SitesApp.getPageByUrl(pageURL).getHtmlContent();
var tablehtml = /(<table[\s\S]{200,1000}Job Name[\s\S]*?<\/table>)/im.exec(html)[1]
var table = scrapetables(tablehtml);
var row_data =
{
Job_Name: na(table[0][3][1]), //indicates the 1st table in the html, row 4, cell 2
Domain_Owner: na(table[0][4][1]), // indicates 1st table in the html, row 5, cell 2 etc...
Urgency_Impact: na(table[0][5][1]),
ISOC_Instructions: na(table[0][6][1])
}
insertRowInTracker(row_data)
}
function na(string) {
if(string) {
return string
} else { return 'N/A'}
}

using katex, '&' alignment symbol displays as 'amp;'

I am using katex to render math.
https://github.com/Khan/KaTeX
Generally, to get this to work I link to the files katex.min.js and katex.min.css from a cdn, which is one of the ways the directions suggest.
I wrap what needs to be rendered in tags and give all the same class. For example:
<span class='math'>\begin{bmatrix}a & b \\c & d\end{bmatrix}</span>
And inside a script tag I apply the following:
var math = document.getElementsByClassName('math');
for (var i = 0; i < math.length; i++) {
katex.render(math[i].innerHTML, math[i]);
}
So, my implementation works but there is a problem in what katex returns. The output of the above gives me:
This exact same question is asked here:
https://github.com/j13z/reveal.js-math-katex-plugin/issues/2
But I can't understand any of it.
The solution is to use element.textContent, not element.innerHTML.
If I use a form like what follows, the matrix will be rendered properly.
var math = document.getElementsByClassName('math');
for (var i = 0; i < math.length; i++) {
katex.render(math[i].textContent, math[i]); // <--element.textContent
}
A solution that works for me is the following (it is more of a hack rather than a fix):
<script type="text/javascript">
//first we define a function
function replaceAmp(str,replaceWhat,replaceTo){
replaceWhat = replaceWhat.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
var re = new RegExp(replaceWhat, 'g');
return str.replace(re,replaceTo);
}
//next we use this function to replace all occurences of 'amp;' with ""
var katexText = $(this).html();
var html = katex.renderToString(String.raw``+katexText+``, {
throwOnError: false
});
//hack to fix amp; error
var amp = '<span class="mord mathdefault">a</span><span class="mord mathdefault">m</span><span class="mord mathdefault">p</span><span class="mpunct">;</span>';
var html = replaceAmp(html, amp, "");
</script>
function convert(input) {
var input = input.replace(/amp;/g, '&'); //Find all 'amp;' and replace with '&'
input=input.replace(/&&/g, '&'); //Find all '&&' and replace with '&'. For leveling 10&x+ &3&y+&125&z = 34232
var html = katex.renderToString(input, {
throwOnError: false});
return html
}
Which version are you using?
Edit the src/utils.js and comment line number 51 to 55 after updated run in terminal npm run build command.

Doc Shell Swapping Example

I'm trying to write a simple example to show docshell swapping from one iframe to another.
I wrote this, can run from scratchpad with environment browser:
var doc = gBrowser.contentDocument;
var iframeA = doc.createElement('iframe');
iframeA.setAttribute('id', 'a');
iframeA.setAttribute('src', 'http://www.bing.com');
doc.documentElement.appendChild(iframeA);
var iframeB = doc.createElement('iframe');
iframeB.setAttribute('id', 'b');
iframeB.setAttribute('src', 'data:text/html,swap to here');
doc.documentElement.appendChild(iframeB);
doc.defaultView.setTimeout(function() {
var srcFrame = iframeA;
var targetFrame = iframeB;
doc.defaultView.alert('will swap now');
srcFrame.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(targetFrame)
doc.defaultView.alert('swap done');
}, 5000)
However this throws this error when it tries to swap:
/*
TypeError: Argument 1 of HTMLIFrameElement.swapFrameLoaders does not implement interface XULElement. Scratchpad/1:17
*/
Any ideas on how to fix?
Thanks
Thanks to #mook and #mossop from irc #extdev for the help.
The reason it didnt work was because:
pretty sure it needs to be a
Looks like gBrowser.contentDocument might be a html page? You must be using XUL elements
No idea if it even works in content either - I think it doesn't
and the things to be swapped must either both be type=content or neither can be, or something like that (translation: the type attribute of both source and target must be the same. so if one is content-primary the other must be content-primary as well)
This works:
var doc = document; //gBrowser.contentDocument;
var iframeA = doc.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'browser');
iframeA.setAttribute('id', 'a');
iframeA.setAttribute('src', 'http://www.bing.com');
iframeA.setAttribute('style', 'height:100px;');
doc.documentElement.appendChild(iframeA);
var iframeB = doc.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'browser');
iframeB.setAttribute('id', 'b');
iframeB.setAttribute('src', 'data:text/html,swap to here');
iframeB.setAttribute('style', 'height:100px;');
doc.documentElement.appendChild(iframeB);
doc.defaultView.setTimeout(function() {
var srcFrame = iframeA;
var targetFrame = iframeB;
doc.defaultView.alert('will swap now');
srcFrame.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(targetFrame)
doc.defaultView.alert('swap done');
}, 5000)
so in other words even if you made iframe with xul namespace it would not swap if you put it in an html document. like this example here:
var xulDoc = document;
var doc = gBrowser.contentDocument;
var iframeA = xulDoc.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'iframe');
iframeA.setAttribute('id', 'a');
iframeA.setAttribute('src', 'http://www.bing.com');
iframeA.setAttribute('type', 'content');
iframeA.setAttribute('style', 'height:100px;width:100px;');
doc.documentElement.appendChild(iframeA);
var iframeB = xulDoc.createElementNS('http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul', 'iframe');
iframeB.setAttribute('id', 'b');
iframeB.setAttribute('src', 'data:text/html,swap to here');
iframeB.setAttribute('type', 'content');
iframeB.setAttribute('style', 'height:100px;width:100px;');
doc.documentElement.appendChild(iframeB);
doc.defaultView.setTimeout(function() {
var srcFrame = iframeA;
var targetFrame = iframeB;
doc.defaultView.alert('will swap now');
srcFrame.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(targetFrame)
doc.defaultView.alert('swap done');
}, 5000)
It throws:
NS_ERROR_NOT_IMPLEMENTED: Scratchpad/2:21
this line is the swapFrameLoaders line
so its recommended to not use iframe with xul namespace as if you do you wont get the DOMContentLoaded and some other events http://forums.mozillazine.org/viewtopic.php?f=19&t=2809781&hilit=iframe+html+namespace
so its recommended to use element

Create table dynamically in jQuery Mobile

I am creating table plugin in JQM in html file. It works properly
html page
<table data-role="table" data-mode="columntoggle" class="ui-responsive table" id="service">
....
</table>
But i want to create a table dynamically. when doing this. JQM default plugin isn't apply
My code is
var service_table = $('<table data-role="table" data-mode="columntoggle" class="ui-responsive table" id="service"></table>')
var service_tr_th = $("<tr><th>Name</th></tr>");
var service_tr=$('<tr></tr>');
var service_name_td=$('<td>'+retServiceName+'</td>');
$(service_name_td).appendTo(service_tr);
$(service_tr_th).appendTo("#categories");
$(service_tr).appendTo(service_table);
$(service_table).appendTo("#categories");
in Html page
<div id="categories"></div>
When creating a column toggle table, add THEAD and TBODY, and data-priority="x" to the header cells (see http://demos.jquerymobile.com/1.4.2/table-column-toggle/). Finally call the .table() method to tell jQM to enhance the table:
var service_table = $('<table data-role="table" data-mode="columntoggle" class="ui-responsive table-stroke" id="service"></table>');
var service_tr_th = $("<thead><tr><th data-priority='1'>Name</th><th>Col2</th data-priority='2'></tr></thead>");
var service_tbody = $('<tbody></tbody>');
var service_tr = $('<tr></tr>');
var service_name_td = $('<td>' + retServiceName + '</td><td></td>');
service_name_td.appendTo(service_tr);
service_tr_th.appendTo(service_table);
service_tr.appendTo(service_tbody);
service_tbody.appendTo(service_table);
service_table.appendTo($("#categories"));
service_table.table();
Here is a DEMO
NOTE: you don't need $() around variables that are already jQuery collections e.g. service_tr, service_name_td, etc.

how to copy link name to title

i wanna ask how to change title in
name
so i want to make link name copy to title automatic
so if i make this code
title link
to
title link
how to do that in php or javascript
i know some in php
but need to make all words in link at database or make for every link variable $
can some one help me in that?
I'd suggest:
function textToTitle(elem, attr) {
if (!elem || !attr) {
// if function's called without an element/node,
// or without a string (an attribute such as 'title',
// 'data-customAttribute', etc...) then returns false and quits
return false;
}
else {
// if elem is a node use that node, otherwise assume it's a
// a string containing the id of an element, search for that element
// and use that
elem = elem.nodeType == 1 ? elem : document.getElementById(elem);
// gets the text of the element (innerText for IE)
var text = elem.textContent || elem.innerText;
// sets the attribute
elem.setAttribute(attr, text);
}
}
var link = document.getElementsByTagName('a');
for (var i = 0, len = link.length; i < len; i++) {
textToTitle(link[i], 'title');
}
JS Fiddle demo.
And since it seems traditional to offer a concise jQuery option:
$('a').attr('title', function() { return $(this).text(); });
JS Fiddle demo.
If you don't want to use a library:
var allLinks = document.getElementsByTagName('a');
for(var i = 0; i < allLinks.length; i++){
allLinks[i].title = allLinks[i].innerHTML;
}
Since you wanted to do all this to one element on the page, consider using something like this:
var allLinks = document.getElementById('myelement').getElementsByTagName('a'); // gets all the link elements out of #myelement
for ( int i = 0; i < allLinks.length; i++ ){
allLinks[i].title = allLinks[i].innerHTML;
}
Actually, this is roughly the same as before but we are changing the input elements.
Or, assuming you use jQuery, you could do something like this:
$('a').each(function(){ // runs through each link element on the page
$(this).attr('title', $(this).html()); // and changes the title to the text within itself ($(this).html())
});
In JQuery you can change an attribute by knowing the current tag and using the .attr() feature. Something like $('a').attr('title', 'new_title'); http://api.jquery.com/attr/

Resources