Thursday, December 18, 2008

Getting the "skinny" on skinning Spry menus

Massimo Foti is ever tweaking code he runs across and is probably the biggest reason I ever got into Dreamweaver extensions in a bit way. Lately it seems that some of his spare time has been turned on Spry. He recently announced in the Adobe Spry forum that he is releasing a set of skins for the Spry menu bar in Office 2003 styles: Silver, Green, and Blue.

I've thought a good bit recently that skinning Spry widgets would be quite useful, as nobody likes out of the box styles these days. I mean, imagine a Spry version of Themeroller!

Labels:

Tuesday, December 16, 2008

Spry 1.7 feature preview updates SpryData for basic xpath functionality

Looks like some action on the Adobe Spry Framework front and a preview of a change that the 1.7 release will bring. Better basic xpath support in the Spry Data sets are set in the preview that can allow users to avoid including xpath.js which can save ~72kb according to the post on the Spry Team's blog.

I'm looking forward to seeing what else might be included in the 1.7 release. I think that it would be good if the Spry team could post a roadmap for the Spry framework so that we can see what sort of widgets and functionality are being planned. This would allow people to be able to comment on what is proposed, as well as give Spry widget developers some ideas on what to avoid working on so as to prevent duplicate work.

Labels:

Saturday, September 20, 2008

Pesudo Threading in JavaScript: Watch what you're doing

I was working on some debugging and I wanted to do some logging of some variables that were going through numerous changes. However I didn't want to be littering my code with all the debug/logging code, you just know that I'd leave some of it in and cause me to do more searching and potentially have additional errors down the road.

Anyway, I ended up using the JavaScript object watch method when working with simple values by adding the watch to the parent object. This allowed me to keep my debugging/logging code in just a couple of places. The watch method also allows you to perform data validation by intercepting changes to values.

A basic example:

// Basic logger object
function Logger(){
this.loggedItems = [];
this.logItem = function(itemToLog){
this.loggedItems.push(itemToLog);
}
this.showLog = function(){
alert(this.loggedItems.join('\n'));
}
}
// Create an instance of the logger object
var myLog = new Logger();

// Basic object
function Person(){
this.age = 0;

// add watch on age property
this.watch('age',
function(prop, oldVal, newVal){
myLog.logItem('setting ' + prop + ' to ' + newVal);
return newVal;
}
);
}

// Create new object
var p = new Person();
// set value
p.age = 80;
// drink from the fountain of youth
p.age = 70;
p.age = 60;
p.age = 50;
// Let's see that log
myLog.showLog();
// result:
// setting age to 80
// setting age to 70
// setting age to 60
// setting age to 50


So what this does is to react any time that age is changed. So when age is changed, a function intercepts that change and is passed three parameters, the name of the property that was watched, the old value of the property, and then the new value of the property. This function then returns the value that will be used as the value that is actually assigned to the property when it is changed. In this example, we aren't doing any validation (you know a person can't be -12 years old right?), so we could check the value and if it isn't a valid value being assigned to the property, then we can return the old value and effectively not make any change to the property.

What we are doing instead is logging the new value. This allows us to track the changes to this property as the application runs. In this simple example, we just just show the log after a couple of changes. You could expand this to more of your simple value properties.

When I'm done debugging I just pull the code from the parent objects rather than from many places within the application. Still prone to leaving in some logging code, but it seems less so than logging at every point the property is changed.

A caveat: Changes to an object or adding an element to an array aren't handled with the watch method as the "value" of the property being watched isn't changing, that is, the array contents are changing, but it's still that same array, and it's the array that is being watched, not the contents of the array, same thing with an object.

Of course there are other ways to log and may be some better ways to add the logging in that would even further limit any issues when the debugging is over, but I just wanted to get across the basic usage of the watch method.

So what's with the "pseduo threading" in the post title? Well that is basically from the processing that occurs, it steps through executing each line as it reaches it, until the assignment of the watched property, the processing then breaks off to the function passed into the watch method, does what ever processing needed, then execution picks up where it left off on the next line after the property assignment. So it's sorta "threaded" in that the other code gets executed without being directly called in the code, but it's pseduo in that it's actually executed inline.

Alright, I think that's enough rambling for now.

Labels: ,

Saturday, August 16, 2008

Help for creating custom Spry transitions

I've mentioned one way to add more transitions to Spry in Upping Your Transition Count, but that assumes that you have pre-created easing equations. What if you none of the ones you've found meet your needs? Well you can create your own...if you know how to write one. All I've seen in my research is that your function needs to work like f(0) = 0 and f(1) = 1, but really what does that mean to someone that doesn't work with math functions much, and what is the 0 and 1 in this case when easing equations take 4 parameters (at least that's the case with Spry and Robert Penner's equations)?

To be honest, I really don't know (at least not yet), but I have found a tool that can be useful in creating a custom equation. Check out the easing function generator, a Flash application that allows you to use drag and drop or directly enter in some values to see what your easing equation will look like. For use with Spry, make sure to select the F5/FMX radio button at the top of the application. Drag and drop the numbered boxes at the bottom which will change the graph of the curve on the left. Then to see how the new equation changes the movement of an object, click on the red ball, then click elsewhere in the movie and the ball will move to that second location using your equation. Rinse lather, repeat as necessary to get an transition equation that you like.

Once you have an equation, copy the text of the function and paste it into SpryEffects.js at the end of the built in equations. Make sure to give the equation a unique name, and put a comma at the end of the previous equation. growSpecificTransition is the last transition in the default SpryEffects.js file (at least in 1.6.1), and the resulting code would look similar to:
growSpecificTransition: function(time, begin, change, duration)
{
if (time > duration) return change+begin;
var pos = time/duration;
return begin + (5 * Math.pow(pos, 3) - 6.4 * Math.pow(pos, 2) + 2 * pos) * change;
},

myNewTransition: function(t, b, c, d) {
var ts=(t/=d)*t;
var tc=ts*t;
return b+c*(17.495*tc*ts + -42.84*ts*ts + 35.695*tc + -11.2*ts + 1.85*t);
}


Once you add that to your copy of SpryEffects.js, then you can use your new transition like you would any other transition. For example, an accordion could be written as:
var Accordion1 = new Spry.Widget.Accordion("Accordion1", {transition: Spry.Effect.Transitions.myNewTransition});

Or because SpryEffects.js automatically adds all transition off of the Spry obejct, youc an use:
var Accordion1 = new Spry.Widget.Accordion("Accordion1", {transition: Spry.myNewTransition});

You can also try to integrate your new equation into the Spry Effects Transition Sample (online, or within the Spry download package: /samples/effects/transition_sample.html). Just add your equation to the SpryEffects.js for the sampler and add the name of your equation to the transitions select list, and you should be good to go.

Please post a link to any examples of any fun easing equations you've come up with. Also, if you happen to know how to write easing equations and can explain how in layman's terms, please do post your explanation, thanks!

Labels: , , ,

Wednesday, July 30, 2008

Speaking at MAX 2008: Extending Spry Framework

I added the speaker badge a few days ago, but forgot to mention that I'll be speaking at MAX 2008 in San Francisco in November. The title of the session if Extending the Spry Framework, and I'll be giving it on Wednesday, November 19th at 9:30 am - 10:30 am. Check out the session browser select Spry from the Product list to see the Spry related sessions. Here's the session description:
Move beyond the standard widgets, transitions, and effects available with the Spry framework. We’ll explore how both novices and power users can enhance their work by extending existing widgets to add new functionality, building new widgets with custom transitions from powerful Spry base components, and combining multiple visual effects to enhance the user experience.
If you have any thoughts on what widgets you'd like to see added to the framework, please do leave a comment so I can try to work it into the presentation.

If you're coming to MAX, please stop by and we can talk Dreamweaver and Spry!

Wednesday, July 2, 2008

Conceptual condensation: meaning scales

Well, I guess that didn't take long, only about 10 minutes since the last one, so here's another in the series.

Hitting one of my frequent blog stops I ran into "meaning scales" on gapingvoid, in particular this quote:

The size of the endeavor doesn't matter as much as how meaningful it becomes to you.


Source: Meaning Scales, People Don't

Labels:

Conceptual condensation: choice architechs

Every once in a while I run into a word or term that just seems to resonate with me, and not necessarily because I agree with the concept the words are trying to convey. Find the term for the first time will just make me sit back and think for a while. I've already blogged about one such term: cognitive heatsink.

I ran across another resonating term this afternoon. I've tracked Stephen Few's blog for a bit now. He has an opinionated view on data visualization. I don't know about you, but I like it when folks have a strong passion for something and can articulate that passion, especially if they are strong opinions weakly held. That is, no "staying the course" just because you decided on the course and no amount of new information can ever cause you to change your mind. Anyway, when I read his review of Nudge: Improving Decisions About Health, Wealth, and Happiness I ran into the term: "choice architects".

It's not clear if the term is pulled from the book itself, or is just Few's way of discussing the book, but it just seemed to strike a chord with me regardless of its source. I think that it has stuck with me because I'm finishing up the specification stage of a project where I feel that we've put quite a lot of effort into balancing the huge number of potential choices that could be made by the user against the much smaller number of choices that most folks will want to be able to make most of the time. So I'll be thinking of myself as a choice architect in future projects, and I hope that keeping this new concept in mind will make me better at what I do. That's one reason I'm blogging it today as I can go back later and see if this term really has stuck with me, or if it is just another passing fancy.

As this is the second time I've blogged about a term that rolled around for a while in my brain, I think that I'll make "conceptual condensation" a recurring post topic. If you have a term that just seems to stick with you for a while, even if you don't know why at the time you first ran into it, please post the term, your thoughts on why it resonates, and if appropriate include a link to where encountered the term.

Please note: I didn't say or mean condescension in the post title. :-)

Labels:

How to Create Spry Tabbed panels with the tabs at the bottom

Spry Tabbed panels in 1.6.1 allow you to have your tabs at the top or on the left. Dreamweaver CS3 & CS4 (try the CS4 beta) will allow you to insert the tabs on the top, but it's pretty easy to change to vertical tab on the left by swapping out the class for the widget wrapper from TabbedPanels to VTabbedPanels. See the Spry Tabbed Panels documentation for more info on how the default tabbed panels work.

However, if you want you tabs to appear on the bottom of the panels, then you're outta luck, by default that is. Fortunately the Tabbed Panels are pretty easy to tweak to allow you to have your tabs on the bottom, by making three small changes. The first is to reorder the code that comprises the widget such that the container for the tabs is after the container for the panel content. So you move from:
<div id="TabbedPanels1" class="TabbedPanels">
<ul class="TabbedPanelsTabGroup">
<li class="TabbedPanelsTab" tabindex="0">Tab 1</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 2</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 3</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 4</li>
</ul>
<div class="TabbedPanelsContentGroup">
<div class="TabbedPanelsContent">Content 1</div>
<div class="TabbedPanelsContent">Content 2</div>
<div class="TabbedPanelsContent">Content 3</div>
<div class="TabbedPanelsContent">Content 4</div>
</div>
</div>

To:
<div id="TabbedPanels1" class="TabbedPanels">
<div class="TabbedPanelsContentGroup">
<div class="TabbedPanelsContent">Content 1</div>
<div class="TabbedPanelsContent">Content 2</div>
<div class="TabbedPanelsContent">Content 3</div>
<div class="TabbedPanelsContent">Content 4</div>
</div>
<ul class="TabbedPanelsTabGroup">
<li class="TabbedPanelsTab" tabindex="0">Tab 1</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 2</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 3</li>
<li class="TabbedPanelsTab" tabindex="0">Tab 4</li>
</ul>
</div>

This change allows for easy display of the tabs at the bottom, because the tabs are now below the content panels.


Then you need to tweak the CSS a bit. You can change the code in the SpryTabbedPanels.css, but you may need the default for some other tabbed panel instance. So if you add the following style block to your page (or add it to a separate CSS file):
<style type="text/css">
<!--
.TabbedPanelsTab {
position: relative;
top: -1px;
float: left;
padding: 4px 10px;
margin: 0px 1px 0px 0px;
font: bold 0.7em sans-serif;
background-color: #DDD;
list-style: none;
border-left: solid 1px #CCC;
border-bottom: solid 1px #999;
border-top: solid 1px #999;
border-right: solid 1px #999;
-moz-user-select: none;
-khtml-user-select: none;
cursor: pointer;
}
.TabbedPanelsTabSelected {
background-color: #EEE;
border-top: 1px solid #EEE;
}
-->
</style>

If you compare the selectors to the matching styles in SpryTabbedPanels.css (.TabbedPanelsTab selector lines 67-83, and .TabbedPanelsTabSelected selector lines 105-108), you'll note that there are only two changes, moving from "top:1px;" to "top:-1px" for .TabbedPanelsTab and moving from "border-bottom: 1px solid #EEE;" to "border-top: 1px solid #EEE;". These changes will allow the bottom tab to overlap the bottom of the content making it look seamless for the "selected" tab.

So two down, one to go. The next change is in the JavaScript to allow the Spry Tabbed Panales widget to recognize the reordered tabs and content panels. As with the CSS, I'm putting this code into the page itself rather than modify the Spry files. Add the following to your page (pulled from SpryTabbedPanels.js lines 124-133 and lines 144-153):
<script type="text/javascript">
<!--
Spry.Widget.TabbedPanels.prototype.getTabGroup = function()
{
if (this.element)
{
var children = this.getElementChildren(this.element);
if (children.length)
return children[1];
}
return null;
};

Spry.Widget.TabbedPanels.prototype.getContentPanelGroup = function()
{
if (this.element)
{
var children = this.getElementChildren(this.element);
if (children.length > 1)
return children[0];
}
return null;
};
-->
</script>

The only changes here are moving from "return children[0];" to "return children[1];" in Spry.Widget.TabbedPanels.prototype.getTabGroup.

Both changes reflect the change in order of the tabs and the content panels within the Tabbed panel wrapper div (<div id="TabbedPanels1" class="TabbedPanels"></div>).

Save and preview your page and viola, bottom tabs on your Spry Tabbed Panels.

A couple of things to keep in mind. Dreamweaver won't properly display your tabs and content panels in design view, so you'll lose a bit there, and if your content panels are of differing heights, sat tab 1 content is 200 pixels high and tab 2 is 50 pixels high, then switching tabs will cause the bottom of the tabbed panels to move up and down (taking with them the tabs too).

The former issue can't be worked around without mucking about a good int in Dreamweaver's internals, the second probably can be worked around by setting specific heights for the content areas and then maybe setting an overflow:scroll to allow for content that is too tall to the height you set. I haven't tested that type of change, but should be something like that.

Labels:

Tuesday, June 17, 2008

Upping Your Transition Count

No, I'm not becoming all health conscious, just looking at adding more transitions to those already present withing Adobe Spry's effects library.

Spry includes a few transitions which you can check out in the Effects Transitions Sampler, such as Linear, Sinusoidal, and Pulsate. For a complete list of build in transitions, check out Working with Spry Effects, and scroll down to the "Transition overview" section.

Transitions are within the Spry.Effect.Transitions namespace object located in SpryEffects.js within the Includes folder in the Spry download package. Once that file is open, you'll see the supported effects defined as functions attached to the Spry.Effect.Transitions object. This functions are commonly referred to as easing equations, and are in a specific format, so unless you're familiar with the math involved in creating an easing equation { f(0)=0 and f(1)=1 }, then I suggest you do what I've lazily done and look for pre-exisiting easing equations to play around with. And for that, we have Robert Penner's to work with, or at least start from. So you could take the equations you find at Penner's site and then modify them for JavaScript, but handily, a number of folks have already done that, and one such example is at YUI.

Now, the YUI code has the equations in a YAHOO.util.Easing name space, so you don't want to copy that out, so near the beginning of the code sample drop:

YAHOO.util.Easing = {

and at the end, drop:


};


So if you grab all of the code that lies between the two snippets listed above, and then you paste it into SpryEffects,js between lines 81 and 82, then you'll have the listed easing equations available for use in transitions for your effects and widgets. Important: give credit where credit is due, make sure to copy the copyright info for the easing equations along with the equations themselves! As the equations and Spry are both BSD licenses, they mix together well, but pay attention when mixing open and closed source code.

Please note that by default, all of the transitions are added to the Spry object directly (As of Spry 1.6.1 that is done in SpryEffects.js lines 83-86), so if you write your own equation, or use someone else's, please make sure to check the name of the function and ensure that there's no naming conflict because of the potential for overwriting another Spry object's, or for your transition to be overwritten by some other code. For example, naming an easing function Effect, would cause your function to be assigned to Spry.Effect, which would cause an issue for you (or anyone else) that needs the default Spry.Effect object.

So if everything has gone correctly, you can now enjoy 16 new transitions along with the original 8 that are part of Spry 1.6.1.

If you have run across any other transitions or effect tidbits, trivia, or interesting findings for Spry, or that you feel might apply to Spry, please leave a comment so we all can press forward!

Tuesday, June 3, 2008

Nested Spry Collapsible Panels

I ran into a project where I wanted to be able to use nested Spry collapsible panels in Spry 1.6.1 but I saw that when I expanded the child panels that they expanded over the border of the parent panel. I'll try to get a sample up soon to show how it didn't work correctly for me. But in the mean time, you can fix it by opening up SpryCollapsiblePanel.js (within a SpryAssets folder is you added the panel via Dreamweaver) and go to line 431, which reads:
this.content.style.height = this.toHeight + "px";

Change that to:
this.content.style.height = "auto";

And you should be good to go. The first fix I had tried set the height to an empty string which worked for any panel that doesn't have a height set in the CSS for the content. If you do have a height set in the CSS for the content, then setting the height to an empty string causes the CSS to take effect and if the CSS height is different than the height that Spry determined the content of the panel to be when the panel opens, then you could get some jumping as the animation finishes and then the height adjusts to the value set in the CSS.

Labels: