javascript:
(function(){
function ShowPrintStyles(Style)
{
var $Style=$(Style);
$Style.text($Style.text().replace('@media screen', '@media xxscreen').replace('@media print', '@media screen'));
$Style.attr('ShowingPrintStyles', true);
console.log(Style);
}
ShowHide = window.ShowPrintStyles;
if (ShowHide)
{
window.ShowPrintStyles=false;
$('style[OrigSrc]').each(function(id, item){
var $Link = $('<link>').attr({rel:'stylesheet', href:$(item).attr('OrigSrc')});
$(item).replaceWith($Link);
});
} else {
window.ShowPrintStyles=true;
$('link').each(function(id, item){
var $Style = $('<style>').attr({OrigSrc:$(item).attr('href')})
.load($(item).attr('href'), null, function(){
ShowPrintStyles($Style[0]);
$(item).replaceWith($Style);
});
});
}
})();
The ShowPrintStyles function takes an inline style element, replaces any "@media screen" sections with "@media xxscreen", then does likewise for the print sections to screen.
This obviously has the effect of disabling any screen-only styles, while enabling the print-only ones (Assuming you are viewing on a screen that is - if not then adjust the javascript to suit your device)
A property is set on the global window object to indicate whether we are showing or hiding the print styles.
If we are showing then any link elements are replaced with an inline style so that we can modify the css content.
If we are hiding then the inline styles are replaced with their original link elements.
A bit about the stuff I've done
Thursday, 28 November 2013
Viewing print styles with firebug
Wednesday, 24 July 2013
Private members in javascript classes
Whoever told you this couldn't be done was lying.
By utilising closures we can create a class that holds its own private members which cannot be accessed outside of that class - even by adding a new function onto the class
Consider the following javascript function
function Outer()
{
var PrivateVar='PrivateValue';
function Inner()
{
PrivateVar='Some Value';
}
}
This is known as a closure.
You can read more about them here:
http://stackoverflow.com/questions/111102/how-do-javascript-closures-work
Put simply the inner function has access to the variables in the outer function.
These variables are "live" - that is they are not fixed when the inner function was created.
Now consider that the Outer and Inner functions are, in fact, constructors
function MyClassWithPrivateVars(params)
{
var PrivateVar;
function MyClass(params)
{
PrivateVar='Initial Value';
}
return new MyClass(params);
}
var Instance = new MyClassWithPrivateVars('whatever');
When you call the outer constructor you are actually calling the inner constructor.
The inner constructor has access to the vars within the outer constructor as before.
Let's add a method to the inner constructor.
This new method of course has access to the variables in the outer constructor.
In this instance the method is a "property".
function MyClassWithPrivateVars(params)
{
var PrivateVar;
function MyClass(params)
{
PrivateVar='Initial Value';
this.PublicProperty = function(value)
{
if (typeof(value)!=="undefined")
{
PrivateVar=value;
}
return PrivateVar;
}
}
return new MyClass();
}
Now if we create an instance of MyClassWithPrivateVars
and invoke the property we'll see that it gets the value as expected.
var MyInstance=new MyClassWithPrivateVars();
console.log(MyInstance.PublicProperty());
Outputs "Initial Value"
because the private variable was set to that value in the constructor.
MyInstance.PublicProperty(10);
console.log(MyInstance.PublicProperty());
This time we get 10
So is the variable really private?
Well - let's try and access it directly:
console.log(MyInstance.PrivateVar);
This should output undefined
as PrivateVar is not a member of the inner class.
Likewise
console.log(MyClassWithPrivateVars.PrivateVar);
is also undefined because PrivateVar is a member of the instance of the outer class - not of the constructor itself.
Friday, 31 May 2013
Type 'System.Collections.Generic.IDictionary`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' is not supported for deserialization of an array.
So I was getting this error when calling a page method from jquery. Google turned up quite a few variations but not the exact string, and the suggestions associated with those variations were not relevant. It turned out that in my case the data packet I was sending in the jquery ajax call was wrong. I had code like this:
MyParam = [];
...
$.ajax({
type: 'POST',
url: document.location.pathname + '/DoSomething',
data: JSON.stringify(MyParam),
contentType: "application/json; charset=utf-8",
dataType: "json",
complete: function (a, b, c, d) { console.log(a, b, c, d); }
});
and my page method looked like this:
[WebMethod()]
public static void DoSomething(List<s> B)
{
B.ToString();
}
fairly simple but there are 2 things wrong with this. The first problem was the data - I hadn't named the paramater
so the fixed javascript code looks like this:
MyParam = [];
...
$.ajax({
type: 'POST',
url: document.location.pathname + '/DoSomething',
data: JSON.stringify({B:MyParam}),
contentType: "application/json; charset=utf-8",
dataType: "json",
complete: function (a, b, c, d) { console.log(a, b, c, d); }
});
The second problem was it turns out the .NET javascript seraliser doesn't like List<t> so a quick change to the C# fixed that.
[WebMethod()]
public static void DoSomething(S[] B)
{
B.ToString();
}
So in summary - make sure you pass an object with named parameters as the data argument - not simple the content of the only parameter!
Friday, 4 January 2013
Staging long loops in Javascript
Sometimes you may even get a message pop up asking if you want to terminate a stuck script.
Not only does this make for a bad user experience but it can cause scripts to terminate without warning.
The solution?
Plenty of posts on forum sites such as StackOverflow will tell you to use setTimeout.
It can get kind of messy though - so I thought I'd write a wrapper function to take away the pain, and have something that *looks* mostly like the familiar simple loop.
Now obviously there is a performance hit by adding all these extra "unnecessary" delays. (approx. 30% for a tight loop, less for a complex one) BUT if the browser pops up a box and waits for the user to respond, this will add much more delay to the total execution time!
so - heres the code:
function StagedLoop(IterationsBetweenPauses, Delay, AtLeastOneDelay, Test, Run, Complete)
{ /// <summary>Stages a loop, inserting timer pauses every n Iterations
///Warning - this method is asyncronus and will exit before looping is completed!
/// put code to be run only after looping has finished into the Complete inline function parameter
///</summary>
///<param name="Test">A method to run which will perform the "test" part of the loop to see if looping should continue. This test will be run at the start of the loop (i.e. the loop may run 0 or more times)</param>
///<param name="Run">A method which will be run on every iteration of the loop - don't forget to increment any counters!</param>
///<param name="IterationsBetweenPauses">How many loops to run before using a timer pause - suggestion: use a number between 10 and 10000, but if iteration counters will appear anywhere on screen don't use a round number (i.e. 1000)</param>
///<param name="Delay">Milliseconds to set the timer pause for between stages. Recommended value: 1 - this gives the browser ample time to do "stuff" but avoids unnecassary delay. N.B. the actual delay could be substantially longer than the one specified here due to the way browser events are handled</param>
///<param name="Complete">Function to run when the loop has exited</param>
if (IterationsBetweenPauses.IterationsBetweenPauses)
{
Run = IterationsBetweenPauses.Run;
Delay = IterationsBetweenPauses.Delay;
Complete = IterationsBetweenPauses.Complete;
AtLeastOneDelay = IterationsBetweenPauses.AtLeastOneDelay;
Test = IterationsBetweenPauses.Test;
IterationsBetweenPauses = IterationsBetweenPauses.IterationsBetweenPauses;
}
if (AtLeastOneDelay)
{
setTimeout(StagedLoop, Delay, { Test: Test, Run: Run, IterationsBetweenPauses: IterationsBetweenPauses, Delay: Delay, Complete: Complete, AtLeastOneDelay: false });
return;
}
var RemainingIterations = IterationsBetweenPauses;
while (RemainingIterations > 0)
{
if (Test && !Test()) { Complete(); return; }
Run();
RemainingIterations--;
}
setTimeout(StagedLoop, Delay, { Test: Test, Run: Run, IterationsBetweenPauses: IterationsBetweenPauses, Delay: Delay, AtLeastOneDelay: AtLeastOneDelay, Complete: Complete });
}
And here is an example usage:
var I = 0;
StagedLoop(4567, 1, false,function () { return I < 10000; }, function ()
{
DoSomethingWith(I);
I++; //don't forget to increment!
},function(){
//run the next bit of code here
});
//N.B. code places here will be run *before* the loop exists (possibly before it runs even a single iteration)
//Don't put any code here!
This would replace a simple for loop such as this one:
for (var I=0;I<10000;I++)
{
DoSomethingWith(I);
}
//run the next bit of code here
ok - so it's not quite as clean, but it gets the job done with minimal fuss! Complete test harness below showing the difference in times taken
<div id="here">Please Wait... - there is a good chance the browser will "hang" at this point</div>
<script type="text/javascript">
setTimeout(RunTest, 100);
function RunTest()
{
var Iterations = 100000;
var I;
var Start = new Date().getTime();
for (I = 0; I < Iterations; I++)
{
document.getElementById('here').innerHTML = I;
}
var End = new Date().getTime();
var Normal = (End - Start);
I = 0;
Start = new Date().getTime();
StagedLoop(4567, 1, false,function () { return I < Iterations; }, function ()
{
document.getElementById('here').innerHTML = I;
I++;
}, function ()
{
document.getElementById('here').innerHTML = 'Please wait...';
End = new Date().getTime();
var Staged = (End - Start);
console.log('Duration for staged loop: ' + Staged);
console.log('Duration for "normal" loop: ' + Normal);
document.getElementById('here').innerHTML = 'Duration for "normal" loop: ' + Normal + '<br/>' +
'Duration for staged loop: ' + Staged + '<br/>' +
'% performance loss: ' + Math.round(((100.0 * (Staged - Normal)) / Normal)) + '%';
console.log(Staged - Normal);
console.log(100.0 * (Staged - Normal));
console.log(((100.0 * (Staged - Normal)) / Normal));
//end inline function
});
//N.B. code places here will be run *before* the loop exits (possibly before it runs even a single iteration)
//Don't put any code here!
}
function StagedLoop(IterationsBetweenPauses, Delay, AtLeastOneDelay, Test, Run, Complete)
{/// <summary>Stages a loop, inserting timer pauses every n Iterations
///Warning - this method is asyncronus and will exit before looping is completed!
/// put code to be run only after looping has finished into the Complete inline function parameter
///</summary>
///<param name="Test">A method to run which will perform the "test" part of the loop to see if looping should continue. This test will be run at the start of the loop (i.e. the loop may run 0 or more times)</param>
///<param name="Run">A method which will be run on every iteration of the loop - don't forget to increment any counters!</param>
///<param name="IterationsBetweenPauses">How many loops to run before using a timer pause - suggestion: use a number between 10 and 10000, but if iteration counters will appear anywhere on screen don't use a round number (i.e. 1000)</param>
///<param name="Delay">Milliseconds to set the timer pause for between stages. Recommended value: 1 - this gives the browser ample time to do "stuff" but avoids unnecassary delay. N.B. the actual delay could be substantially longer than the one specified here due to the way browser events are handled</param>
///<param name="Complete">Function to run when the loop has exited</param>
if (IterationsBetweenPauses.IterationsBetweenPauses)
{
Run = IterationsBetweenPauses.Run;
Delay = IterationsBetweenPauses.Delay;
Complete = IterationsBetweenPauses.Complete;
AtLeastOneDelay = IterationsBetweenPauses.AtLeastOneDelay;
Test = IterationsBetweenPauses.Test;
IterationsBetweenPauses = IterationsBetweenPauses.IterationsBetweenPauses;
}
if (AtLeastOneDelay)
{
setTimeout(StagedLoop, Delay, { Test: Test, Run: Run, IterationsBetweenPauses: IterationsBetweenPauses, Delay: Delay, Complete: Complete, AtLeastOneDelay: false });
return;
}
var RemainingIterations = IterationsBetweenPauses;
while (RemainingIterations > 0)
{
if (Test && !Test()) { Complete(); return; }
Run();
RemainingIterations--;
}
setTimeout(StagedLoop, Delay, { Test: Test, Run: Run, IterationsBetweenPauses: IterationsBetweenPauses, Delay: Delay, AtLeastOneDelay: AtLeastOneDelay, Complete: Complete });
}
</script>