If the text We are sorry but we made a boo boo appears then
Wait 5 seconds
reload
I would like to do this in JavaScript.
Here is an attempt
(function () {
"use strict";
function walkTheDOM(node, func) {
if (node && node.nodeType) {
if (typeof func === "function") {
func(node);
}
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
}
function filterElementsByContains(elements, string) {
var toStringFN = {}.toString,
text = toStringFN.call(elements),
result,
length,
i,
element;
if (text !== "[object NodeList]" && text !== "[object Array]" && !($() instanceof jQuery)) {
return result;
}
result = [];
if (typeof string === "string") {
string = new RegExp("^" + string + "$");
} else if (toStringFN.call(string) !== "[object RegExp]") {
return result;
}
function getText(node) {
if (node.nodeType === 3) {
text += node.nodeValue;
}
}
length = elements.length;
i = 0;
while (i < length) {
text = "";
element = elements[i];
walkTheDOM(element, getText);
if (string.test(text)) {
result.push(element);
}
i += 1;
}
return result;
}
if(filterElementsByContains([document.getElementsByTagName("table")[0]], /We are sorry but we made a boo boo/).length) {
location.reload();
}
The above could should, I think, work for the text if it appears in a specific place. I want to make it more general - so that the text could appear anywhere on that page.
Also, I would like to know how to add a pause so that, for example, it waits 5 seconds before reloading.
I guess I would add incorporate something like:
setTimeout(
function()
{
//location.reload();
}, 5000);
Just do an indexOf on the body's textContent/innerText property
var content = document.body.textContent || document.body.innerText;
var hasText = content.indexOf("We are sorry but we made a boo boo")!==-1;
if(hasText){
setTimeout(function(){
window.location = "http://www.example.com";
},5000);
}
This may work:
var bodyText = document.body.textContent || document.body.innerText;
var msg = "We are sorry but we made a boo boo";
if (bodyText.indexOf(msg) > -1) {
setTimeout(function() {
location.reload();
}, 5000);
}
--sorry for nearly duplicate answer :\ --
--edit--
Tell me - how can I add a second rule, it's a different way of
phrasing the error message: There was an internal error in our system.
We logged the problem and will investigate it later.
This will check for both messages:
var bodyText = document.body.textContent || document.body.innerText;
var msg = [
"We are sorry but we made a boo boo",
"There was an internal error in our system. We logged the problem and will investigate it later."
];
var flag = false;
for (var i = 0; i < msg.length; i++) {
if bodyText.indexOf(msg[i]) {
flag = true;
}
}
if (flag) {
setTimeout(function() {
location.reload();
}, 5000);
}
Explanation: All I did was modify msg to be an array of strings rather than a string itself. Then for every msg we want to check, we loop through the values and compare msg against bodyText. If bodyText contains one of the msg's, we set a flag to true, and then perform an if statement on flag.
If you want to check anywhere in the page... then you have to do just that. Get every DOM element and check if there is your String there... I do not think there is another way.
Yes using setTimeout will do the trick for waiting before reload.
Related
Basically, I want to wait for an element to appear (e.g. dynamically loaded element). I want to know how I can edit it to support xpath,different DOM documents (e.g. dynamically loaded iframe) and also set a timeout value so that it will quit if after X number of seconds, the element is till not found, throw an error that I can handle.
waitUntilExists(xpath,document,5,function(){
// Element exists, if after 5 seconds and it doesn't exist handle it here.
})
original code to modify:
(function(){
var _waitUntilExists = {
pending_functions : [],
loop_and_call : function()
{
if(!_waitUntilExists.pending_functions.length){return}
for(var i=0;i<_waitUntilExists.pending_functions.length;i++)
{
var obj = _waitUntilExists.pending_functions[i];
var resolution = document.getElementById(obj.id);
if(obj.id == document){
resolution = document.body;
}
if(resolution){
var _f = obj.f;
_waitUntilExists.pending_functions.splice(i, 1)
if(obj.c == "itself"){obj.c = resolution}
_f.call(obj.c)
i--
}
}
},
global_interval : setInterval(function(){_waitUntilExists.loop_and_call()},5)
}
if(document.addEventListener){
document.addEventListener("DOMNodeInserted", _waitUntilExists.loop_and_call, false);
clearInterval(_waitUntilExists.global_interval);
}
window.waitUntilExists = function(id,the_function,context){
context = context || window
if(typeof id == "function"){context = the_function;the_function = id;id=document}
_waitUntilExists.pending_functions.push({f:the_function,id:id,c:context})
}
waitUntilExists.stop = function(id,f){
for(var i=0;i<_waitUntilExists.pending_functions.length;i++){
if(_waitUntilExists.pending_functions[i].id==id && (typeof f == "undefined" || _waitUntilExists.pending_functions[i].f == f))
{
_waitUntilExists.pending_functions.splice(i, 1)
}
}
}
waitUntilExists.stopAll = function(){
_waitUntilExists.pending_functions = []
}
})()
I'm new in StackOverflow and not sure whether I should ask this question here or not, so if I'm asking this question at wrong place, please let me know.
I want to implement dataSource in javascript (like ASP.NET). So I have created a jQuery plugin with these functions:
$.getUniqueString = function (prefix) {
if (!prefix) prefix = "s";
for (var loopIndex = 0; true; loopIndex++) {
if (typeof window[prefix + loopIndex] != "undefined") { // if sourceId exists
continue;
}
prefix = prefix + loopIndex;
break;
}
return prefix;
}
$.fn.dataSource = function (source) {
var sourceId;
if (!source) {
sourceId = $(this).attr("data-source-id");
return window[sourceId];
}
sourceId = $.getUniqueString();
$(this).attr("data-source-id", sourceId);
window[sourceId] = source;
}
This plugin works fine. And till now, I have not faced any difficulty. Here is the link to a working example (fiddle): http://jsfiddle.net/Gu2KQ/
But, my questions are:
Is my code safe enough for the client browser not to crash?
Any suggestion to optimize this code more?
Any other option to implement the same functionality better?
Any help would be appreciated.
Eval is unnecessary here. You can get and set global variables as properties of the window object.
$.getUniqueString = function (prefix) {
if (!prefix) prefix = "s";
for (var loopIndex = 0; true; loopIndex++) {
if (typeof window[prefix + loopIndex] != "undefined") { // if sourceId exists
continue;
}
prefix = prefix + loopIndex;
break;
}
return prefix;
}
$.fn.dataSource = function (source) {
var sourceId;
if (!source) {
sourceId = $(this).attr("data-source-id");
return window[sourceId];
}
sourceId = $.getUniqueString();
$(this).attr("data-source-id", sourceId);
window[sourceId] = source;
}
Ideally, I would like them all running, but when I have the second and/or third one active, Script 1 fails. (It just does nothing)
I wonder, is it possible to merge them into one script? Would that solve the problem? (I am tempted to try cutting and pasting them into one script just to see what happens)
Script 1 (the reloader)
(function () {
"use strict";
function walkTheDOM(node, func) {
if (node && node.nodeType) {
if (typeof func === "function") {
func(node);
}
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
}
function filterElementsByContains(elements, string) {
var toStringFN = {}.toString,
text = toStringFN.call(elements),
result,
length,
i,
element;
if (text !== "[object NodeList]" && text !== "[object Array]" && !($() instanceof jQuery)) {
return result;
}
result = [];
if (typeof string === "string") {
string = new RegExp("^" + string + "$");
} else if (toStringFN.call(string) !== "[object RegExp]") {
return result;
}
function getText(node) {
if (node.nodeType === 3) {
text += node.nodeValue;
}
}
length = elements.length;
i = 0;
while (i < length) {
text = "";
element = elements[i];
walkTheDOM(element, getText);
if (string.test(text)) {
result.push(element);
}
i += 1;
}
return result;
}
if(!filterElementsByContains([document.getElementsByTagName("table")[0]], /We are proud to announce that the November discounts have been chosen/).length) {
location.reload();
}
}());
Script 2 (Jump to last sheet, if it's multi sheet)
function getPreviousLink(){
var nextLink = document.getElementById('pagination-next-link');
var links = document.getElementsByClassName('v_page_nav')[0].getElementsByTagName("a");
for(var i=0; i < links.length; i++){
if(links[i] == nextLink) { return links[i-1]; }
}
}
var link = getPreviousLink();
link.target="_blank";
link.click();
Script 3 (open previous sheet, if there is one)
var link = document.getElementById('pagination-prev-link');
link.target="_blank";
link.click();
If the second 2 scripts aren't changing anything that would cause your first script to break (like something the first script looks for gets removed by script 2).... I would suggest going into 'manage scripts' and changing the order they run in... sometimes that can fix issues like this.
Occassionally I get the exception "PageMap asked for range which it does not have" from my Ext Js 4.2.1 infinite scrolling grid. It is raised in data/PageMap.js on line 211. Of course one should not ask for non-existing entries, but this is sometimes done by the framework itself. Seems to be somehow connected to adding/removing records or reloading the grid. There are already some threads on this topic in the Sencha forum, e.g. this, but no killer solution or bugfix was proposed yet.
Meanwhile, I have to keep this exception from the users' eyes. What would be a good way to do so? Tricky thing is that it is sometimes provoked just by the user moving the scrollbar, so there is no single line of my code directly involved.
I found the root cause to be that when it's rendering rows, it determines if it's before a selected row. If it's working on the last row, it still looks for row + 1. (Ext.view.Table:931 in 4.2.1)
My simple solution is to just make it return false:
Ext.override(Ext.selection.RowModel,
{
isRowSelected: function (record, index)
{
try
{
return this.isSelected(record);
}
catch (e)
{
return false;
}
}
});
Christoph,
I have similar troubles with "PageMap asked for range which it does not have" during asynchronuous refreshing of grids. I catched some of sources of errors in the ExtJS 4.2.1 code and created simple override, that works for me. You can try if it will work for you. I will be happy for your feedback.
Ext.override(Ext.view.Table, {
getRecord: function (node) {
node = this.getNode(node);
if (node) {
var recordIndex = node.getAttribute('data-recordIndex');
if (recordIndex) {
recordIndex = parseInt(recordIndex, 10);
if (recordIndex > -1) {
// Eliminates one of sources of "PageMap asked for range which it does not have" error
if (this.store.getCount() > 0) {
return this.store.data.getAt(recordIndex);
}
}
}
return this.dataSource.data.get(node.getAttribute('data-recordId'));
}
},
renderRow: function (record, rowIdx, out) {
var me = this,
isMetadataRecord = rowIdx === -1,
selModel = me.selModel,
rowValues = me.rowValues,
itemClasses = rowValues.itemClasses,
rowClasses = rowValues.rowClasses,
cls,
rowTpl = me.rowTpl;
rowValues.record = record;
rowValues.recordId = record.internalId;
rowValues.recordIndex = rowIdx;
rowValues.rowId = me.getRowId(record);
rowValues.itemCls = rowValues.rowCls = '';
if (!rowValues.columns) {
rowValues.columns = me.ownerCt.columnManager.getColumns();
}
itemClasses.length = rowClasses.length = 0;
if (!isMetadataRecord) {
itemClasses[0] = Ext.baseCSSPrefix + "grid-row";
if (selModel && selModel.isRowSelected) {
var storeRows = this.getStore().getCount();
// Eliminates one of sources of "PageMap asked for range which it does not have" error
if (rowIdx + 1 < storeRows) {
if (selModel.isRowSelected(rowIdx + 1)) {
itemClasses.push(me.beforeSelectedItemCls);
}
}
if (selModel.isRowSelected(record)) {
itemClasses.push(me.selectedItemCls);
}
}
if (me.stripeRows && rowIdx % 2 !== 0) {
rowClasses.push(me.altRowCls);
}
if (me.getRowClass) {
cls = me.getRowClass(record, rowIdx, null, me.dataSource);
if (cls) {
rowClasses.push(cls);
}
}
}
if (out) {
rowTpl.applyOut(rowValues, out);
} else {
return rowTpl.apply(rowValues);
}
}
});
all these codes don't work for me, after many debugging I wrote this override which solve the problem.
Ext.define('overrides.LruCache', {
override: 'Ext.util.LruCache',
// private. Only used by internal methods.
unlinkEntry: function (entry) {
// Stitch the list back up.
if (entry) {
if (this.last && this.last.key == entry.key)
this.last = entry.prev;
if (this.first && this.first.key == entry.key)
this.first = entry.next;
if (entry.next) {
entry.next.prev = entry.prev;
} else {
this.last = entry.prev;
}
if (entry.prev) {
entry.prev.next = entry.next;
} else {
this.first = entry.next;
}
entry.prev = entry.next = null;
}
}
});
This is my solution for my specific case with the same error
it somehow lost DOM element for child
this code fix that
Ext.define('override.Ext.view.Table', {
/**
* Returns the node given the passed Record, or index or node.
* #param {HTMLElement/String/Number/Ext.data.Model} nodeInfo The node or record
* #param {Boolean} [dataRow] `true` to return the data row (not the top level row if wrapped), `false`
* to return the top level row.
* #return {HTMLElement} The node or null if it wasn't found
*/
override: 'Ext.view.Table',
getNode: function (nodeInfo, dataRow) {
// if (!dataRow) dataRow = false
var fly,
result = this.callParent(arguments)
if (result && result.tagName) {
if (dataRow) {
if (!(fly = Ext.fly(result)).is(this.dataRowSelector)) {
result = fly.down(this.dataRowSelector, true)
}
} else if (dataRow === false) {
if (!(fly = Ext.fly(result)).is(this.itemSelector)) {
result = fly.up(this.itemSelector, null, true)
}
if (this.xtype == 'gridview' && !this.body.dom.querySelector(`#${result.id}`)) {
result = null
}
}
}
return result
},
})
I am using Typeahead by twitter. I am running into this warning from Intellij. This is causing the "window.location.href" for each link to be the last item in my list of items.
How can I fix my code?
Below is my code:
AutoSuggest.prototype.config = function () {
var me = this;
var comp, options;
var gotoUrl = "/{0}/{1}";
var imgurl = '<img src="/icon/{0}.gif"/>';
var target;
for (var i = 0; i < me.targets.length; i++) {
target = me.targets[i];
if ($("#" + target.inputId).length != 0) {
options = {
source: function (query, process) { // where to get the data
process(me.results);
},
// set max results to display
items: 10,
matcher: function (item) { // how to make sure the result select is correct/matching
// we check the query against the ticker then the company name
comp = me.map[item];
var symbol = comp.s.toLowerCase();
return (this.query.trim().toLowerCase() == symbol.substring(0, 1) ||
comp.c.toLowerCase().indexOf(this.query.trim().toLowerCase()) != -1);
},
highlighter: function (item) { // how to show the data
comp = me.map[item];
if (typeof comp === 'undefined') {
return "<span>No Match Found.</span>";
}
if (comp.t == 0) {
imgurl = comp.v;
} else if (comp.t == -1) {
imgurl = me.format(imgurl, "empty");
} else {
imgurl = me.format(imgurl, comp.t);
}
return "\n<span id='compVenue'>" + imgurl + "</span>" +
"\n<span id='compSymbol'><b>" + comp.s + "</b></span>" +
"\n<span id='compName'>" + comp.c + "</span>";
},
sorter: function (items) { // sort our results
if (items.length == 0) {
items.push(Object());
}
return items;
},
// the problem starts here when i start using target inside the functions
updater: function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
};
$("#" + target.inputId).typeahead(options);
// lastly, set up the functions for the buttons
$("#" + target.buttonId).click(function () {
window.location.href = me.format(gotoUrl, $("#" + target.inputId).val(), target.destination);
});
}
}
};
With #cdhowie's help, some more code:
i will update the updater and also the href for the click()
updater: (function (inner_target) { // what to do when item is selected
return function (item) {
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}}(target))};
I liked the paragraph Closures Inside Loops from Javascript Garden
It explains three ways of doing it.
The wrong way of using a closure inside a loop
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Solution 1 with anonymous wrapper
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
Solution 2 - returning a function from a closure
for(var i = 0; i < 10; i++) {
setTimeout((function(e) {
return function() {
console.log(e);
}
})(i), 1000)
}
Solution 3, my favorite, where I think I finally understood bind - yaay! bind FTW!
for(var i = 0; i < 10; i++) {
setTimeout(console.log.bind(console, i), 1000);
}
I highly recommend Javascript garden - it showed me this and many more Javascript quirks (and made me like JS even more).
p.s. if your brain didn't melt you haven't had enough Javascript that day.
You need to nest two functions here, creating a new closure that captures the value of the variable (instead of the variable itself) at the moment the closure is created. You can do this using arguments to an immediately-invoked outer function. Replace this expression:
function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, target.destination);
return item;
}
With this:
(function (inner_target) {
return function (item) { // what to do when item is selected
comp = me.map[item];
if (typeof comp === 'undefined') {
return this.query;
}
window.location.href = me.format(gotoUrl, comp.s, inner_target.destination);
return item;
}
}(target))
Note that we pass target into the outer function, which becomes the argument inner_target, effectively capturing the value of target at the moment the outer function is called. The outer function returns an inner function, which uses inner_target instead of target, and inner_target will not change.
(Note that you can rename inner_target to target and you will be okay -- the closest target will be used, which would be the function parameter. However, having two variables with the same name in such a tight scope could be very confusing and so I have named them differently in my example so that you can see what's going on.)
In ecmascript 6 we have new opportunities.
The let statement declares a block scope local variable, optionally initializing it to a value.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Since the only scoping that JavaScript has is function scope, you can simply move the closure to an external function, outside of the scope you're in.
Just to clarify on #BogdanRuzhitskiy answer (as I couldn't figure out how to add the code in a comment), the idea with using let is to create a local variable inside the for block:
for(var i = 0; i < 10; i++) {
let captureI = i;
setTimeout(function() {
console.log(captureI);
}, 1000);
}
This will work in pretty much any modern browser except IE11.