When I catch an error in ExtendScript, I would like to be able to log its stack trace. It appears that errors do not contain stack traces in ExtendScript, so I'm playing around with the idea of adding stack traces to errors.
The only way I know of to get a stack trace is $.stack. The field $.stack contains the current stack trace at the moment that you access the field.
My first attempt was to create my own error object that includes the stack. The Error object is very special in that it can get the line and filename of the code that created it. For example,
try {
throw new Error("Houston, we have a problem.");
}
catch (e) {
$.writeln("Line: " + e.line);
$.writeln("File: " + e.fileName);
$.writeln("Message: " + e.message);
}
Will print:
Line: 2
File: ~/Desktop/Source1.jsx
Message: Houston, we have a problem.
I don't think it's possible to create your own object with this ability. The closest I can get is this:
function MyError(msg, file, line) {
this.message = msg;
this.fileName = file;
this.line = line;
this.stack = $.stack;
}
try {
throw new MyError("Houston, we have a problem.", $.fileName, $.line);
}
catch (e) {
$.writeln("Line: " + e.line);
$.writeln("File: " + e.fileName);
$.writeln("Message: " + e.message);
$.writeln("Stack: " + e.stack);
}
Which prints:
Line: 9
File: ~/Desktop/Source2.jsx
Message: Houston, we have a problem.
Stack: [Source3.jsx]
MyError("Houston, we have a p"...,"~/Desktop/Source2.js"...,9)
Here we can see that I'm creating my own error object and explicitly passing it the line and file name (since MyError can't figure that out on its own). I've also included the current stack when the error gets created.
This works fine when I call my own error object, but it doesn't work when other code calls the regular Error object or when an error is generated automatically (e.g. by illegal access). I want to be able to get the stack trace of any error, no matter how it is generated.
Other approaches might be to modify Error's constructor, modify Error's prototype, or replace the Error object entirely. I haven't been able to get any of these approaches to work.
Another idea would be to put a catch block in every single method of my code and add the current stack to the error if it doesn't already have one. I would like to avoid this option if possible.
I'm out of ideas. Is there any way to get the stack trace of errors?
It isn't perfect, but I found a partial solution.
Fact 1: Error.prototype is an Error object.
Fact 2: The method Error.prototype.toString is called whenever an error is created.
Fact 3: The field Error.prototype.toString can be modified.
That method typically just returns the string "Error", so we can replace it with our own method that stores the stack and then returns the string "Error".
Error.prototype.toString = function() {
if (typeof this.stack === "undefined" || this.stack === null) {
this.stack = "placeholder";
// The previous line is needed because the next line may indirectly call this method.
this.stack = $.stack;
}
return "Error";
}
try {
throw new Error("Houston, we have a problem.");
}
catch (e) {
$.writeln("Line: " + e.line);
$.writeln("File: " + e.fileName);
$.writeln("Message: " + e.message);
$.writeln("Stack: " + e.stack);
}
Result:
Line: 11
File: ~/Desktop/Source10.jsx
Message: Houston, we have a problem.
Stack: [Source10.jsx]
toString()
It works! The only problem is automatic errors.
Error.prototype.toString = function() {
if (typeof this.stack === "undefined" || this.stack === null) {
this.stack = "placeholder";
// The previous line is needed because the next line may indirectly call this method.
this.stack = $.stack;
}
return "Error";
}
try {
var foo = null;
foo.bar;
}
catch (e) {
$.writeln("Line: " + e.line);
$.writeln("File: " + e.fileName);
$.writeln("Message: " + e.message);
$.writeln("Stack: " + e.stack);
}
Result:
Line: 12
File: ~/Desktop/Source12.jsx
Message: null is not an object
Stack: undefined
So it doesn't work on all errors, but its progress.
I've come up with another solution, though this one requires you to change some of your code. Instead of calling methods as usual:
myObject.myMethod1("Hello", "world");
You'll need to switch to calling methods like this:
myObject.do("myMethod1", "Hello", "world");
Here's a complete example of how it works:
Object.prototype.do = function stackHelper() {
// Convert the arguments into an array.
var argumentArray = Array.prototype.slice.call(arguments);
// Remove the first argument, which is the function's name.
var functionString = argumentArray.shift();
try {
this[functionString].apply(this, argumentArray);
}
catch (e) {
if (typeof e.stack === "undefined" || e.stack === null) {
e.stack = $.stack;
}
throw e;
}
};
var myObject = {
myMethod1: function myMethod1(myArg1, myArg2){
this.do("myMethod2", myArg1, myArg2);
},
myMethod2: function myMethod2(myArg1, myArg2){
this.do("myMethod3", myArg1, myArg2);
},
myMethod3: function myMethod3(myArg1, myArg2){
$.writeln(myArg1 + ", " + myArg2 + "!");
var foo = null;
foo.bar; // Throws an error.
},
};
try {
myObject.do("myMethod1", "Hello", "world");
}
catch (e) {
$.writeln("Stack: " + e.stack);
}
The output looks like this:
Hello, world!
Stack: [do.jsx]
stackHelper("myMethod1","Hello","world")
myMethod1("Hello","world")
stackHelper("myMethod2","Hello","world")
myMethod2("Hello","world")
stackHelper("myMethod3","Hello","world")
It's not a great solution, but at least it works on all errors.
As far as I know you cannot modify the [native Code] of the Error.prototype.toString-function. So I came up with this solution:
function ReturnCustomErrorString(e, additionalMessage)
{
try {
var errorString = e.toString();
errorString = errorString.concat("\n", "additionalMessage: " + additionalMessage + "\n", "file: " + e.fileName + "\n", "line: " + e.line + "\n", "stack-trace: \n" + $.stack);
return errorString;
}
catch (e) {
alert("Error in : " + ReturnCustomErrorString.name + "(...)\n" + e);
exit();
}
}
Usage:
try {
// code that does throw an error
} catch (e) {
alert(ReturnCustomErrorString(e));
}
Before I wrote this function I often did something like this in the catch-block:
alert(e);
Now I'm doing alert(ReturnCustomErrorString(e));, but I get much more useful information. So at the moment I think this solution is pretty good.
If you need simply show a custom message, I wrote this code.
I think that solved, ... for me it's ok.
try
{
app.selection[0].contents = 1
}
catch (myError)
{
alert(myError.number); // For check the number error
if (myError.number == 30477)
{
alert("Mensagem Edu\n" + "Line: " + myError.line + "\n" + "File: " + myError.fileName + "\n" + "Message: " + myError.message + "\n" + "Stack: " + myError.stack);
exit();
}
else (myError);
{}
exit();
}
I also found that extending the Error object causes issues, it has a "special status" in ExtendScript, unfortunately.
The best I could come up with is the following:
function WrappedError(error) {
return {
name: 'ExtendScriptError',
message: error.message,
source: error.source,
line: error.line,
stack: $.stack,
}
}
Which is used like this:
throw WrappedError(new Error('Error Message Goes Here'))
The key to making it work is creating a real "Error" (object), on the actual line where the error is occurring, this way, we can get the correct line number in our wrapped error, and we also have access to the "err.source", which will be interesting for providing context later.
Next, when evaluating ExtendScript code from the "CEP" side, I wrap the call in a try / catch:
function safeRun(code) {
const safeCode = `(function () {
function errorToPretty (err) {
var stack = (err.stack && err.stack.split('\\n')) || []
var lines = (err.source && err.source.split('\\n')) || []
err.line--
stack.shift()
stack.shift()
stack.pop()
stack.reverse()
return {
name: err.name,
message: err.message,
line: err.line,
code: err.code,
context: [
lines[err.line - 2] || '',
lines[err.line - 1] || '',
'---> ' + lines[err.line] || '',
lines[err.line + 1] || '',
lines[err.line + 2] || ''
],
stack: stack
}
}
try {
return ${code}
} catch (err) {
return JSON.stringify(errorToPretty(err))
}
})()`
return evalExtendscript(safeCode).then((res) => {
if (typeof res === 'object' && res.stack && res.line && res.message && res.context) {
const e = new Error(res.message + '\n\n' + res.context.join('\n'))
e.name = res.name
e.stack = `${res.name}: ${res.message}
${res.stack.map((func) => `at ${func} (extendscript.js:0:0)`).join('\n ')}`
throw e
}
return res
})
}
Related
I'm using METEOR#1.4.2.3 with following Plugins:
cordova:cordova-plugin-backbutton 0.3.0
cordova:cordova-plugin-camera 2.1.1
cordova:cordova-plugin-device 1.1.4
cordova:cordova-plugin-sim 1.3.2
cordova:cordova.plugins.diagnostic 2.3.16
cordova:phonegap-plugin-barcodescanner 3.0.0
I try to implement new right structure for Android API Level 26 (from API Level 22 to 26) and it doesn't work.
Code:
function onDeviceReady() {
console.log(device.cordova);
cordova.plugins.diagnostic.requestRuntimePermissions(function(statuses) {
for (var permission in statuses) {
switch (statuses[permission]) {
case cordova.plugins.diagnostic.permissionStatus.GRANTED:
console.log("Permission granted to use " + permission);
break;
case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
console.log("Permission to use " + permission + " has not been requested yet");
break;
case cordova.plugins.diagnostic.permissionStatus.DENIED:
console.log("Permission denied to use " + permission + " - ask again?");
break;
case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
console.log("Permission permanently denied to use " + permission + " - guess we won't be using it then!");
break;
}
}
}, function(error) {
console.error("The following error occurred: " + error);
}, [
cordova.plugins.diagnostic.permission.CAMERA,
cordova.plugins.diagnostic.permission.WRITE_EXTERNAL_STORAGE,
cordova.plugins.diagnostic.permission.READ_EXTERNAL_STORAGE,
cordova.plugins.diagnostic.permission.READ_PHONE_STATE
]);
}
I always get this error and APP is frozen:
I20180831-10:52:52.483(2)? 08-31 10:52:44.612 29275 29275 I chromium: [INFO:CONSOLE(5283)] "Uncaught TypeError: Cannot read property 'CAMERA' of undefined", source: http://localhost:12136/app/app.js?hash=4b27536f027d34508de1ae9fccb904c97e426df1 (5283)
Welcome to Stack Overflow.
An error message like this
Cannot read property 'CAMERA' of undefined"
Serves to tell you to look in your code for where there is a .CAMERA, which is this line:
cordova.plugins.diagnostic.permission.CAMERA,
Which means that cordova.plugins.diagnostic.permission is not defined. It means that cordova.plugins.diagnostic is defined, so you need to check the documentation for that plugin.
That will tell you where to look, and the next piece of advice is to write defensive code to allow for the unexpected. Something like this:
if (cordova && cordova.plugins && cordova.plugins.diagnostic && cordova.plugins.diagnostic.permission) {
// Everything is defined, expect cordova.plugins.diagnostic.permission.CAMERA to be valid
} else {
console.error("Something undefined in cordova.plugins.diagnostic.permission")
}
i fixed the problem.
I'm adding Plugin
cordova:cordova-plugin-android-permissions 1.0.0
to the project.
The modified Function OnDeviceReady:
function onDeviceReady() {
console.log(device.cordova);
var permissions = cordova.plugins.permissions;
permissions.checkPermission(permissions.READ_PHONE_STATE, function( status ){
if ( status.hasPermission ) {
console.log("Yes (READ_PHONE_STATE) ");
}
else {
console.warn("No (READ_PHONE_STATE) ");
permissions.requestPermission(permissions.READ_PHONE_STATE, success, error('READ_PHONE_STATE'));
}
});
permissions.checkPermission(permissions.CAMERA, function( status ){
if ( status.hasPermission ) {
console.log("Yes (CAMERA) ");
}
else {
console.warn("No (CAMERA) ");
permissions.requestPermission(permissions.CAMERA, success, error("CAMERA"));
}
});
permissions.checkPermission(permissions.WRITE_EXTERNAL_STORAGE, function( status ){
if ( status.hasPermission ) {
console.log("Yes (WRITE_EXTERNAL_STORAGE) ");
}
else {
console.warn("No (WRITE_EXTERNAL_STORAGE) ");
permissions.requestPermission(permissions.WRITE_EXTERNAL_STORAGE,
success,error("WRITE_EXTERNAL_STORAGE")
);
}
});
permissions.checkPermission(permissions.READ_EXTERNAL_STORAGE, function( status ){
if ( status.hasPermission ) {
console.log("Yes (READ_EXTERNAL_STORAGE) ");
}
else {
console.warn("No (READ_EXTERNAL_STORAGE) ");
permissions.requestPermission(permissions.READ_EXTERNAL_STORAGE, success, error('READ_EXTERNAL_STORAGE'));
}
});
function error(permission4request) {
console.warn(permission4request + ' permission is not turned on');
console.log(permission4request + ' permission is not turned on');
}
fdsf
function success( status ) {
if( !status.hasPermission ) error();
}
loginLogic();
}
This Code works with API Level 26 to request permissions.
I build a Ionic app which use Ionic's SqlitePlugin.
When I call execSql method,I found that it add a SELECT 1 sql before my sql.
log is below:
codova:SQLitePlugin.backgroundExecuteSqlBatch:[{"dbargs":{"dbname":"appdb"},"executes":[{"qid":null,"sql":"SELECT 1","params":[]},{"qid":null,"sql":"select * from [abcde] where k1 = ? ","params":["六块"]}]}]
I looked it's code,
SQLitePluginTransaction = function(db, fn, error, success, txlock, readOnly) {
if (typeof fn !== "function") {
/*
This is consistent with the implementation in Chrome -- it
throws if you pass anything other than a function. This also
prevents us from stalling our txQueue if somebody passes a
false value for fn.
*/
throw newSQLError("transaction expected a function");
}
this.db = db;
this.fn = fn;
this.error = error;
this.success = success;
this.txlock = txlock;
this.readOnly = readOnly;
this.executes = [];
if (txlock) {
this.addStatement("BEGIN", [], null, function(tx, err) {
throw newSQLError("unable to begin transaction: " + err.message, err.code);
});
} else {
this.addStatement("SELECT 1", [], null, null);
}
It sames that it add SELECT 1 when txlock is false. txlock is false when I call executeSql and is true when I call sqlBatch.
Why add SELECT ONE?
When I follow the examples for gulp-w3c-css I am unable to get the results to print in the console instead of in an output directory.
Compare how I am using CSSLint and W3C-CSS below. I'd like the function to be identical.
var gulp = require('gulp'),
csslint = require('gulp-csslint'),
cssvalidate = require('gulp-w3c-css');
gulp.task('csslint', () =>
gulp.src('testcss/laxhjalpen.css')
.pipe(csslint('.csslintrc'))
.pipe(csslint.reporter())
);
// Does not work
gulp.task('cssvalid', () =>
gulp.src('testcss/*css')
.pipe(cssvalidate())
// Next line works but is not what I want
.pipe(gulp.dest('reports'))
// I suppose I need to get this construct to work but I can't
.pipe(gutil.buffer(function(err, files) {
if (err) {
gutil.log('An error occured', err);
} else {
// No idea what to write
// files - array of validation results (from manual)
}
}))
);
The very best solution would be just to have a reporter function that works like the csslint.reporter does.
The gulp-w3c-css plugin serializes the validation results for each file to JSON and stores that JSON in the file.contents property. The format of that JSON serialization looks roughly like the following (for more details see the w3c-css documentation):
{
errors: [ { line: 5, message: 'Some error' },
{ line: 42, message: 'Some error' } ],
warnings: [ { line: 13, message: 'Some warning' },
{ line: 23, message: 'Some warning' } ]
}
So all you have to do is parse that JSON and then log the information to the console in any way you want.
Here's a simple example of how you could do it:
var gulp = require('gulp');
var cssvalidate = require('gulp-w3c-css');
var gutil = require('gulp-util');
var map = require('map-stream');
gulp.task('cssvalid', function () {
return gulp.src('testcss/*css')
.pipe(cssvalidate())
.pipe(map(function(file, done) {
if (file.contents.length == 0) {
console.log('Success: ' + file.path);
console.log(gutil.colors.green('No errors or warnings\n'));
} else {
var results = JSON.parse(file.contents.toString());
results.errors.forEach(function(error) {
console.log('Error: ' + file.path + ': line ' + error.line);
console.log(gutil.colors.red(error.message) + '\n');
});
results.warnings.forEach(function(warning) {
console.log('Warning: ' + file.path + ': line ' + warning.line);
console.log(gutil.colors.yellow(warning.message) + '\n');
});
}
done(null, file);
}));
});
I used map-stream instead of gutil.buffer() so that the results for each file are printed as soon as they are available instead of printing everything at the very end.
We are having an issue with signalR. We have an auction site that runs on signalr for real time bidding. We fixed some issues with the browser and everything seemed to be working well. Then we installed new relic on our server and noticed that every minute we are getting http error code 400 on signalr connect, reconnect and abort. Here's a screenshot:
SignalR connect and reconnect are the most time consuming operations of the site according to new relic.
Here is SignalR backend code (We use sql server as signalr backplane):
public class SignalRHub : Hub
{
public void BroadCastMessage(String msg)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<SignalRHub>();
hubContext.Clients.All.receiveMessage(msg);
}
}
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
string appString=string.Empty;
//Gets the connection string.
if (System.Configuration.ConfigurationSettings.AppSettings["SignaRScaleoutConn"] != null)
{
appString = System.Configuration.ConfigurationSettings.AppSettings["SignaRScaleoutConn"].ToString();
}
GlobalHost.DependencyResolver.UseSqlServer(appString);
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromMinutes(15); //I added this timeout, but it is not required.
app.MapSignalR();
}
}
The javascript client looks like this, it's lengthy, but most of it is jQuery to affect the DOM, I include it all in case something may be wrong inside it.
$(function () {
var chatProxy = $.connection.signalRHub;
$.connection.hub.start();
chatProxy.client.receiveMessage = function (msg) {
var all = $(".soon").map(function () {
var hiddenModelId = $("#hiddenListingId");
if (msg == hiddenModelId.val()) {
$.ajax({
async: "true",
url: "/Listing/AuctionRemainingTime",
type: "POST",
dataType: 'json',
data: '{ "listingID": "' + msg + '"}',
contentType: "application/json; charset=utf-8",
success: function (data) {
if (data != null) {
SoonSettings.HasReloadedThisTick = false;
var element = document.getElementById(msg);
var obj = JSON.parse(data)
// For Clock Counter End Date Time Interval
// Adds 2 minutes to the soon clock when bid is close to finishing.
var hdID = "hdn" + obj.ListingId;
var hdValue = $("#" + hdID);
if (obj.EndDate != hdValue.val()) {
SoonSettings.HasUpdated = false; //Allows clock to change color once it gets under two minutes.
$('#' + hdID).val(obj.EndDate);
Soon.destroy(element);
Soon.create(element, { //Recreates clock with the before 2 minute tick event.
'due': 'in ' + obj.Seconds + ' seconds',
'layout':'group label-uppercase',
'visual':'ring cap-round progressgradient-00fff6_075fff ring-width-custom gap-0',
'face':'text',
'eventTick': 'tick'
});
}
var highbid = obj.HighBidderURL;
// For Date Ends Info.
var ListingEndDate = $("#tdAuctionListingEndDate");
if (obj.EndDate != ListingEndDate.val()) {
$('#' + hdID).val(obj.EndDate);
ListingEndDate.text(obj.EndDate + " Eastern");
ListingEndDate.effect("pulsate", { times: 5 }, 5000);
}
else
{
$(".Bidding_Current_Price").stop(true, true); ///Removes the pulsating effect.
$(".Bidding_Current_Price").removeAttr("style"); //Removes unnecessary attribute from HTML.
}
//Bid div notification.
if (obj.AcceptedActionCount.replace(/[^:]+:*/, "") > 0) {
if (obj.Disposition != '' && obj.Disposition != null) {
if (obj.Disposition == "Neutral") {
$("#spanNeutralBid").show();
$("#divOutbidNotification").hide();
$("#spanPositiveBid").hide();
$("#divProxyBidNotification").hide();
}
else if (obj.Disposition == "Positive") {
$("#spanPositiveBid").show();
$("#divOutbidNotification").hide();
$("#spanNeutralBid").hide();
$("#divProxyBidNotification").hide();
}
else if (obj.Disposition == "Negative") {
$("#divOutbidNotification").show();
$("#spanNeutralBid").hide();
$("#spanPositiveBid").hide();
$("#divProxyBidNotification").hide();
}
else {
$("#divOutbidNotification").hide();
$("#spanNeutralBid").hide();
$("#divProxyBidNotification").hide();
$("#spanPositiveBid").hide();
}
}
}
// For Highlight Current Price when it is Updated
var hdCurrentPrice = $("#hdnCurrentPrice");
if (obj.CurrentPrice != hdCurrentPrice.val()) {
$(".Bidding_Current_Price").text(obj.CurrentPrice);
$(".Bidding_Current_Price").effect("pulsate", { times: 5 }, 5000);
$("#hdnCurrentPrice").val(obj.CurrentPrice);
}
else {
$(".Bidding_Current_Price").stop(true, true);
$(".Bidding_Current_Price").removeAttr("style");
}
// For ReservePrice Status
$("#spanReservePriceStatus").html(obj.ReservePriceStatus);
$("#smallReservePriceStatus").html(obj.ReservePriceStatus);
// For Bid Count
var spanBidCounter = $("#spanBidCount");
$(spanBidCounter).text(obj.AcceptedActionCount);
var stringAppend = "<tr id='trhHighBidder'><td><strong>HighBidder</strong></td>";
stringAppend += "<td>";
if (obj.isAdmin == true) {
stringAppend += "<a id='anchorHighBid' href=" + obj.HighBidderURL + ">";
stringAppend += "<span id='spanHighBidder'>" + obj.CurrentListingActionUserName + "</span>"
stringAppend += "</a>";
}
else {
stringAppend += "<span id='spanHighBidderAnonymous'>" + obj.CurrentListingActionUserName + "</span>";
}
stringAppend += "</td></tr>";
if (obj.AcceptedActionCount.replace(/[^:]+:*/, "") > 0) {
if ($("#tblAuctionDetail").find("#rowHighBidder").length > 0) {
if ($("#tblAuctionDetail").find("#trhHighBidder").length > 0) {
$("#trhHighBidder").remove();
}
}
else {
//add tr to table
if (!$("#tblAuctionDetail").find("#trhHighBidder").length > 0) {
$('#tblAuctionDetail > tbody > tr:eq(6)').after(stringAppend);
}
}
}
// For High Bidder
if (obj.isAdmin) {
var anchorElement = $("#anchorHighBid");
$(anchorElement).attr("href", obj.HighBidderURL);
var spanHighBidder = $("#spanHighBidder");
$(spanHighBidder).text(obj.CurrentListingActionUserName);
}
else {
var spanAdminHighBid = $("#spanHighBidderAnonymous");
$(spanAdminHighBid).text(obj.CurrentListingActionUserName)
}
}
},
error: function (xhr, textStatus, errorThrown) {
}
});
}
});
};
});
Is there anything wrong with the client or the server signalr code that may need to be changed to avoid these errors happening so often? The 400 code has the tendency of showing up almost every minute. I am very new to signalR and know very little of how to make effective code with it.
The real time bidding in the site does work, it's just to find a way to avoid these constant errors. Any help explaining anything of how signalR works is appreciated.
Thanks,
I'd give a try changing the transportation method of SignalR: http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client#transport
and check if problem persists.
If possible to get UserAgent from Bad Request log, try to narrow down which browsers get 400 error. I think, maybe, some browsers are not connecting with correct transport method.
i have been using colorbox lightbox(slideshow) but the problem is whenever i click on the image i get "settings is undefined". which setting is it talking about. i am confused. i tried to change the settings but i can't find what to change. the settings it showed was
if (settings.slideshow && $related[1]) {
start = function () {
$slideshow
.text(settings.slideshowStop)
.unbind(click)
.bind(event_complete, function () {
if (index < $related.length - 1 || settings.loop) {
timeOut = setTimeout(publicMethod.next, settings.slideshowSpeed);
}
})
.bind(event_load, function () {
clearTimeout(timeOut);
})
.one(click + ' ' + event_cleanup, stop);
$box.removeClass(className + "off").addClass(className + "on");
timeOut = setTimeout(publicMethod.next, settings.slideshowSpeed);
};
stop = function () {
clearTimeout(timeOut);
$slideshow
.text(settings.slideshowStart)
.unbind([event_complete, event_load, event_cleanup, click].join(' '))
.one(click, function () {
publicMethod.next();
start();
});
$box.removeClass(className + "on").addClass(className + "off");
};
if (settings.slideshowAuto) {
start();
} else {
stop();
}
} else {
$box.removeClass(className + "off " + className + "on");
}
}
what do i have to change here. thanks for any suggestion or help.
You don't change anything there. You are likely getting that error because you are doing someone on your end that is removing the settings data. Show what you are doing, not snippets of code from the plugin.