Animator.js: JavaScript animation library

View animator.js on GitHub

france-flag-icon Version Française par Vicky Rotarova

Animator.js was a library I wrote back in 2006 to handle animation on web pages. For a time it was quite ahead of the curve: It was the first library to feature CSS morphing - the ability to smoothly transition between two styles defined as CSS classes.

The world of JavaScript has moved on a lot since then. Animator.js has now been incorporated into most of the major JavaScript frameworks, either by directly porting the code (Animator.js is released under a BSD license that allows people to do this) or by borrowing the techniques. Animator.js has served its purpose, and most people will be better served by a general purpose JavaScript framework: I recommend jQuery.

I'm keeping this page up here for historical interest, and because Animator.js may still be of interest to people who want a small, fast library that does animation and nothing else.

Thanks to Tim Stone, Kaspar Fischer, Clint Priest and other developers who contributed feedback and features.

Background and motivation

I've removed this section because it contained criticisms of other JavaScript libraries that were true in 2006, but not today. I also took the opportunity to rant on the topic of object-oriented inheritance, which was a topic I explored in greater length in the article Inheritance is Evil and Must Be Destroyed - Bernie Sumption, 2010

A better way of animating

An Animator is an object that generates a series of numbers between zero and one. Zero represents the start of the animation, one the end.

Check this out:

// This object controls the progress of the animation
ex1 = new Animator()
// the Animator's subjects define its behaviour
ex1.addSubject(updateButton);
function updateButton(value) {
    var button = document.getElementById("ex1Button");
    button.innerHTML = "Progress: " + Math.round(value * 100) + "%";
}
// now click below: each click to the button calls ex1.toggle()

The Animator above was left with the default options, here's an example that uses more configuration:

ex2 = new Animator({
    duration: 1200,
    interval: 400,
    onComplete: function() {
        document.getElementById("ex2Target").innerHTML += "Bing! ";
    }
})
ex2.addSubject(updateButton);
function updateButton(value) {
    document.getElementById("ex2Target").innerHTML += " Badda ";
}

Animating element styles

Most of the time you want to animate one or more a style properties of an element. There are essentially only three kinds of CSS value - ones that scale numerically (like 10px), ones that scale with a RGB colour value(like #RRGGBB), and ones that don't scale (like bold / italic). Animator provides three utility classes for each of these kinds of properties, and between them they can animate any CSS style.

// animate margin-left from 0 to 100 px
ex3 = new Animator().addSubject(
    new NumericalStyleSubject(
        "ex3Button",
        "margin-left",
        0,
        100));

// animate background-color from white to black
ex4 = new Animator().addSubject(
    new ColorStyleSubject(
        "ex4Button",
        "background-color",
        "#FFFFFF",
        "#F4C"));

// animating both - note how calls to addSubject() can be chained:
ex5 = new Animator()
    .addSubject(
        new NumericalStyleSubject(
            "ex5Button",
            "margin-left",
            0,
            100))
    .addSubject(
        new ColorStyleSubject(
            "ex5Button",
            "background-color",
            "#FFFFFF",
            "#F4C"))
    .addSubject(
        new DiscreteStyleSubject(
            "ex5Button", "font-weight",
            "normal",
            "bold",
            0.5));
// also, check out the last subject, which causes font-weight to
//switch from normal to bold half way through the animation

If you've ever used moofx or scriptaculous then you are probably thinking that this is quite verbose, and you'd be right. Animator has a killer feature that removes the verbosity, but before we get to that, here are a couple more things you can do:

More complex effects

What if you have a number of elements that you want to animate in the same way? In that case, pass an array of elements into the Subject's constructor. There is no way to add and remove elements from a Subject after it has been constructed - if you want to do that, use one Subject for each element and use addSubject() and removeSubject() on the Animator.

// Applying the same effect to different elements is easy
ex6 = new Animator().addSubject(
    new NumericalStyleSubject(
        ["dot1", "dot2", "dot3"],
        "margin-right",
        10,
        50));
     

In previous examples, each subject has referred to the same element. This does not have to be the case:

// applying different effects to different elements is possible
ex7 = new Animator()
    .addSubject(
        new ColorStyleSubject(
            "ex5ButtonA",
            "background-color",
            "#FF9",
            "#9F9"))
    .addSubject(
        new NumericalStyleSubject(
            "ex5ButtonB",
            "padding",
            "5px",
            "15px"));

   

Opacity gets special treatment. Since IE does not support the standard 'opacity' CSS style, NumericalStyleSubject will convert it into an appropriate filter.

ex8 = new Animator().addSubject(
    new NumericalStyleSubject(
        "ex8Button",
        "opacity",
        1,
        0.25));

Controlling the animation

When you click on a sample button in this article, you are calling toggle() on an animator object. There are a few more control functions:

  • play() plays the animation from the start to the end
  • stop() stops the animation at its current position
  • reverse() plays the animation in reverse
  • seekTo(pos) plays the animation from it's current position
  • seekFromTo(from, to)
play()
0%
stop()
reverse()
seekTo(0.5)
seekFromTo(0.25, 0.75)

The benefit of using seekTo() is that it wil avoid sudden jumps in state when called half way through an animation:

this div uses play() and reverse on mouseover and mouseout
this div uses seekTo(1) and seekTo(0) on mouseover and mouseout

Transitions

A transition is a function that takes a state (a number between 0 and 1) and returns another number between 0 and 1. This can be used to simulate acceleration, deceleration and more complex effects. You can pass in a transition to an Animator object's constructor using the cleverly named transition property.

The Animator.tx object provides a few ready made transitions:
  • Animator.tx.easeInOut is the default transition, and creates a smooth effect
  • Animator.tx.linear maintains a constant rate of animation
  • Animator.tx.easeIn starts slow, gets faster
  • Animator.tx.strongEaseIn exaggerated version of the above
  • Animator.tx.easeOut starts fast, gets slower
  • Animator.tx.strongEaseOut exaggerated version of the above
  • Animator.tx.elastic go slightly past the target point, then be drawn back
  • Animator.tx.veryElastic as above, but with an extra pass
  • Animator.tx.bouncy Hit the target point then bounce back
  • Animator.tx.veryBouncy as above, but with an extra 2 bounces

Sometimes you'll want to fine tune the above transitions. If you look at the source code where the Animator.tx object is created, you'll see that the above functions are all made by four factory functions:

Animator.makeEaseIn()

// make a transition that gradually accelerates. pass in 1 for smooth
// gravitational acceleration, higher values for an exaggerated effect
ex9 = new Animator({
    transition: Animator.makeEaseIn(3),
    duration: 1000
});
ex9.addSubject(
    new NumericalStyleSubject(
        "ex9Button",
        "margin-left",
        0,
        200));

Animator.makeElastic()

// make a transition that, like an object with momentum being
// attracted to a target point, goes past the target then returns
ex10 = new Animator({
    transition: Animator.makeElastic(3),
    duration: 2000
});
ex10.addSubject(
    new NumericalStyleSubject(
        "ex10Button",
        "margin-left",
        0,
        200));

Animator.makeBounce()

// make a transition that, like a ball falling to floor, reaches
// the target and bounces back again
ex11 = new Animator({
    transition: Animator.makeBounce(3),
    duration: 2000
});
ex11.addSubject(
    new NumericalStyleSubject(
        "ex11Button",
        "margin-left",
        0,
        200));

Animator.makeADSR()

An Attack Decay Sustain Release envelope is a technique I lifted from music production. It is very useful for animations that start and end at the same value.

// This example shows you what an ADSR envelope looks like, but is
// otherwise useless the first three arguments are the boundary
// points of the 4 phases. The last is the sustain level.
// All should be between 0 and 1.
ex12 = new Animator({
    transition: Animator.makeADSR(0.25, 0.5, 0.75, 0.5),
    duration: 2000
});
ex12.addSubject(
    new NumericalStyleSubject(
        "ex12Button",
        'margin-left',
        0,
        400));

A practical use of ADSR is to hold an animation in a certain state for a while, as in this yellow fade example

// This yellow fade is emphasised by holding it at full yellow
// for the first half of the animation
ex13= new Animator({
    transition: Animator.makeADSR(0, 0, 0.5, 1),
    duration: 1500
});
ex13.addSubject(
    new ColorStyleSubject(
        "ex13Button",
        "background-color",
        "#FFFFFF",
        "#FFFF00"));

Custom functions

Of course, you can write your own functions that do any kind of transition:

function setupEx14() {
    var wobbles = parseInt(prompt(
        "Enter a number of wobbles (try between 1 and 5)", ""));
    if (!wobbles) {
        alert("Sorry, I didn't understand that, have 2 wobbles.");
        wobbles = 2;
    }
    // do some kind of crazy trigonometric stuff. I don't even
    // know what it all means, I just went crazy with the Math
    // functions
    ex14Tx = function(pos) {
        return ((-Math.cos(pos*Math.PI*((1+(2*wobbles))*pos))/2) + 0.5);
    }
    ex14 = new Animator({
        transition: ex14Tx,
        duration: 2000
    });
    ex14.addSubject(
        new NumericalStyleSubject(
            "ex14Button",
            "margin-left",
            0,
            100));
    ex14.play();
};

 

The killer feature

I wanted to explain how Animator works under the hood before I revealed this feature.

Like I said before, it's all a bit verbose at the moment, and most of the code in the above examples is just boilerplate. What we need is some kind of language that lets us define the style that we want to animate an object towards. Oh wait a second, we've already got one: CSS. A CSS style contains all the information we need to define an animation state:

ex15 = new Animator().addSubject(
    new CSSStyleSubject(
        "ex15Button",
        "width: 10em; background-color: rgb(256, 256, 256); font-style: normal",
        "width: 40em; background-color: #F39; font-style: italic"));
// note how you can use any unit, not just 'px'.

CSSStyleSubject is a wrapper around the other three Subjects. It parses two CSS rule sets and for each property declaration, creates a NumericalStyleSubject if it looks like a number, or a ColorStyleSubject if it looks like a colour, or a DiscreteStyleSubject otherwise. DiscreteStyleSubjects are created with a threshold of 0.5, in other words the style changes from normal to italic half way through the animation.

Conveniently, you can also pass in CSS class names instead of rule sets:

ex16 = new Animator().addSubject(new CSSStyleSubject(
    "ex16Button",
    "small white",
    "big blue bendy"));
// the classes small, big, white, blue and bendy are
// defined in this page's source.

When you're creating animations from CSS classes, it's easy to lose track of what your animator object is doing. The Animator.inspect() method returns a string that describes the animator:

This is pretty good, but we can still remove some more cruft. Most of the time, the element you want to animate will already be in its initial style. If this is the case, you can omit one of the rule sets and the initial state will be inferred from the elements current style. This uses getComputedStyle (or Element.currentStyle in IE) so reflects the element's actual style after applying CSS rules in style sheets.

ex17 = new Animator().addSubject(
    new CSSStyleSubject(
        "ex17Button",
        "width: 300px; background-color: #F39"));

Finally, there is one last bit of syntactic sugar to make it easy to apply effects. The Animator.apply(element, style, options) function is a wrapper around creating a single CSSStyleSubject. The second argument is the style to fade to and the third is an optional set of constructor parameters for the Animator object. If you want to specify the full from and to styles, pass in a two item array as the second parameter.

ex18 = Animator.apply("ex18Button", "greenish"); // ta da!

Oh go on, one more feature...

By popular demand... Several people asked for an easy way to chain several animations together. AnimatorChain is an object that behaves a bit like an Animator, but wraps several other Animator objects and causes them to play in sequence.

var animators = [];
for (var i=0; i<3; i++) {
    animators[i] = Animator.apply("ex18blob-" + i), "blobEnd";
}
ex19 = new AnimatorChain(animators);
// the AnimatorChain object has toggle(), play(), reverse()
// and seekTo(state) functions just like Animator objects,
// so you can often use them where code expects an Animator

Well, that's it. Enjoy using Animator.js, and feel free to leave comments and feedback below.

150 thoughts on “Animator.js: JavaScript animation library”

  1. Looking forward for the release without dependencies. Also do you have the license or terms of use for corporate / professional websites?

  2. Hi Tahir,

    Currently it is Creative Commons Attribution 2.0, but I’ll be moving to BSD at the next release.

    Both of these licenses mean that you can use it as you see fit, as long as you leave a note in the source code saying where it comes from.

  3. As far as I can tell from skimming the Behaviour docs, that library works by applying user-defined functions as event handlers on elements. In that case, Animator will drop in nicely.

    You might want to be careful about caching Animator objects – don’t create a new Animator each time someone clicks on a link, just re-use the one you create the first time.

  4. I think more people will use it if there are more examples available (like an accordion that is ready to use). Anyway, at the moment you are probably more interested in improving that thingy.

    Great stuff you are doing, appreciate it!

  5. So far, love it, and thank you for the interesting tutorial as I read through – just noting (and you can delete this comment when you read it) I saw a minor typo:

    “Sometimes you’ll want to fine tune the aboce transitions.”

    did you mean ‘above transitions’?

  6. Is there any way to queue effects with Animator? For instance, I want to have a heading that bounces a couple of times from left to right, so I have one Animator that scales text-indent from 0 – 150px, then I have another runs the reverse, 150px – 0, but with a Bounce transition. Currently I have to use the onComplete function, but this doesn’t seem very elegant. Ideas?

  7. Mike: if you have the effect you want working on a button-click, as per my examples, just move the code into an onload function:

    window.onload = function() {

    var a = Animator.apply(…);

    a.play();

    }

    You’ll need to make sure that the initial style of the img element includes “opacity:0; filter:alpha(0%)” so that you can’t see the image when the page is loading.

    Lachlan: use the onComplete option of the Animator. See the Badda Bing example in the demo article.

    And everyone: do send me links to the work you’ve done with Animator, it’s fun!

  8. I’ve been trying to use your opacity feature on a hide show effect, with no progress unfortunately. Would you have an example or idea of how I can have an object show(display=block) and then fade in as well as fade out and hide(display=none).

  9. You can only use Animator on CSS properties that scale. In other words, ‘display’ can’t be animated because what comes half way between ‘block’ and ‘none’? Fortunately, you can use ‘opacity’ instead, see the fading example in the article.

  10. bernie,

    Wow. When I found Animator.js, and saw just how simple and robust it was, I started giggling like an idiot. It is GREAT, and I thank you very much for providing such an excellent script. I did notice a few bugs, though.

    1. Animator.js thinks negative numeric values are invalid in CSSStyleSubjects.

    The solution is to add “-?” to CSSStyleSubject.numericalRe, so that it becomes /^-?d+(%|[a-zA-Z]{2})?$/.

    2. Non-integer-value opacity does not work.

    I haven’t looked to figure out what’s causing this failure yet since it doesn’t affect what I’m working on at this moment… :)

    3. ‘border-width’, ‘padding’, ‘margin’ properties do not work in CSSStyleSubject (but border-left-width, padding-left, margin-left, etc. do).

    I haven’t spent time to figure this one out yet, either, since I can reasonably work-around this problem for now.

    Also, a feature request:

    1. An easy way to chain animations (that functions as expected with toggle(), and doesn’t require the order of execution to be manually reversed). As part of this, an option to execute the chained animations with a ± offset from the end of the previous animation would be useful.

    Also, in response to Comment #14, something that could be implemented for non-numeric properties such as display would be to provide an option:

    1. Ignore these types of non-numeric properties (default)

    2. Switch from the first value to the second value at x, where x is either the percentage of the animation complete or the step number.

    Cheers,

  11. Thanks for the feedback Mr Snover, I’ve put your suggestions into the new version with one exception – I can’t replicate the bug in point 3. For example, padding works fine for me. If you let me know the browser you were using and send a code sample, I’ll track down the bug and fix it.

  12. Nice library just one slight problem on Opera… whenever a box goes left after an effect it leaves a nasty top border. Example ex16 however the problem does not appear.

    Just thought I would let you know and I look forward to using your script.

  13. Hey wow, that was quick. Thanks, mate. :)

    I’m working on my real site, which you can preview at http://zetafleet.com/new/. It’s only working 100% in Firefox right now, but works most of the way in Opera and Safari WebKit and even IE7.

    I’m probably doing some stuff I shouldn’t be, like accessing this.subjects[0].subjects[0].els[0] on onComplete as an easy way to remove elements from the document when their animation is done without having to store extra references to them. (By the way, this does not work in IE. The second subjects array is blank. Any thoughts why? Safari also seems to have problems with this.) I’ll probably change this method to something more reliable down the line, but I was lazy and it worked for what I needed at the time… :)

    Cheers,

  14. Arg. Forgot something. Or things.

    The padding problem I was having (http://www.berniecode.com/blog/2006/11/05/moving-animatorjs-forwards/#comment-29) occurred during development of the site linked in my previous post. When I set window.DEBUG = true, I received alerts about invalid value ” for padding, border, etc.

    Also, if you look at the Animator.js, I had to hack a fix in for calculating width and height of elements because for some reason Opera (9.02) was returning 0px. I don’t know how reproducible that problem is, and it’s probably simply an Opera bug. I’ll start working with the new version and see if it’s fixed.

    Cheers,

  15. ste – This sounds like a similar problem to one that I got in Firefox, in which case it’s actually a browser rendering bug. If you can’t get the problem to go away by using a different CSS property then as a last resort you can put an onStep action that removes and re-inserts the element from the DOM on each frame, to force the browser to completely re-render the element. See the fireFoxRenderingBugFix function on the source of my home page for an example. Strangely, IE seems to have the fewest rendering bugs on animation.

  16. Colin – congratulations, I think that’s the coolest example of Animator.js usage I’ve seen!

    As for the width problem, it might be an Opera bug but another possible reason for this would be if the element you’re animating does not have a specific width applied with CSS, for example if you specify the width and height of an image with html attributes instead of CSS.

    That’s not an excuse however – it should work anyway. I’ll add your fix to the next version.

  17. Hey bernie,

    Thanks for the kind words. I’ve had to discard the throbbers over the images for now in order to prevent myself from being driven completely crazy. I’m pretty sure it’s an Opera bug since the element being tested does have a width and height specified in CSS. Fortunately, offsetWidth/offsetHeight are supported by everything in the world now even though they aren’t part of the formal DOM, so it works out OK.

    Also: the opacity fix for MSIE is only applied to NumericStyleSubjects, not CSSStyleSubjects.

    Let me know if you want me to actually try providing fixes for problems every once and a while instead of simply reporting them to you, too. The lovely thing about Animator.js is that it’s robust enough that if one method doesn’t work there seems to always be a second that does. :)

    Cheers,

  18. HI ! Thanks for the small footprint animation js. It works killer on your site, but i am having troubles making it work for me.

    Could you post a simple instruction list of what’s needed for a minimal install? Or make your download as a zip with an html page with a sample? Your page’s source works when downloaded but when I strip out all but one ID, things dont work.

    I am getting an “el has no properties” error.

    thank you, Brent

  19. Hi brent. “el has no properties” sounds like a problem with the toggleSample function on the page, i.e. a problem with my demo page, not with Animator.

    I guess the error happens when you click one of the buttons below a sample right? You have probably removed the ID of the div that contains the source code that you are trying to run. The toggleSample function finds the div with the example source code and runs it (thereby guaranteeing that my examples don’t contain typos – very clever). If you remove the ID, you can’t run the example.

    If that’s not the problem, email me, attaching the source code – bernie @ [this website].

  20. Hey Bernie,

    I have an idea I’d like to use this for (I’m just a lowly designer and don’t understand js…) – but – I’m trying to collapse a div – in one version, it’s natural height (auto?) –> to a collapsed height with overflow: hidden. Meaning – I’m trying to hide *some* of the content, but not all (complete collapse). I’ve found that animator.js can’t interpret height: auto. Have any ideas?

    Thank you very much!

    Dave

  21. Hi David,

    Animator can only be used on properties that scale with numeric or colour values, so the trick is to find a numeric property that will produce the effect you’re looking for.

    What you should try is setting the overflow to hidden and animating the width and height of the element. If you know the size of the content then you can just animate from that to the smaller size. If you don’t know the size of the content you can find it out by setting the div to auto, then reading the offsetWidth and offsetHeight properties of the div.

    If you have trouble getting it working, put a test page online and I’ll have a look for you.

    And there’s nothing lowly about designers!

  22. Hey, Bernie.

    I’ve been looking at the library, and it looks really nice. Especially the class-to-class transformations. Not seen that in any other lib.

    I was trying to dig up your email address, but I’m either blind or it’s not on your site. And the phone number on the “hire me” page is without a country prefix. Could you perhaps update your contact info, or get in touch with me directly?

    Cheers.

  23. Hi Bernie,

    Nice piece of work :-)

    When doing some tests, I found that the CSSStyleSubject constructor doesn’t handle the fractional ‘opacity’ attribute correctly. For me, the following fix worked, but be warned – this has not been tested thoroughly.

    } else if(match = CSSStyleSubject.numericalRe.exec(fromStyle[prop])) {

    from = parseFloat(fromStyle[prop]);

    to = parseFloat(toStyle[prop]);

    Cheers!

  24. Hi Bernie,

    Hope everything’s getting on well. Just wanted to tell you that on the example page for Animator.js, the “Home” link at the top of the page is currently linking to “Documents%20and%20Settings/Bernard%20Sumption/Desktop/animator/”. Shock and horror! ;)

    Cheers,

  25. I need to know how to stop an animation before it’s finished playing, so that I can start another animation on the same element. Is this possible?

    Use case: tool tip div which fades in on mouse over and fades out on mouse out. If I move the mouse too quickly to the next hot link and start a new fade in animation before the fade out is complete, it blinks all schizo like… I know scriptalicious does this but I don’t want to go back, I like The Bernie’s animator too much!

  26. Why thank you Eric!

    What you’re looking for is the seekTo(pos) method, which causes the Animator to play towards a certain position without resetting the current position.

    el.onmouseover = function() {anim.seekTo(1);}

    el.onmouseout = function() {anim.seekTo(0);}

    I’ve updated the samples page to include some examples.

  27. Great Effects, the only thing is seem to not be possible to do a mouseover by wapping background-image from the css. Is it possible, just curious?

  28. If you mean animating from background-image:none to background-image:url(foo.jpg) then no, because you can only animate numeric or colour properties.

    However, you can probably get the effect that you want by using an image element and animating its opacity, as in the opacity example in the article.

  29. Hi Bernie, thanks for the help. Is it possible you do a simple sample with a mouse over effect with a background image. Im pretty new to javascript. thanks

  30. This in the HTML:

    <img id=foo src=foo.jpg>

    This in the script:

    var i = document.getElementById(“foo”);

    var a = Animator.apply(i, [“opacity:1″, “opacity:0″]);

    a.play();

  31. Hey dude, great script. :)

    One thing though – in the accordion function, you use innerHTML. Wouldn’t it be better to use createTextNode to make it XHTML compliant.

    Cheers.

  32. Good question.

    innerHTML is only used for the remembrance feature, where it uses the inner HTML of a header element to append to the URL in a #fragment-identifier so that the currently selected page is bookmarkable.

    It’s read and not written, so createTextNode wouldn’t work, and I was too lazy to use a recursive DOM innerText replacement algorithm since every real browser supports innerHTML.

    I’ve never really liked XHTML anyway. Mangos excite me, not trucks

  33. After fighting endless battles with scriptaculous and other homegrown specific purpose animation scripts, this script is a wonderful find. Not only is it elegant, but it is an exceptionally clean implementation. That’s rare IMO. Most scripts I find are nearly random cobblings. Not so here. I learned a couple of things just reading your animator script. Thanks!

    How about if you were to start accumulating worthy scripts that are written using animator? I have a script or two that I might be willing to donate, and we could get/give some pointers on things we could improve on.

  34. Ok, I’ve been de-cruftizing one of my animator.js usinge scripts to bring it up to Bernie standards… found a couple of bugs in the process. Here’s a patch. In general, you have to test for type of undefined in a couple of places, because the legitimate value of a numeric style could be zero, which is false in ‘if (thisValueCouldBeZero)’. The other thing I found was using [1] on the end of the numeric regex eval… that doesn’t work on FF2 (at least).

    I’m on Firefox 2.0, gotta confess I haven’t tested on Opera 9 and IE7 yet.

    354c354

    if (typeof toStyle[prop] === ‘undefined’) {

    365c365

    units = match[1] || CSSStyleSubject.numericalRe.exec(toStyle[prop]);

    399c399

    if (typeof value !== ‘undefined’) {

  35. Thanks Tim, I’ve patched the release version.

    By the way, I’m a fan of (foo != null) rather than (typeof foo == ‘undefined’), since the former tests for null and undefined at the same time.

  36. Excellent! That goes in my bag of best practices. Always kinda wondered about that. So here’s my scenario, playing out in Opera 9 and IE7: 1. create an animator using Animator.apply with a target style classname specified. 2. move the animated element by setting style.top and style.left. 3. trigger the animation. 4. in Opera 9, the animated element moves back to its original position, and animates a bit strangely (kinda sorta). In IE 7, the animation blows chunks on animator.js line 244, setting one of ‘top’, ‘left’, ‘bottom’, ‘right’ properties. Works fine in FF2. I’ve messed with it for a while, but not found a workaround other than not using a classname as the target style. The problem seems to be in the deduction of the fromStyle and toStyle properties, and in the numeric-ness of those properties (in the case of IE7). I’ve put up a test case at test.timsworlds.com/animatortest.html

  37. Cheers for the bug report Tim. I’m off to France for a week (the Dordogne, if anyone wonders) but I’ll look at the issue you raised on my return.

  38. Hey, got another little bug for ya…

    Line 201 should be var fn = … rather than fn =

    Without the var declaration, bouncy and veryBouncy transitions are identical ;)

  39. Maybe I am being a bit thick, but I can’t see a version number. If there isn’t one, then adding one would really useful. Amazing work btw.

    Thanks

    Tom

  40. Good idea, I’ll add one. I haven’t been too hot with release management on Animator, probably because it started as a little demo script and morphed into a product.

    I have also been taking the Torvalds view that only wimps use [source control] _real_ men just upload their important stuff on ftp, and let the rest of the world mirror it.

  41. Are you passing in CSS class names and using IE?

    The CSS class name handling works by applying the classes to the elements, and reading back their rendered styles. Since IE doesn’t actually support opacity, this won’t work.

    Try passing in a CSS rule instead:

    var a = Animator.apply(el, [“opacity: 1″, “opacity: 0″]);

    This passes the ‘opacity’ rule directly into NumericalStyleSubject which will convert it into a filter:alpha(X%)

  42. Awesome library. I’m a big fan of scriptaculous for some basic effects (and coming from Ruby on Rails it’s pretty easy to become a fan without doing much research into alternatives), but I have always felt like the dang thing requires just too much effort to customize anything. This library seems like a godsend to those of us who don’t want to follow the cookie-cutter formula for building rich websites. (Please don’t look at my homepage for an example of a rich site – it’s still very much Web 1.0 and probably will be for a long time.)

    The thing I find the most impressive, though, isn’t the library, but your clear desire to build a *good* library as opposed to hacking something together. I haven’t specifically looked at scriptaculous, but with an amazing number of open source projects, I find the quality of code to be so poor that I wonder how some of these people even learned to tie their shoes. It’s always a true pleasure to see open source that is written in a professional manner. And with comments, too! I may just cry, I’m so happy!

    My apologies if I seem like a jerk, but badly-written OS code ruins the usefulness of OS to such a degree that I wish some of these authors would just leave their code to themselves :P – thanks for not being one of them!

  43. Ta!

    Perhaps it’s a side-effect of code written by many people that open source projects often have hacky code. Well written JavaScript can be so beautiful (now who’s the nerd), but it seems that libraries get judged entirely by their results, ease of use and file size, not by how easy it is to read and modify their code.

    Oddly enough, the Java community is much better: the source code for Spring is so well structured and documented you could base a university Java course on it.

  44. Hey Bernie,

    I noticed that when I used opacity in IE to show a menu the fade in would work the first time but not the second. The menu would just show after the duration of the animation was reached. I think I found where the problem is.

    In the setState method in NumericalStyleSubject there is a visibility variable but from what I could tell, it wasn’t being used. I added 3 lines inside of the for loop to use the visibility variable (I changed the property check from opacity to filter) and it fixes the problem.

    var visibility = (this.property == ‘filter’ && state == 0) ? ‘hidden’ : ‘visible';

    var j=0;

    for (var i=0; i 20) return;

    }

    There may be a more efficient way to do this but for now this works.

    Love the animator, it’s the best!

  45. Looks like the for loop got munged in my above post. I’ll try once more, but if the code doesn’t show, that’s fine. I’m sure you know what I’m talking about.

    for (var i=0; i 20) return;

    }

  46. hi there, im not sure if there’s something really basic that im not understanding here, but my problem is this; i have a bulleted list of links, and im wanting to apply a hover property to each of the links within it. i can make the hover work when i have a specific id on the anchor tag within the ul li, but clearly i cant have the same id on all the links as it isnt valid, nor does it mean i can apply this behaviour to new links without adding the id to them as well. Does animator.js only work when used on a specific one-off element? i’ve tried to find answers to this, but i cant seem to find any documentation/forums…cheers.

  47. I think I see what you’re getting at.

    Although you can pass multiple elements into a single Animator, the Animator will apply the same animation to every element passed into it, therefore when you pass in an array of link elements they will all be animated in chorus.

    If you want to animate each one individually, loop over the array and construct an Animator object for every link element.

    Does that work?

  48. “If you want to animate each one individually, loop over the array and construct an Animator object for every link element.

    Does that work? ”

    im pretty rubbish at scripting, but im gonna give this a go and see if i can make it happen. cheers.

  49. i guess all i’m trying to do is this;

    http://demos.mootools.net/Fx.Styles

    which i’m able to achieve in seconds with mootools, but doing it in animator just confuses. like i said, im no scripter, but im usually pretty good at working out what things do when i’ve some source example to pick apart /break etc. i really love the way you’ve done animator, and i’m using it elsewhere on my new site, but if i could work out how to do this in animator, then i’d have no need for mootools at all.

    if there’s something major that im not getting, then please clue me in. otherwise i’ll need to use mootools for the time being. keep up the good work man, this is good stuff.

    dave.

  50. mootools includes CSS selector code that Animator does not have. If you want to easily apply an animation to many elements, use a library that provides CSS selectors. Using prototype for example:

    $$(“#someDiv a”).each(function(el) {

    var a = Animator.apply(el, [options]);

    el.onmouseover = a.seekTo.bind(a, 1);

    el.onmouseout = a.seekTo.bind(a, 0);

    });

    If you’re not sure how to traverse the DOM and build an array of elements, I suggest you save yourself a headache and use prototype as above or even just use mootools if it works for you.

    Good luck!

  51. “how to traverse the DOM and build an array of elements”

    i’m gonna make this the next thing i try and learn. seems like it’d be a useful thing to know how to do for all aspects of website development. thanks for the advice, i look forward to using animator as much as possible elsewhere on my site. like i said, it was a joy to use up until that point where i became unsure if i was able to do what i wanted. (at which point it was still a joy to use, i was just stuck ;))

    again, keep up the good work dude, this is a sweet thing you’ve made here. good luck for the future.

  52. Very cool examples, but I don’t get it. I’m trying with a minimal example like the following, but it doesn’t work (or am I doing something very wrong?)

    —-

    var ex18;

    function $(id) {

    return document.getElementById(id);

    }

    .greenish {background-color: #3F9;}

    How’s this for one line of code?

    the “saved from url” stuff is used to avoid IE to complain about page containing scripts when it’s opened locally

  53. Silly me, I had forgotten to call ex18.toggle(), so that should be it. You could remind this near the end of the document since by reaching the end of the article one may have forgotten it (add a sum-up paragraph at the end with some bullets or 1-2-3 steps something)

    congrats again,

    George

  54. btw, could I define offsets at the target CSS string? If I use “left:-10″ it goes to -10, not to offset -10 compared to current pos. I’d like to have a rectangle that moves away from the mouse by say (-10,-10) at mouseOver event

  55. if CSS syntax doesn’t support this (not sure if it does somehow alreay), you could add a “targetIsOffest” boolean param to treat the target CSS style values as offsets (for numbers and colors it should work fine to treat them as offsets instead of values, but for bold/italic not sure what it should do in that case, maybe just do as usual)

  56. Hey Bernie,

    I’m a javascript novice and more of a designer, but I would like to start using a javascript library for animations, after looking around I think I would like to use Animator because of the ability to animate based on CSS classes (I absolutely love the idea there)

    The problem is I have not been able to create even a test page that runs the animations. I get no errors but no animations either. Hopefully there is just something I have not realized I was supposed to do because of my lack of javascript knowledge.

    here is one such test page test.php

  57. If you run a script in the head section it won’t have access to the elements in the body. Move the script to the end of the body, or put it in a window.onload=function(){ /* script here */ } block.

    I hope that helps.

  58. Tanner, I see you miss var ani; before you use the ani variable, maybe it plays a role on whether the variable will persist outside the script block (has been many-many years since I read netscape’s javascript manual). Also you seem to be missing the definition of the “right” function

  59. Some interesting stuff:

    http://www.robertpenner.com/easing/

    see the “Equations visualizer” link, you select a function there and then click somewhere away from the ball for the ball to move there using the selected function

    (could implement all these maybe with animator – similar or with ADSR)

    he also has a free book chapter about it online there, plus can download the code as actionscript

    found the link at

    http://ajaxian.com/archives/animatorjs-animations-using-css

    (mentioning there that this code is also ported to MooTools/javascript)

    which discusses this page

  60. >You can get this effect by >animating ‘margin-left’ from 0 to -10, >which will shift the box relative to its >position set with ‘left’

    the thing is that at next mouseover, it will do the same effect and not do margin-left from -10 to -20 this time as I’d like

    it’s unfortunate that they don’t have “offseting” CSS values (since they’re cascading rules they should I think). I do think it would help to add this (for color offseting too)

  61. That’s just the way that Animator works – when you define an effect, it is the same each time it plays.

    However it’s easy to do what you’re describing: just create the animator inside the moveLeft() function so that a new one is created with each call. Use NumericalStyleSubject instead of a CSS string and pass in a lower number each time:

    1st call: new NumericalStyleSubject(el, “width”, 0, -10);

    2nd call: new NumericalStyleSubject(el, “width”, -10, -20);

    … etc …

    Remember to store your Animator globally (window.myAnimator = new Animator(…)) so that it does not vanish when the moveLeft() function finishes.

  62. Bernie —

    I like the ideas and samples on this website and I want to use them, but I need some simple implementation cookbook that will show a minimal setup for, say, accordion. I can’t find any kind of instructions or documentation. Am I missing them? Are they parked somewhere else? Please help; I’ve been circling your website for hours!

    Karl

  63. Best way to move an Element over a Path (2D-function)? (circle, ellise, linear, sin, cos…)

    This type of Animation is not linear (as NumericStyleSubject) and move over two coordinates (X and Y)

    Thank you

  64. A more flexible solution:

    One Transition function per Subject instead per Animation. This allows multiple Transitions on same Animation.

    for example: with images and movements…

    · an image moves linear

    · other image jumps (with elastic)

    · third moves over a ellipse

    of course this can be used to fade, resizing, marging…

  65. Jose; to your first post:

    Motion in a 2D space can be broken down into 2 separate motions, one for X and one for Y. You might need 2 different Animator objects animating the same object.

    To move an object in a circle, you’d set up 2 similar Animators, one animating ‘x’ with a sin function for easing, and one animating ‘y’ with a cosine for easing.

    If you find yourself doing this a lot, write your own Subject class to provide the motion in a single Animator.

    To your second post:

    It is indeed more flexible to have multiple easings but I only really have time to work on one open source project at a time, and currently it’s making a better version of Animator.js for Flash. You’ll either have to use multiple Animator objects, or modify Animator.js to do this (and send me the modification if you want me to merge the changes back into the main release)

  66. Love the Library thank you for publishing it. Now to my question.

    Safari on Windows:

    It doesn’t recognize margin, margin-left, etc using the CSSStyleSubject style switcher function. i.e. CSSStyleSubject( $(‘ID_Name’), “style1″, “style2″);

    What am i doing wrong or is this just a bug in safari?

  67. Hi Bernie,

    First off, awesome library! Very well designed and easy to use (largely because of the great tutorial). Anyway, I have several small tweaks that I needed so I thought I would post them as individual messages. I hope I can remember all the pieces. If it helps, I can email you my entire modified file.

    Again, thanks for the fantastic work! Job well done indeed!

    Best regards,

    Don

  68. Tweak #1 – Color names in IE

    I use color names a lot (like “white”) which are preserved verbatim in IE. To support this, I added these lines to ColorStyleSubject parseColor:

    match = ColorStyleSubject.parseColor.colorNames[string.toLowerCase()];

    if (match)

    return match;

    and this object:

    ColorStyleSubject.parseColor.colorNames = {

    aqua:”#00FFFF”, black: “#000000″, blue:”#0000FF”, fuchsia:”#FF00FF”,

    gray:”#808080″, green:”#008000″, lime:”#00FF00″, maroon:”#800000″,

    navy:”#000080″, olive:”#808000″, purple:”#800080″, red:”#FF0000″,

    silver:”#C0C0C0″, teal:”#008080″, white: “#FFFFFF”, yellow:”#FFFF00″

    };

    I chose to only support the W3C standard 16 colors (otherwise it would get a bit out of control).

  69. Tweak #2 – Set Operations on Class Names

    In my use of CSS, I often have multiple class names per element. In these cases, what I need to accomplish is to animate between the current set of class names and the set of class names after additions and/or removals of individual names. I express this as an object for style1 in the CSSStyleSubject ctor like so:

    new CSSStyleSubject(el,

    { add : [“add1″, “add2″],

    remove : [“remove1″,”remove2″] });

    This approach is just how I’ve handled class manipulations in my designs (optional arrays of names to add/remove).

    I then added these helper functions to CSSStyleSubject.prototype:

    getIndex : function (array, item)

    {

    if (array)

    for (var i = 0, n = array.length; i < n; ++i)

    if (array[i] == item)

    return i;

    return -1;

    },

    mergeClasses : function (base, mod)

    {

    var getIndex = this.getIndex;

    var c = base ? base.split(” “) : [];

    var added = [];

    for (var i = 0; i < c.length; ++i)

    {

    var item = c[i], a = getIndex(mod.add || null, item);

    if (a != -1) {

    if (added[a])

    c.splice(i–, 1); // changes c.length and i

    else

    added[a] = true;

    } else if (getIndex(mod.remove || null, item) != -1)

    c.splice(i–, 1); // changes c.length and i

    }

    for (var i = 0, n = mod.add ? mod.add.length : 0; i < n; ++i)

    if (!added[i])

    c.push(mod.add[i]);

    return c.join(” “);

    },

    The “||null” stuff was to appease Firebug’s break on all errors. I also extended the probing of style1 in the same ctor:

    if (style1.add || style1.remove) {

    this.modStyle = style1; // for Tweak #3

    fromStyle = this.mergeClasses(els[0].className,

    { add : style1.remove, remove : style1.add });

    toStyle = this.mergeClasses(fromStyle, style1);

    fromStyle = this.parseStyle(fromStyle, els[0]);

    toStyle = this.parseStyle(toStyle, els[0]);

    } else if (style2) {

    .. as before ..

  70. Tweak #3 – Removal of Element Styles

    The final tweak I made was to remove the various element style overrides when the animation reached state 0 or 1. The reason is so that I could compute other animations from the new state (the styles blocked the CSS cascade) using CSSStyleSubject. I could (and may yet) switch to a “preplan everything” mode, but that approach will hit limits as the number of states and transitions between them increases. So, here goes.

    In the CSSStyleSubject ctor, I remembered the els array:

    this.els = els = Animator.makeArray(els);

    I chose to enable this style restoration only in my Set Operations mode (see Tweak #2), which explains this line:

    this.modStyle = style1;

    from Tweak #2. Right after the “remove unchanging properties” loop, I added this:

    this.cleanupStyles = [];

    I then changed this line:

    this.subjects[this.subjects.length] = new type(els, prop, from, to, units);

    To this:

    var sub = new type(els, prop, from, to, units);

    this.subjects[this.subjects.length] = sub;

    this.cleanupStyles.push(sub.property);

    Lastly, the setState method looks like this now:

    setState: function(state) {

    var useProps = (0 < state && state < 1);

    if (this.modStyle) {

    var mod = this.modStyle;

    if (state < 1)

    mod = { add : mod.remove, remove : mod.add };

    for (var i=0; i<this.els.length; ++i) {

    var el = this.els[i];

    var ocn = el.className;

    var ncn = this.mergeClasses(ocn, mod);

    if (ocn != ncn)

    el.className = ncn;

    if (!useProps)

    for (var j=0; j<this.cleanupStyles.length; ++j) {

    var prop = this.cleanupStyles[j];

    el.style[prop] = “”; // mostly OK

    try { delete el.style[prop]; } catch (e) {} // often fails

    }

    }

    }

    if (useProps)

    for (var i=0; i<this.subjects.length; i++) {

    this.subjects[i].setState(state);

    }

    },

  71. Tweak #4 – IE vs. CSSStyleSubject

    Like others I ran into issues with IE and currentStyle popping out “auto” for width and/or height as well as not recognizing filter:alpha(opacity) as the way to read opacity. Here’s may attempt to hack around those issues (all inside of getStyle):

    CSSStyleSubject.getStyle = function(el, property){

    var style;

    if (document.defaultView && document.defaultView.getComputedStyle)

    style = document.defaultView.getComputedStyle(el, “”).getPropertyValue(property);

    if (!style) {

    var s = Animator.camelize(property);

    if (el.currentStyle) {

    // IE6 propogates opacity CSS values, but doesn’t honor them. To do

    // what IE6 does, we parse the “filter:alpha(opacity=#%)” rule…

    if (property != “opacity”)

    style = el.currentStyle[s];

    else {

    var f = el.currentStyle[“filter”];

    if (f) {

    if (!CSSStyleSubject.alphaFilterRE) // if (regex not cached)

    CSSStyleSubject.alphaFilterRE = /alpha(opacity=([0-9.]+)%?)/i;

    var m = CSSStyleSubject.alphaFilterRE.exec(f);

    if (m && m[1])

    style = (parseFloat(m[1]) / 100) + “”; // stringify

    }

    if (!style)

    style = “1”;

    }

    }

    if (!style)

    style = el.style[s];

    }

    if (style == “auto”) {

    if (property == “width”)

    style = el.offsetWidth + “px”;

    else if (property == “height”)

    style = el.offsetHeight + “px”;

    }

    return style;

    }

  72. Nope, sorry.

    I suggest you get the most basic Animator working that you can, then gradually add features until you discover the thing that is making this happen.

    Bernie :o)

  73. Bernie – wow. really some of the nicest looking effects I’ve seen. I’m a real real REAL newbie at this, and tried to get ex15 to work (or any of them in fact) but lack the know-how to get the code to execute.

    Could you please (email is good) send me a single page with all the required scripts included – (I can edit the styles myself if need be) – for ex15.

    I dont quite get WHERE/HOW to use the code (even a downright copy!)

    Just so I can get to grips on how to use these effects. I am looking forward to being able to put together a little page…..

  74. if a code sample is:

    ex15 = new Animator(…);

    Then start the animation with the code:

    ex15.toggle();

    If you want the animator to start on a button click, put the above line in an onclick=”ex15.toggle();” attribute on a button.

    Make sure that the two lines are either in a script tag AFTER the elements you are trying to animate in the page, or in an onload event.

  75. Hey bernie sorry to bother you here, but I wonder if its possible to trigger Subject1 and then, when Subject1 animation is done, trigger Subject2. thanks

  76. Yout animator.js code is wonderful for some main reasons:

    1) It does only animations, that’s exactly what everyone mostly needs now days on the web.

    No tons of lines of code to do everything like jQuery and Mootools, when we need only an easy way to animate a website.

    2) The code is readable, it does not require scientists to understand the code, normal sw engineers can read it.

    What’s up? Will you go on updating it and releasing more.

    I read in post 56 that you are currently trying to make a version fro Flash, why is this? I’m asking because another good question in now days is do we give up javascript animation library to move all to Flash? Flash seems to me still to buggy, I don’t know what u think about it.

    Thanks anyway so much for sharing such a nice clean and readable piece of code.

    Cheers!

  77. Thanks again Bernie, this little library really helped me understand animation and a lot more about javascript. Let me know if you release anything else :)

  78. Your animation library is one of the cleanest and most useful that I have encountered. I liked your example of chaining together animations.
    The feature that I need is to be able to sync several animators together and play them concurrently not chained. The TimelineMax class by greensock provides this functionality for flash but I have yet to find a JS library that has this feature. It would be nice to animate several objects with different starting time offsets and control them using one timeline.

  79. Great stuff and very understandable code.
    The only bad thing in this page for me is that pinky background that makes me narrow my eyes while read the text/code (which is very addictive as it becomes easier and more powerful). ;)
    Great job!

  80. Just wanted to thank you for the animator.js

    (It’s running on my website. Just discovered where it came from and thought I ought to say thanks.)

    Thanks.

    ps.It was incorporated into an website builder tool I used to first build my site. Have migrated to dreamweaver, figured out the js and still appreciate it’s simplicity

  81. I’m using this to animate some SVG (some SMIL’s DOM methods are missing in Webkit) and so far, the only thing I miss is the ability to pause an animation

      1. Nice, although I needed a pause/resume method, so I wrote them:

        	pause: function() {
        		if (this.intervalId) {
        			window.clearInterval(this.intervalId);
        			this.intervalId = null;
        		}
        	},
        	resume: function() {this.seekTo(1)},

        They work great for me :)

  82. Thanks heaps for this javascript library, it has all the mathematics I was looking for :)
    I ported the concept idea to Delphi (Object Pascal), minus the CSS stuff.

    I’m going to use the animation to make a smooth zoom in/out for a map in my application. It seems eye candy and animation is quite lacking in desktop programming..

  83. IE9 : opacity bug fix
    when
    css)
    xtag { opacity:1.0; -moz-opacity:1.0; -khtml-opacity:1.0; filter:alpha(opacity=100); }

    js)
    var animator = new Animator( { ‘duration':150 })
    .addSubject(new NumericalStyleSubject(obj_xtag, “opacity”, 1, 0));

    patch)
    [1st : line 45 : insert : _get_ie_ver]
    Animator.prototype = {
    _get_ie_ver:function(){
    var version = 0; // we assume a sane browser
    if (navigator.appVersion.indexOf(“MSIE”) != -1){
    // bah, IE again, lets downgrade version number
    version = parseFloat(navigator.appVersion.split(“MSIE”)[1]);
    }
    return version;
    },

    [2nd : line 258? : replace : if command]
    // original code : if (property == ‘opacity’ && window.ActiveXObject) {
    if (property == ‘opacity’ && window.ActiveXObject && this.ie_ver<9) { /* for IE9 BS */
    this.property = 'filter';

  84. Bernie, brother.
    Thanks for giving us some inspiration.

    I was wondering if anyone has or has seen a good gradient-colour-fade algorithm.
    I pose this question cos I’ve been using RaphaelJS, and fading from one colour to a gradient it’s pretty ugly…nothing like Flash+AS3.
    I’d love to find a way to help that Raphael to have that sorted; So why not asking around the Gurus first?

    cheers ears

    Otto

    1. If it’s a simple linear gradient from two colours to two different colours, then in principle, you just fade both colours at the same time.

      Animator.js gives you the primitives you need, but you’ll need to write some glue code. Create a new subject, called say GradientSubject. A subject is just an object with a setState method that takes one argument. See the docs in the download for how to add a subject to an Animator object.

      As you run the Animator, this setState method will be called with a series of numbers between 0 and 1. I haven’t executed the following code and it’s far from complete (The problem of actually generating CSS for the gradient is left to you) but it should get you started.

      function GradientSubject(els, property, aFrom, aTo, bFrom, bTo) {
          this.a = new ColourStyleSubject(null, null, aFrom, aTo);
          this.b = new ColourStyleSubject(null, null, bFrom, bTo);
      }
      GradientSubject.prototype = {
          setState: function(state) {
              var aColour = this.a.getValueForState(state);
              var bColour = this.a.getValueForState(state);
              // now assemble your gradient CSS using the above rgb tuples.
          }
      }

      Let me know if you get something working.

      Bernie :o)

  85. hi bernie, thank you for this small but really useful library
    I suggest to create a special “Subject” class to animate js properties
    for example,
    el.scrollTop
    el.scrollLeft
    thank you!

  86. Hello,

    First of all, thank you for sharing this great piece of code. It’s really useful when you’re stuck to not use jQuery or other library.
    I’m quite new to programming and I have a problem that I don’t know how to solve. Maybe I’m doing something wrong.

    So, if I have an animation that runs to the end, and after that I change from outside your code a property (like width) for that element, when the animation is seekTo(0) it doesn’t take into account the new width I set. Can you make your class take into account any changes when it is seekTo(0) or seekTo(1)?

    Thank you very much again for this great class.

  87. thanks for this awesome library. im happy to see soundmanager using this great library. thank you once again for sharing it.

    1. 255. It’s an 8 bit unsigned integer, so values above 255 are invalid. If you’re lucky, the program interpreting the number will round it down. Otherwise it might overflow to 0, or do something else wacky.

  88. hi Bernie, I think that the opacity part must be fixed in some way, because I always have problems with IE (ie9 accepts opacity and -ms-opacity also)

    the fix would be setting always every “synonym” of “opacity” independently by the browser

    1. I’m not really maintaining Animator any more, and I’m recommending jQuery as an alternative. If you fix this, send me a patch and I’ll update the distribution.

      1. jquery sucks!

        your Animator’s OOP design is excellent, and I will continue to use it for this reason…

        btw I will fix this, someday, and I will update you

        thank you :)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>