//parameters
var IL_REPEAT_DELAY     = 3000; //milliseconds
var IL_SAFE_RANGE       = 30;
var IL_WARNING          = "You have chosen more than " + IL_SAFE_RANGE + " frames \
to play. This may take several minutes to load and significant amount of memory \
(512 MB recommended), otherwise your browser may freeze. Are you sure you want to proceed?";

var IL_LOAD_TIMEOUT     = 1000; //milliseconds
var IL_PROGRESS_TICKS   = 10; //number of ticks in progress bar


//internal structure to store all the image loops on the page
var IMAGE_LOOP = new Array();
//handle of image loop accepting keyboard input
var IL_H = 0;

//factory
function image_loop(folder_url, still_url, tag) {
    IMAGE_LOOP[IMAGE_LOOP.length] = new _image_loop(
        IMAGE_LOOP.length, folder_url, still_url, tag);
}

//constant
var IL_HTML_ELEMENTS    = 21; //count of HTML elements needed for this script

//global handler for pressed keys, preserving already existing
var il_document_onkeypress = null;
if (document.onkeypress)
    il_document_onkeypress = document.onkeypress;

document.onkeypress = function (ev) {
    if (il_document_onkeypress)
        il_document_onkeypress(ev);

    var self = IMAGE_LOOP[IL_H];
    var keynum;
    var keychar;

    ev = ev || window.event;
    if(window.event) keynum = ev.keyCode;
    else if(ev.which) keynum = ev.which;

    if (ev.altKey || ev.ctrlKey || ev.metaKey) return;

    keychar = String.fromCharCode(keynum);
    switch (keychar) {
        case 's': case 'S':
            self.do_start.onclick(); break;
        case 'c': case 'C':
            if (!self.do_pause.disabled && self.do_pause.value == "Cancel")
                self.do_pause.onclick();
            break;
        case 'p': case 'P':
            if (!self.do_pause.disabled && self.do_pause.value == "Pause")
                self.do_pause.onclick();
            break;
        case 'r': case 'R':
            if (!self.do_pause.disabled && self.do_pause.value == "Resume")
                self.do_pause.onclick();
            break;
        case 'w': case 'W':
            self.do_rewind.onclick(); break;
        case 'f': case 'F':
            self.do_forward.onclick(); break;
        case 'b': case 'B':
            self.do_back.onclick(); break;
        case '1': case '2': case '3':
        case '4': case '5': case '6':
        case '7': case '8': case '9':
            if (!IMAGE_LOOP[parseInt(keychar) - 1]) break;

            var s = new String();

            s = self.trace.innerHTML;
            s = s.replace(/\<B\>/gi, "");
            s = s.replace(/\<\/B\>/gi, "");
            self.trace.innerHTML = s;

            IL_H = parseInt(keychar) - 1;
            self = IMAGE_LOOP[IL_H];

            s = self.trace.innerHTML;
            s = s.replace(/\<B\>/gi, "");
            s = s.replace(/\<\/B\>/gi, "");
            s = s.replace(/\[/gi, "<B>[");
            s = s.replace(/\]/gi, "]</B>");
            self.trace.innerHTML = s;

            s = s.replace(/\<B\>/gi, "");
            s = s.replace(/\<\/B\>/gi, "");
            window.status = s;

            break;
    }
}
//----------------------------------------------------------------------

//constructor
function _image_loop(handle, folder_url, still_url, tag) {
    this.handle = handle;

    this.folder_url = folder_url;
    if (!folder_url) this.folder_url = "images";

    this.still_url = still_url;

    this.tag = tag;
    this.root = null;
    if (!tag) {
        var nodes = document.body.childNodes;
        for (i = 0; i < nodes.length; i++) {
            if (nodes[i].className == "image_loop") {
                this.root = nodes[i];
                break;
            }
        }
        this.trace_tag = "";
    } else {
        this.root = document.getElementById(tag);
        this.trace_tag = "[" + (this.handle + 1) + "] ";
    }

    var elements = 0;
    var groups = this.root.childNodes;
    for (i = 0; i < groups.length; i++) {
        if (groups[i].className == "controls") {
            var controls = groups[i].childNodes;
            for (j = 0; j < controls.length; j++) {
                if (controls[j].className == "from_month")
                    this.from_month = controls[j];
                else if (controls[j].className == "from_day")
                    this.from_day = controls[j];
                else if (controls[j].className == "to_month")
                    this.to_month = controls[j];
                else if (controls[j].className == "to_day")
                    this.to_day = controls[j];
                else if (controls[j].className == "start")
                    this.do_start = controls[j];
                else if (controls[j].className == "pause")
                    this.do_pause = controls[j];
                else if (controls[j].className == "forward")
                    this.do_forward = controls[j];
                else if (controls[j].className == "back")
                    this.do_back = controls[j];
                else if (controls[j].className == "rewind")
                    this.do_rewind = controls[j];
                else if (controls[j].className == "each")
                    this.do_each = controls[j];
                else if (controls[j].className == "fps")
                    this.do_fps = controls[j];
                else if (controls[j].className == "zoom")
                    this.do_zoom = controls[j];
                else if (controls[j].className == "still_show")
                    this.still_show = controls[j];
                else if (controls[j].className == "still_month")
                    this.still_month = controls[j];
                else if (controls[j].className == "still_day")
                    this.still_day = controls[j];
                else if (controls[j].className == "trace")
                    this.trace = controls[j];
                else if (controls[j].className == "status")
                    this.status = controls[j];
                else continue;

                elements++;
            }

            if (this.status) {
                var details = this.status.childNodes;
                for (j = 0; j < details.length; j++) {
                    if (details[j].className == "progress")
                        this.progress = details[j];
                    else if (details[j].className == "progressbar")
                        this.progressbar = details[j];
                    else continue;
    
                    elements++;
                }
            }
        }
        else if (groups[i].className == "watch") {
            this.watch = groups[i];
            var details = groups[i].childNodes;
            for (j = 0; j < details.length; j++) {
                if (details[j].className == "still")
                    this.still = details[j];
                else if (details[j].className == "movie")
                    this.movie = details[j];
                else continue;

                elements++;
            }
        }
    }
    if (elements != IL_HTML_ELEMENTS) {
        alert("ERROR! Image looping can't start due to" +
            " some tags missing in HTML.");
        return null;
    }

    this.add_needs_null = true;
    this.watch_set      = false;

    this.fps            = 1;
    this.zoom           = 1;

    this.frame_id       = 0;
    this.frame_from     = 0;
    this.frame_to       = 0;
    this.frame_min      = 0;
    this.frame_max      = 0;
    this.frame_still    = 0;
    this.direction      = 1;
    this.each           = 1;

    this.frame_count    = 0;
    this.start          = null;
    this.tanim          = null;
    this.tload          = null;
    this.do_step        = 0;
    this.pause_state    = "Pause";
    this.loaded         = 0;
    this.all            = 0;

    this.names          = new Array();
    this.months         = new Array();
    this.days           = new Array();
    this.pictures       = new Array();
    this.dates          = new Array();

//functions
    this.list           = il_list;
    this.hook           = il_hook;
    this.range          = il_range;
    this.setup_titles   = il_setup_titles;
    this.setup_dates    = il_setup_dates;
    this.setup_months   = il_setup_months;
    this.sync_days      = il_sync_days;
    this.sync_buttons   = il_sync_buttons;
    this.which_frame    = il_which_frame;
    this.update_trace   = il_update_trace;
    this.highlight      = il_highlight;
    this.cancel_loading = il_cancel_loading;
    this.update_status  = il_update_status;

    this.already_warned = false;

//get folder contents
    var request = null;
    if (window.XMLHttpRequest)
        request = new XMLHttpRequest();
    else if (window.ActiveXObject)
        request = new ActiveXObject("Microsoft.XMLHTTP");

    if (request == null) {
        this.trace.innerHTML = this.highlight(true) +
            "ERROR! Image loop doesn't work in this browser.";
        if (this.handle == IL_H) {
            window.status = this.highlight(false) +
                "ERROR! Image loop doesn't work in this browser.";
        }
        return null;
    }    

    request.onreadystatechange = function() {
        switch (request.readyState) {
        case 4:
            var self = IMAGE_LOOP[handle];
            switch (request.status) {
            case 200:
                var res = self.list(request.responseText);
                if (res == false) {
                    self.trace.innerHTML = self.highlight(true) +
                        "ERROR! There are no images to loop.";
                    if (self.handle == IL_H) {
                        window.status = self.highlight(false) +
                            "ERROR! There are no images to loop.";
                    }
                    return null;
                }
                self.hook();
                break;
            default:
                self.trace.innerHTML = self.highlight(true) +
                    "ERROR! Can't get image list.";
                if (self.handle == IL_H) {
                    window.status = self.highlight(false) +
                        "ERROR! Can't get image list.";
                }
                break;
            }
            break;
        }
    };
    request.open("GET", this.folder_url, true);
    request.send(null);
}

//parse folder contents
function il_list(s) {
    var m = s.match(/\"\>\s*\d\d\d\d\.jpg\<\/A\>|\"\>\s*\d\d\d\d\.gif\<\/A\>|\"\>\s*\d\d\d\d\.png\<\/A\>/gi);
    if (m == null) return false;

    for (i = 0; i < m.length; i++) {
        this.names[i] = m[i].match(/\d+\.\w+/gi);
    }

    return true;
}

//connect HTML tags to JavaScript code
function il_hook() {
    var h = this.handle;

//check compatibility
    var o = document.createElement('option');
    try {
        this.add_needs_null = true;
        this.do_fps.add(o, null);
        this.do_fps.remove(0);
    } catch(ex) {
        this.add_needs_null = false;
    }

//fps
    this.setup_titles(this.do_fps, Array(
        "1/3 fps", "1/2 fps",
        "1 fps", "2 fps", "3 fps", "5 fps", "10 fps", "20 fps", "30 fps"
    ));
    this.do_fps.onchange = function () {
        var self = IMAGE_LOOP[h];

        switch (self.do_fps.selectedIndex) {
        case 0: self.fps = 1/3; break;
        case 1: self.fps = 1/2; break;
        case 2: self.fps = 1; break;
        case 3: self.fps = 2; break;
        case 4: self.fps = 3; break;
        case 5: self.fps = 5; break;
        case 6: self.fps = 10; break;
        case 7: self.fps = 20; break;
        case 8: self.fps = 30; break;
        }

        if (self.tanim) {
            clearTimeout(self.tanim); self.tanim = null;
            self.frame_count = 0;
            self.start = new Date();
            self.tanim = il_animate(h);
        }
    }
    this.do_fps.selectedIndex = 2;

//zoom
    this.setup_titles(this.do_zoom, Array(
        ".25x", ".50x", ".75x", "1x", "1.5x", "2x", "4x"
    ));
    this.do_zoom.onchange = function () {
        var self = IMAGE_LOOP[h];

        switch (self.do_zoom.selectedIndex) {
        case 0: self.zoom = 0.25; break;
        case 1: self.zoom = 0.50; break;
        case 2: self.zoom = 0.75; break;
        case 3: self.zoom = 1.00; break;
        case 4: self.zoom = 1.50; break;
        case 5: self.zoom = 2.00; break;
        case 6: self.zoom = 4.00; break;
        }

        var height = new String(self.watch.style.height);
        var width = new String(self.watch.style.width);

        height = height.replace(/px/gi, "");
        width = width.replace(/px/gi, "");

        self.movie.style.height = height * self.zoom;
        self.movie.style.width = width * self.zoom ;
        self.movie.style.top = (height - self.movie.height) / 2;
        self.movie.style.left = (width - self.movie.width) / 2;

        if (self.still_show.selectedIndex == 0) return;

        var still_img = document.getElementById(self.tag + "_still_image");
        still_img.style.height = self.movie.style.height;
        still_img.style.width = self.movie.style.width;
        self.still.style.top = self.movie.style.top;
        self.still.style.left = self.movie.style.left;
    }
    this.do_zoom.selectedIndex = 3;

//each
    this.setup_titles(this.do_each, Array(
        "Each frame", "Each second frame", "Each third frame"
    ));
    this.do_each.onchange = function () {
        var self = IMAGE_LOOP[h];

        switch (self.do_each.selectedIndex) {
        case 0: self.each = 1; break;
        case 1: self.each = 2; break;
        case 2: self.each = 3; break;
        }
    }
    this.do_each.selectedIndex = 0;

//still
    var still_titles = new Array ("No still image", "Still frame");
    if (this.still_url) still_titles[2] = "Still image";
    this.setup_titles(this.still_show, still_titles);

    this.still_show.onchange = function() {
        var self = IMAGE_LOOP[h];

        self.frame_still = self.which_frame(
            self.still_month.selectedIndex,
            self.still_day.selectedIndex);

        switch (self.still_show.selectedIndex) {
        case 0:
            self.still.innerHTML        = "";
            self.still.style.opacity    = 0;
            self.still.style.filter     = "progid:DXImageTransform.Microsoft.Alpha(opacity=0)";
            self.still.style.MozOpacity = 0;
            self.still_month.disabled   = true;
            self.still_day.disabled     = true;
            break;
        case 1:
            self.still.innerHTML =
                "<IMG ID=\"" + self.tag + "_still_image\"" +
                " SRC=\"" + self.folder_url + "/" +
                self.names[self.frame_still] +
                "\" STYLE=\"z-index: 1;\"></IMG>";
            self.still.style.opacity    = 0.5;
            self.still.style.filter     = "progid:DXImageTransform.Microsoft.Alpha(opacity=50)";
            self.still.style.MozOpacity = 0.5;
            self.still_month.disabled   = false;
            self.still_day.disabled     = false;

            var still_img =
                document.getElementById(self.tag + "_still_image");
            still_img.style.height      = self.movie.style.height;
            still_img.style.width       = self.movie.style.width;
            self.still.style.top        = self.movie.style.top;
            self.still.style.left       = self.movie.style.left;

            break;
        case 2:
            if (!self.still_url) break;

            self.still.innerHTML =
                "<IMG ID=\"" + self.tag + "_still_image\"" +
                " SRC=\"" + self.still_url +
                "\" STYLE=\"z-index: 1;\"></IMG>";
            self.still.style.opacity    = 0.5;
            self.still.style.filter     = "progid:DXImageTransform.Microsoft.Alpha(opacity=50)";
            self.still.style.MozOpacity = 0.5;
            self.still_month.disabled   = true;
            self.still_day.disabled     = true;

            var still_img =
                document.getElementById(self.tag + "_still_image");
            still_img.style.height      = self.movie.style.height;
            still_img.style.width       = self.movie.style.width;
            self.still.style.top        = self.movie.style.top;
            self.still.style.left       = self.movie.style.left;

            break;
        }
    }

//dates
    this.setup_dates();

    this.from_month.selectedIndex = 0;
    this.from_month.onchange = function() {
        var self = IMAGE_LOOP[h];
        self.sync_days(self.from_month, self.from_day);
    }
    this.from_month.onchange();
    this.from_day.selectedIndex = 0;

    this.to_month.selectedIndex = this.to_month.length - 1;
    this.to_month.onchange = function() {
        var self = IMAGE_LOOP[h];
        self.sync_days(self.to_month, self.to_day);
    }
    this.to_month.onchange();
    this.to_day.selectedIndex = this.to_day.length - 1;

    this.still_month.selectedIndex = 0;
    this.still_month.onchange = function() {
        var self = IMAGE_LOOP[h];
        self.sync_days(self.still_month, self.still_day);
        self.still_show.onchange();
    }
    this.still_month.onchange();
    this.still_day.selectedIndex = 0;

    this.still_day.onchange = function() {
        var self = IMAGE_LOOP[h];
        self.still_show.onchange();
    }

//buttons
    this.do_start.onclick = function() {
        var self = IMAGE_LOOP[h];

        self.cancel_loading();
        self.sync_buttons(true, true, "Pause");
        if (self.range(0, true))
            il_load(h);
        else {
            self.cancel_loading();
            self.sync_buttons(false, false, "Pause");
        }
    }

    this.do_pause.onclick = function() {
        var self = IMAGE_LOOP[h];

        self.cancel_loading();
        if (self.do_pause.value == "Cancel") {
            self.sync_buttons(false, false, "Pause");
            self.do_pause.value = "Pause";
            if (!self.do_step) self.frame_id = self.frame_from;
            self.update_trace();
        } else if (self.do_pause.value == "Pause") {
            self.sync_buttons(false, true, "Resume");
            self.do_pause.value = "Resume";
        } else if (self.do_pause.value == "Resume") {
            self.sync_buttons(true, true, "Pause");
            if (self.range(0, false))
                il_load(h);
            else {
                self.cancel_loading();
                self.sync_buttons(false, false, "Pause");
            }
        }
    }

    this.do_forward.onclick = function() {
        var self = IMAGE_LOOP[h];

        self.cancel_loading();
        self.sync_buttons(false, true, "Resume");
        if (self.range(1, false))
            il_load(h);
        else {
            self.cancel_loading();
            self.sync_buttons(false, true, "Resume");
        }
    }

    this.do_back.onclick = function() {
        var self = IMAGE_LOOP[h];

        self.cancel_loading();
        self.sync_buttons(false, true, "Resume");
        if (self.range(-1, false))
            il_load(h);
        else {
            self.cancel_loading();
            self.sync_buttons(false, true, "Resume");
        }
    }

    this.do_rewind.onclick = function() {
        var self = IMAGE_LOOP[h];

        self.cancel_loading();
        self.sync_buttons(false, false, "Pause");
        self.range(1, true);
        il_load(h);
    }

//watch
    this.watch.onmousedown = function(ev) {
        var self = IMAGE_LOOP[h];

        var pos = il_get_mouse_offset(self.watch, ev);
        var height = self.movie.height / self.zoom;
        var width = self.movie.width / self.zoom;

        if (self.zoom < 1) {
            self.movie.style.top = pos.y - self.movie.height / 2;
            self.movie.style.left = pos.x - self.movie.width / 2;

            var t = new String(self.movie.style.top);
            var l = new String(self.movie.style.left);
            t = t.replace(/px/gi, "");
            l = l.replace(/px/gi, "");

            if (t < 0) self.movie.style.top = 0;
            if (l < 0) self.movie.style.left = 0;
            if (t >= height - self.movie.height)
                self.movie.style.top = height - self.movie.height;
            if (l >= width - self.movie.width)
                self.movie.style.left = width - self.movie.width;

            self.still.style.top = self.movie.style.top;
            self.still.style.left = self.movie.style.left;
        }
        if (self.zoom > 1) {
            var t = new String(self.movie.style.top);
            var l = new String(self.movie.style.left);
            t = t.replace(/px/gi, "");
            l = l.replace(/px/gi, "");

            var dy = parseInt(t) + self.movie.height / 2 - pos.y;
            var dx = parseInt(l) + self.movie.width / 2 - pos.x;
            var y = (height - self.movie.height) / 2 + dy;
            var x = (width - self.movie.width) / 2 + dx;

            if (y > 0) y = 0;
            if (x > 0) x = 0;
            if (y + self.movie.height <= height)
                y = height - self.movie.height;
            if (x + self.movie.width <= width)
                x = width - self.movie.width;

            self.movie.style.top = y;
            self.movie.style.left = x;
            self.still.style.top = self.movie.style.top;
            self.still.style.left = self.movie.style.left;
        }
    }

//startup
    for (i = 0; i < this.names.length; i++) this.pictures[i] = new Image();
    this.do_rewind.onclick();
}

//determine what to play
function il_range(do_step, do_rewind) {
    this.do_step = do_step;
    this.do_pause.value = "Cancel";

    this.frame_from = this.which_frame(
        this.from_month.selectedIndex,
        this.from_day.selectedIndex);

    this.frame_to = this.which_frame(
        this.to_month.selectedIndex,
        this.to_day.selectedIndex);

    if (this.frame_from <= this.frame_to) this.direction = 1;
    else this.direction = -1;

    if (do_rewind)
        this.frame_id = this.frame_from - this.direction * this.each;

    if (this.frame_from <= this.frame_to) {
        this.frame_min = this.frame_from;
        this.frame_max = this.frame_to;
    } else {
        this.frame_min = this.frame_to;
        this.frame_max = this.frame_from;
    }

    if (do_step) {
        var id = this.frame_id +
            this.direction * this.each * this.do_step;
        if (this.direction > 0) {
            if (id > this.frame_to) id = this.frame_from;
            if (id < this.frame_from) id = this.frame_to;
        } else {
            if (id < this.frame_to) id = this.frame_from;
            if (id > this.frame_from) id = this.frame_to;
        }
        this.frame_min = id;
        this.frame_max = id;
    }

    this.loaded = 0;
    this.all = 0;
    if (this.direction > 0) {
        for (i = this.frame_min; i <= this.frame_max; i += this.each) {
            this.all++;
        }
    } else {
        for (i = this.frame_max; i >= this.frame_min; i -= this.each) {
            this.all++;
        }
    }

    if (!this.already_warned && this.all > IL_SAFE_RANGE) {
        if (confirm(IL_WARNING))
            this.already_warned = true;
        else
            return false;
    }

    if (this.direction > 0) {
        for (i = this.frame_min; i <= this.frame_max; i += this.each) {
            this.pictures[i].src = this.folder_url + "/" + this.names[i];
            if (this.pictures[i].complete) this.loaded++;
        }
    } else {
        for (i = this.frame_max; i >= this.frame_min; i -= this.each) {
            this.pictures[i].src = this.folder_url + "/" + this.names[i];
            if (this.pictures[i].complete) this.loaded++;
        }
    }

    this.start = new Date();
    if (this.loaded < this.all && !this.do_step)
        this.status.style.visibility = "visible";

    return true;
}

//setup select box with options
function il_setup_titles(select, titles) {
    for (i = 0; i < titles.length; i++) {
        var o = document.createElement('option');
        o.text = titles[i];
        if (this.add_needs_null) select.add(o, null);
        else select.add(o);
    }
}

//convert file names to names and days
function il_setup_dates() {
    var month, day;
    var cur;
    var title;

    for (i = 0; i < this.names.length; i++) {
        var name = new String(this.names[i]);

        month = parseInt(name.substr(0, 2), 10);
        if (i == 0 || month != cur) {
            this.days[this.months.length] = new Array();
            switch (month) {
            case 1:     title = "January";      break;
            case 2:     title = "February";     break;
            case 3:     title = "March";        break;
            case 4:     title = "April";        break;
            case 5:     title = "May";          break;
            case 6:     title = "June";         break;
            case 7:     title = "July";         break;
            case 8:     title = "August";       break;
            case 9:     title = "September";    break;
            case 10:    title = "October";      break;
            case 11:    title = "November";     break;
            case 12:    title = "December";     break;
            default:    title = "???";          break;
            }
            this.months[this.months.length] = title;
            cur = month;
        }
        day = parseInt(name.substr(2, 2), 10);
        this.days[this.months.length - 1]
            [this.days[this.months.length - 1].length] = day;

        this.dates[this.dates.length] = title + " " + day;
    }

    this.setup_months(this.from_month);
    this.setup_months(this.to_month);
    this.setup_months(this.still_month);
}

//setup select box with months
function il_setup_months(select) {
    for (m = 0; m < this.months.length; m++) {
        var o = document.createElement('option');
        o.text = this.months[m];
        if (this.add_needs_null) select.add(o, null);
        else select.add(o);
    }
}

//sync list of days with month selected
function il_sync_days(month, day) {
    for (i = day.length - 1; i >= 0; i--) day.remove(i);

    var m = month.selectedIndex;
    for (d = 0; d < this.days[m].length; d++) {
        var o = document.createElement('option');
        o.text = this.days[m][d];
        if (this.add_needs_null) day.add(o, null);
        else day.add(o);
    }
}

//set related controls to consistent state
function il_sync_buttons(is_busy, has_pause, what_pause) {
    this.from_month.disabled    = is_busy;
    this.from_day.disabled      = is_busy;
    this.to_month.disabled      = is_busy;
    this.to_day.disabled        = is_busy;
    this.do_each.disabled       = is_busy;

    this.do_pause.disabled      = !has_pause;
    this.pause_state            = what_pause;
}

//determine frame number from month and day
function il_which_frame(sm, sd) {
    var frame = 0;

    for (m = 0; m < this.months.length; m++) {
        for (d = 0; d < this.days[m].length; d++) {
            if (m == sm && d == sd) {
                m = this.months.length;
                break;
            }
            frame++;
        }
    }

    return frame;
}

//update trace of playback
function il_update_trace() {
    var number = 1 + (this.frame_id - this.frame_from) * this.direction;
    var range = 1 + (this.frame_to - this.frame_from) * this.direction;

    this.trace.innerHTML = this.highlight(true) +
        "Frame " + number + " of " + range +
        " (" + this.dates[this.frame_id] + ")";

    if (this.handle == IL_H) {
        window.status = this.highlight(false) +
            "Frame " + number + " of " + range +
            " (" + this.dates[this.frame_id] + ")";
    }
}

//highlight image loop accepting keyboard input
function il_highlight(do_bold) {
    if (this.handle == IL_H && do_bold)
        return "<B>" + this.trace_tag + "</B>";
    else
        return this.trace_tag;
}

//cancel loading images requested by old operation
function il_cancel_loading() {
    if (this.tanim) { clearTimeout(this.tanim); this.tanim = null; }
    if (this.tload) { clearTimeout(this.tload); this.tload = null; }

    this.status.style.visibility = "hidden";

    if (this.direction > 0) {
        for (i = this.frame_min; i <= this.frame_max; i += this.each)
            if (!this.pictures[i].complete) this.pictures[i].src = "foo";
    } else {
        for (i = this.frame_max; i >= this.frame_min; i -= this.each)
            if (!this.pictures[i].complete) this.pictures[i].src = "foo";
    }
}

//update progress bar while loading
function il_update_status(completed) {
    this.trace.innerHTML = this.highlight(true) + "Loading ...";
    if (this.handle == IL_H) {
        window.status = this.highlight(false) + "Loading ...";
    }

    if (this.status.style.visibility == "hidden") return;

    var step = this.progress.width / IL_PROGRESS_TICKS;
    this.progress.style.left = step *
        parseInt(this.progress.width * (completed / this.all - 1) / step);

    var finish = new Date();
    var delta = (finish.getTime() - this.start.getTime());
    var speed = (completed - this.loaded) / delta * 1000;

    if (speed) {
        var mess = " (";

        var s = parseInt((this.all - completed) / speed);
        var m = Math.floor(s / 60);
        if (m > 0) {
            s = parseInt(s - m * 60);
            m = parseInt(m);
            mess += m + " min ";
        }
        mess += s + " sec to go)";

        this.trace.innerHTML += mess;
        if (this.handle == IL_H) window.status += mess;
    }
}
//----------------------------------------------------------------------

//load the images intended to play
function il_load(h) {
    var self = IMAGE_LOOP[h];
    if (self.tanim) { clearTimeout(self.tanim); self.tanim = null; }
    if (self.tload) { clearTimeout(self.tload); self.tload = null; }

    var progress_completed =
        self.progressbar.complete && self.progress.complete;

    var completed = 0;
    if (self.direction > 0) {
        for (i = self.frame_min; i <= self.frame_max; i += self.each)
            if (self.pictures[i].complete) completed++;
    } else {
        for (i = self.frame_max; i >= self.frame_min; i -= self.each)
            if (self.pictures[i].complete) completed++;
    }

    self.update_status(completed);
    if (completed < self.all || !progress_completed) {
        self.tload = setTimeout("il_load(" + h + ")", IL_LOAD_TIMEOUT);
        return;
    }
    self.do_pause.value = self.pause_state;
    self.status.style.visibility = "hidden";

    self.frame_count = 0;
    self.start = new Date();
    il_animate(h);
}

// delays animation before repeating
function il_repeat_delay(h) {
    var self = IMAGE_LOOP[h];

    if (!self.do_step) self.frame_id -= self.direction * self.each;
    else self.frame_id -= self.direction * self.each * self.do_step;

    self.start = new Date();
    self.frame_count = 0;

    il_animate(h);
}

//play selected range of images
function il_animate(h) {
    var self = IMAGE_LOOP[h];
    var repeat_delay = 0;

    if (!self.do_step) self.frame_id += self.direction * self.each;
    else self.frame_id += self.direction * self.each * self.do_step;
    
    if (self.direction > 0) {
        if (self.frame_id > self.frame_to) {
            self.frame_id = self.frame_from;
            if (!self.do_step) {
                self.tanim = setTimeout("il_repeat_delay(" + h + ")", IL_REPEAT_DELAY);
                return;
            }
        }
        if (self.frame_id < self.frame_from) self.frame_id = self.frame_to;
    } else {
        if (self.frame_id < self.frame_to) {
            self.frame_id = self.frame_from;
            if (!self.do_step) {
                self.tanim = setTimeout("il_repeat_delay(" + h + ")", IL_REPEAT_DELAY);
                return;
            }
        }
        if (self.frame_id > self.frame_from) self.frame_id = self.frame_to;
    }

    self.update_trace();

    self.movie.src = self.pictures[self.frame_id].src;
    if (!self.watch_set) {
        self.watch.style.height = self.movie.height;
        self.watch.style.width = self.movie.width;
        self.watch.style.position = "absolute";
        self.watch.style.overflow = "hidden";

        self.movie.style.position = "absolute";
        self.movie.style.top = 0;
        self.movie.style.left = 0;
        self.movie.style.height = self.movie.height;
        self.movie.style.width = self.movie.width;

        self.still.style.position = "absolute";
        self.still.style.background = "transparent";
        self.still.style.zIndex = 2;

        self.status.style.position = "absolute";
        self.status.style.overflow = "hidden";
        self.status.style.height = self.progressbar.height;
        self.status.style.width = self.progressbar.width;
        self.status.style.visibility = "hidden";

        self.progressbar.style.position = "absolute";
        self.progressbar.style.zIndex = 1;
        self.progress.style.position = "absolute";
        self.progress.style.zIndex = 2;
        self.progress.style.left = -self.progress.width;

        self.watch_set = true;
    }
    if (self.do_step) return;

    var finish = new Date();
    var delta = (finish.getTime() - self.start.getTime());
    var delay = ++self.frame_count * 1000 / self.fps - delta;
    if (delay < 0) delay = 0;

    self.tanim = setTimeout("il_animate(" + h + ")", delay);
}
//----------------------------------------------------------------------

//determine mouse position relative to some target
function il_get_mouse_offset(target, ev){
    ev = ev || window.event;

    var doc_pos    = il_get_position(target);
    var mouse_pos  = il_mouse_coords(ev);

    return {x: mouse_pos.x - doc_pos.x, y: mouse_pos.y - doc_pos.y};
}

//determine absolute mouse position
function il_mouse_coords(ev){
    if(ev.pageX || ev.pageY) return {x:ev.pageX, y:ev.pageY};

    return {
        x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
        y: ev.clientY + document.body.scrollTop  - document.body.clientTop
    };
}

//determine absolute target position
function il_get_position(target){
    var left = 0;
    var top  = 0;

    while (target.offsetParent){
        left    += target.offsetLeft;
        top     += target.offsetTop;
        target   = target.offsetParent;
    }

    left += target.offsetLeft;
    top  += target.offsetTop;

    return {x: left, y: top};
}

