How can I extract a javascript variable from html content using xpath? - xpath

I am stuck with the following sample html content: It's a little trickier than I thought.
<div id="map-control">
<script type="text/javascript">...</script>
<script type="text/javascript">
var extStreetviewControl = null;
var pano = null;
var point = new google.maps.LatLng('56.18497', '10.21306');
var noFlash = false;
var noStreetView = false;
</script>
I need to extract the coordinate values var point . I figured regex would do the trick. But I don't know how to proceed. How can I achieve that using xpath?

Your html is marlformed (missing the closing <div>), but try something like
substring-after(//script[2], 'var point = ')

Related

what to do with a function that produces a random image on button click in Javascript

I've written a function that should produce a random image from an array in Javascript.
I'm wanting to execute the function with an html button. I've written the code but it doesn't work. The image should be directed to a flex box div.
Code
var myImages=
["https://picsum.photos/536/354","Images/IMG_4830.jpeg","Images/IMG_4338.jpeg",
"Images/IMG_4096.JPG"];
function randomImages(){
var rnd = Math.floor(Math.random()*myImages.length);
if(rnd == 0){
rnd = 1;
}
var image = document.createElement("img");
var div=document.getElementById("flex-box-create").src = myImages[rnd]
div.appendChild(image)
}
button = <div><button class="btn btn-primary" id="image-Generator"
onclick="randomImages()">Click</button></div>
thanks
The source should be set on the image not the div, try this:
var image = document.createElement("img");
image.src = myImages[rnd];
var div = document.getElementById("flex-box-create");
div.appendChild(image);

Data Validation range from different spreadsheets

I have in my main spreadsheet a dropdown with data validation of a range from another tab in the same spreadsheet with data imported from another spreadsheet or with IMPORTRANGE function or imported with a script.
In both cases the main spreadsheet is very slow cause I have a lot of tab with data imported with both methods.
There is a way to do the data validation of the dropdown in the main sheet taking the data I need directly from the other spreadsheets without import them previously in the main spreadsheet with the IMPORTRANGE function or with a script?
I have tried to write a draft script but not works:
function externalSheetDataValidation() {
var cell = SpreadsheetApp.getActiveRange();
var dataValidationSheet = SpreadsheetApp.openById("xxxxxxxxxx");
var sheet = dataValidationSheet.getSheets()[0];
var range = sheet.getRange("B2:B5000");
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(range, true)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
Logger.log(dataValidationSheet.getName());
}
You want to populate a dropdown based on the values of a range from a different spreadsheet.
You are currently importing those values to a sheet in your spreadsheet in order to use them via requireValueInRange.
You would like to skip the import process.
If that's the case, you can just do the following:
Create a function that returns a simple array with the values from the source range:
function importSheetA() {
return SpreadsheetApp.openById('xxxxx')
.getSheetByName('xxxxx')
.getRange('xxxxx')
.getValues()
.flat(); // This ensures a simple array is returned
}
Populate the dropdowns with requireValueInList instead of requireValueInRange, using the values returned by importSheetA:
function populateDropdown() {
var values = importSheetA();
var rule = SpreadsheetApp.newDataValidation()
.requireValueInList(values, true)
.setAllowInvalid(false)
.build();
var range = SpreadsheetApp.getActiveRange();
range.setDataValidation(rule);
}
Note:
You could update the populated options when the source range is edited if you install an onEdit trigger, and you could also specify what range of cells should be populated with dropdowns without them being necessarily selected, but I'm not sure that's what you want.
Update:
If your data has more than 500 items, value in list criteria is not an option. Your only other option would be to use List from a range instead, but as you said, this would require the source range to be on the same spreadsheet as the dropdown, which you wanted to avoid.
As a workaround, I'd suggest you to programmatically copy the data to a hidden sheet in the target spreadsheet, and use the data in this hidden sheet as your source range when creating the dropdown. For example, this:
function copyRange() {
var cell = SpreadsheetApp.getActiveRange();
var rangeNotation = "B2:B5000"; // Change according to your preferences
var sourceData = SpreadsheetApp.openById(xxxxx)
.getSheetByName(xxxxx)
.getRange(rangeNotation)
.getValues();
var targetSS = SpreadsheetApp.getActiveSpreadsheet();
var hiddenSheetName = "Hidden source data"; // Change according to your preferences
var hiddenSheet = targetSS.getSheetByName(hiddenSheetName);
if (!hiddenSheet) hiddenSheet = targetSS.insertSheet(hiddenSheetName);
var sourceRange = hiddenSheet.getRange(rangeNotation);
sourceRange.setValues(sourceData);
hiddenSheet.hideSheet();
var rule = SpreadsheetApp.newDataValidation()
.requireValueInRange(sourceRange, true)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
Reference:
requireValueInList(values, showDropdown)
Cached Dropdown Dialog
GS:
function getSelectOptions(){
const cs=CacheService.getScriptCache();
const v=JSON.parse(cs.get('cached'));
if(v){return v;}
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Options');
var rg=sh.getDataRange();
var vA=rg.getValues();
var options=[];
for(var i=0;i<vA.length;i++)
{
options.push(vA[i][0]);
}
cs.put('cached', JSON.stringify(vA), 300)
return vA;
}
function showMyselectionDialog() {
SpreadsheetApp.getUi().showModelessDialog(HtmlService.createHtmlOutputFromFile('ah2'), 'Selections');
}
html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<script>
google.script.run
.withSuccessHandler(function(vA) {
updateSelect(vA);
})
.getSelectOptions();
function updateSelect(vA,id){//the id allows me to use it for other elements
var id=id || 'sel1';
var select = document.getElementById(id);
select.options.length = 0;
for(var i=0;i<vA.length;i++)
{
select.options[i] = new Option(vA[i],vA[i]);
}
}
</script>
<body>
<select id='sel1'></select>
</body>
</html>

code highlighting apex (Firefox 31)

The Oracle Application Express code editor is just plain back text on white background. No Code highlighting. Also I can't press "tab" without the textfield loosing focus.
I am using firefox 31 (can't upgrade, rescricted by Admin at work here) Also I can't install plugins. I know you can change css on specific sites using a special folder in firefox ("chrome"-folder / userContent.css). I already used this to change die default size of the textfield, because it was frickin small everytime I opened the edit page.
So do you know any framework or script I can use in Apex ? (I could copy that shit to jsfiddle.net every time but that sucks
(I also found the scratchpad in Firefox, which can run js and jquery. Does that help ?)
[SOLVED]
since you can't use
<script src = "">
etc. in plain js, I had to use loadScript. For css files it was even more complicated, but I got it all working.
This is my code, I run it in scratchpad (firefox). It uses ACE to change a div to an editor with highlighting. When clicking apply I revert the editor-changes in the DOM but keep the text/code.
// Load Ace js
loadScript("http://cdnjs.cloudflare.com/ajax/libs/ace/1.1.01/ace.js", function(){
//initialization code
});
// Load Ace css
var cssId = 'myCss'; // you could encode the css path itself to generate id..
if (!document.getElementById(cssId)){
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = cssId;
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/css/bootstrap.min.css';
link.media = 'all';
head.appendChild(link);
}
// change textarea to div
var editorRegion = document.getElementById('F4000_P4651_PLUG_SOURCE_fieldset');
editorRegion.innerHTML = editorRegion.innerHTML.replace("textarea","div");
// run ACE
highlight();
// Modify the apply Button in Apex to first revert ACE-Editor to normal, then do the usual apply.
var applyChanges = document.getElementById('B3456326662');
applyChanges.setAttribute("onclick","modifiedApply()");
function modifiedApply(){
close();
setTimeout(normalApply, 500);
}
function normalApply(){
javascript:apex.submit('Apply_Changes');
}
// Revert ACE-Changes, but keep changed text/code.
function close(){
var value = editor.getValue();
editor.destroy();
var oldDiv = editor.container;
var newDiv = oldDiv.cloneNode(false);
newDiv.textContent = value;
oldDiv.parentNode.replaceChild(newDiv, oldDiv);
newDiv.outerHTML = newDiv.outerHTML.replace("div","textarea");
var old_new_old = document.getElementById('F4000_P4651_PLUG_SOURCE');
old_new_old.textContent = old_new_old.textContent.substring(0, old_new_old.textContent.length - 6);
}
var editor;
function highlight() {
editor = ace.edit("F4000_P4651_PLUG_SOURCE");
editor.setTheme("ace/theme/monokai");
editor.getSession().setUseWorker(false);
editor.getSession().setMode("ace/mode/javascript");
document.getElementsByClassName('ace_print-margin')[0].setAttribute("style","left:1000px");
}
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}

nvd3 add nvd3 chart dom element from code

I'm using the following code:
http://plnkr.co/edit/CIGW0o?p=preview
in this code the html contains
<nvd3 options="options" data="data"></nvd3>
Im trying to add it from the code:
$scope.buildNewChart = function(){
var xmlString = "<nvd3 options='options' data='data' class='ng-isolate-scope'></nvd3>";
var wrapper= document.createElement('div');
wrapper.innerHTML= xmlString;
var div= wrapper.firstChild;
var elem = document.getElementById("charts");
elem.appendChild(div);
}
but it not works as I can see the element (<nvd3 options="options" data="data"></nvd3>) but its empty
To add some element with Angular, you should use $compile
$scope.buildNewChart = function(){
var chart = angular.element("<nvd3 options='options' data='data' class=''></nvd3>");
var elem = angular.element(document.getElementById("charts"));
elem.append(chart);
$compile(chart)($scope)
}
P.S. Also it goes against Angular practices. Consider to add it into initial HTML, but hide it with ng-if or something

CKEDITOR -- cannnot restore cursor location after DOM modification

I've read this excellent answer to pretty much the same question. However, I have tried every technique that #Reinmar recommended, and none of them seem to work.
The situation is that I am taking the current HTML from the editor and wrapping certain pieces in span tags. I then set the now modified HTML back and try to restore the user's cursor location. No technique works.
Here is a very simple example to reproduce the issue:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script src="//cdn.ckeditor.com/4.4.7/standard/ckeditor.js"></script>
</head>
<body>
<textarea id="cktest"><p>Sometimes Lorem. Sometime Ipsum. Always dolor.</p></textarea>
<script type="text/javascript">
(function () {
var checkTimeout;
var bookmark;
var storeCursorLocation = function(editor) {
bookmark = editor.getSelection().createBookmarks();
};
var restoreCursorLocation = function(editor) {
editor.getSelection().selectBookmarks(bookmark);
};
var validateText = function(editor) {
storeCursorLocation(editor);
var data = editor.document.getBody().getHtml();
data = data.replace("Lorem", "<span class='err-item'>Lorem</span>");
editor.document.getBody().setHtml(data);
restoreCursorLocation(editor);
};
CKEDITOR.replace('cktest', {
on: {
'instanceReady': function(evt) {
},
'key' : function(evt) {
clearTimeout(checkTimeout);
checkTimeout = setTimeout(function () {
validateText(evt.editor);
}, 1000);
}
}
});
})();
</script>
</body>
</html>
This code starts a timer when a user presses a key, and then waits for 1 second after they stop pressing keys to do the check.
Copy this to a new .html file and run it in your favorite browser (I am using Chrome).
When the CKEditor loads, use the mouse to place your cursor somewhere in the middle of the text. Then press the CTRL key and wait 1 second. You will see your cursor jump back to the start of the text.
This code example uses
editor.getSelection().createBookmarks();
to create the bookmark. But I have also tried:
editor.getSelection().createBookmarks(true);
and
editor.getSelection().createBookmarks2();
I have also tried just saving the range using
var ranges = editor.getSelection().getRanges();
and
editor.getSelection().selectRanges(ranges);
in the restoreCursorLocation function.
(function () {
var checkTimeout;
var bookmark;
var storeCursorLocation = function( editor ) {
bookmark = editor.getSelection().createBookmarks( true );
};
var restoreCursorLocation = function( editor ) {
//editor.focus();
editor.getSelection().selectBookmarks( bookmark );
};
var validateText = function( editor ) {
storeCursorLocation( editor );
var data = editor.document.getBody().getHtml();
data = data.replace( "spaceflight", "<span class='err-item'>spaceflight</span>" );
editor.document.getBody().setHtml( data );
restoreCursorLocation( editor );
//fire this event after DOM changes if working with widgets
//editor.fire( 'contentDomInvalidated' );
};
var editor = CKEDITOR.replace( 'editor1', {
extraAllowedContent : 'span(err-item)',
on: {
"pluginsLoaded" : function( event ){
editor.on( 'contentDom', function() {
var editable = editor.editable();
editable.attachListener( editable, 'keyup', function( e ) {
clearTimeout( checkTimeout );
checkTimeout = setTimeout(function () {
validateText( editor );
}, 100 );
});
});
}
}
});
})();
I have checked your code, made some corrections and the above seems to work fine. I know you said you have tried it but for me createBookmarks(true) has done the trick.
Explanations and Notes:
You needed to use createBookmarks(true) which inserts unique span into HTML. Such bookmark is not affected by changes you are doing inside the DOM (there are limits of course e.g. your custom changes remove bookmark).
It was clever to use getBody().getHtml() and getBody().setHTML(). If you have used editor.getData() this would have removed empty spans that represent bookmarks.
Please note however that such approach may break widgets so it is required to fire contentDomInvalidated event after such changes.
I was also focusing editor before restoring selection but this is “just in case” solution, as I have noticed that editor selects bookmark without it. If however, for some reason, you are losing the selection, this would be another thing to use.
Here you have working example: http://jsfiddle.net/j_swiderski/nwbsywnn/1/
Check the default behaviour when you set innerHtml in https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML
Removes all of element's children, parses the content string and assigns the resulting nodes as children of the element
The bookmarks in CKEDITOR are hidden span elements and setting innerHtml will remove all those elements.
Anyway the solution is very simple.
Change your storeCursorLocation function to this
var storeCursorLocation = function(editor) {
bookmark = editor.getSelection().createBookmarks(true);
};
When you pass true as parameters it will use the ids as the reference instead of storing the DOM elements so you can restore then after an innerHtml change.
{Edit}
Reading Solution 2 from #Reinmar he says
If you can avoid uncontrolled innerHTML changes and instead append/remove/move some nodes, then just remember that you have to preserve these elements and this method will work perfectly. You can also move bookmarks' elements if your modifications should change the selection as well.
This is how you do it if you can't replace the contents of the element innerHtml.
This solution is less efficient but might work in some scenarios
Change the validateText function to this.
var validateText = function(editor) {
storeCursorLocation(editor);
var parent = editor.document.getBody().$.firstChild,
nodes = parent.childNodes,
nodeText,
words,
index = 0,
current,
newElement;
while (index < nodes.length) {
current = nodes[index];
nodeText = current.nodeValue;
if (current.nodeType === Node.TEXT_NODE && nodeText.indexOf('Lorem') !== -1) {
words = nodeText.split('Lorem');
newElement = document.createTextNode(words[0]);
parent.insertBefore(newElement, current);
newElement = document.createTextNode(words[1]);
parent.insertBefore(newElement, current.nextSibling);
newElement = document.createElement('span')
newElement.className = 'err-item';
newElement.innerHTML = 'Lorem';
parent.replaceChild(newElement, current);
break;
}
index++;
}
restoreCursorLocation(editor);
};
Basically I'm transversing the nodes of the first p in the chkeditor body and replacing only the node of type text that contains Lorem with a span and add the remaining text before and after as text elements. If you replace the whole text like you were doing it will remove from the DOM the bookmarks so when you tried to restore they don't exist.

Resources