/* DotCom Interactive Core Library of functions

version:        1.7
last modified:  15/08/2009 20:38:08
latest url:     http://fragged.org/js/dci_core.js
dependencies:   mootools 1.2.1 (core+more)

copyright (c) 2008, 2009 by Dimitar Christoff <dimitar@dci.uk.net>

use and modify as you see fit, credit me if you wish

changelog:
1.7
- fixed console.log wrapper to work on Safari 4 (type error on apply)
- removed polka dots as its not used anywhere
- removed clipboard

1.6
- rewrote the element.tooltip prototype, it is now much shorter and _should_ be less dependent on
  css irregularities (weird line spacings etc).

1.5
- removed site specific domready functions and placed the in a separate file.
- cleaned up largerBox, created its own namespace and rewrote it.
- added options for largerBox, including: colours, styles, modal, shadow and tooltip use, selectors

1.4
- Added centerBox function which returns on-screen coordinates for center print of a box.
- Altered slidingTips and improved interface
- added a clipboard copy function for flash < ver 10.

*/



// general variables we set to do with tooltips and images
var delayTimer;

// compatibility with mootools 1.11

Window.implement({
    $E: function(s) {
        return this.document.getElement(s);
    }
});

// element extending functions
Element.implement({
    getText: function(){
        var tag = this.getTag();
        if (['style', 'script'].contains(tag)){
            if (window.ie){
                if (tag == 'style') return this.styleSheet.cssText;
                else if (tag ==  'script') return this.getProperty('text');
            } else {
                return this.innerHTML;
            }
        }
        return ($pick(this.innerText, this.textContent));
    },
    showProgress: function(options) {
        // javascript progress indicator. needs a background image on the prototyped element
        options = $merge({
            count: 0,
            total: 10,
            message: "",
            className: "progress",
            backgroundPosition: 200, // start pos
            tween: true // animate or static
        }, options);

        // calculate % of progress
        var percent = options.total / 100, donePercent = (options.count / percent).round(), BGoffset = options.backgroundPosition - (donePercent*2).round();
        donePercent = (isNaN(donePercent)) ? "0" : donePercent;
        // set inline message
        this.set({
            styles: {
                "background-position": "-" + options.backgroundPosition + "px"
            },
            html: "&nbsp; " + options.count + " of " + options.total + " " + options.message + " ("+donePercent+"%)",
            "class": options.className
        });

        if (options.tween)
            new Fx.Tween(this, {
                duration: 500,
                transition: Fx.Transitions.Cubic.easeIn
            }).start("background-position", "-" + BGoffset);
        else
            this.setStyle("background-position", "-" + BGoffset + "px");

        this.store("percent", donePercent);
        return this;
    },
    smartDispose: function() {
        // dispose of an element and its dropShadow (if there is one)
        var rel = this.get("data-related");
        if ($(rel)) {
            $(rel).dispose();
        }
        this.dispose();
    }, // end smartDispose
    dropShadow: function(options) {
        // creates a shadow effect to a rectangular element

        // define defaults
        var options = $merge({
            id: "dropShadow" + $random(100,1000),
            x: 3, // offset from parent
            y: 3,
            border: "1px solid #000",
            background: "#555",
            opacity: .5,
            zIndex: this.getStyle("z-index").toInt() - 1 // behind parent
        }, options);

        // only apply shadow on absolutely positioned elements
        if (this.getStyle("position") != "absolute")
            return this;

        var c = this.getCoordinates();

        new Element("div", {
            id: options.id,
            styles: {
                position: "absolute",
                left: c.left + options.x,
                top: c.top + options.y,
                width: c.width,
                height: c.height,
                background: options.background,
                zIndex: options.zIndex
            },
            opacity: 0
        }).injectBefore(this).fade(0, options.opacity);

        // store the shadow id into the element
        this.set("data-related", options.id);

        return this;
    }, // end dropShadow
    Bubble: function(message,options) {
        // open up a  bubble popup for text

        var bubbleOptions = $merge({
            y: 64,                  // offset (substracted from element top)
            x: 10,                  // offset to element left
            fadeFrom: 0,            // fade start value
            fadeUntil: .95,         // fade end
            shadowFadeFrom: 0,
            shadowFadeUntil: .5,
            directionX: 2,          // shadow offset X from bubble
            directionY: 2,          // shadow offset Y
            delayShort: 1000,
            delayLong: 5000
        }, options)

        $clear(delayTimer);
        if ($("myBubble")) {
            $("myBubble").dispose();
            $("myBubbles").dispose();
        }

        var coords  = this.getPosition();
        var bubbles = new Element("div", {
            "styles": {
                "position": "absolute",
                "top": coords.y-bubbleOptions.y+bubbleOptions.directionY,
                "left": coords.x+bubbleOptions.x+bubbleOptions.directionX
            },
            "id": "myBubbles",
            "opacity": bubbleOptions.shadowFadeFrom,
            "z-index": 100000001

        }).inject(document.body).fade(bubbleOptions.shadowFadeFrom,bubbleOptions.shadowFadeUntil);
        var bubble = new Element("div", {
            "styles": {
                "position": "absolute",
                "top": coords.y-bubbleOptions.y,
                "left": coords.x+bubbleOptions.x
            },
            "id": "myBubble",
            "opacity": bubbleOptions.fadeFrom,
            "html": message,
            "z-index": 100000002,
            "events": {
                "mouseenter": function() {
                    $clear(delayTimer);
                    this.setOpacity(1);
                },
                "mouseleave": function() {
                    this.setOpacity(bubbleOptions.fadeUntil);
                    delayTimer = (function() {
                        if (bubbleOptions.fadeFrom == bubbleOptions.fadeUntil && $("myBubble")) {
                            $("myBubble").dispose();
                            $("myBubbles").dispose();
                        }
                        else {
                            if ($("myBubble")) {
                                $("myBubble").fade(bubbleOptions.fadeFrom);
                                $("myBubbles").fade(bubbleOptions.shadowFadeFrom);
                                if ($("closeBubble"))
                                    $("closeBubble").dispose();
                                (function() {
                                    $("myBubble").dispose();
                                    $("myBubbles").dispose();
                                }).delay(500);
                            }
                        }
                    }).delay(bubbleOptions.delayShort);
                }
            }
        }).inject(document.body).fade(bubbleOptions.fadeUntil);


        new Element("img", {
            "src": imagePath + "close.gif",
            "id": "closeBubble",
            "events": {
                "mouseenter": function() {
                    this.set("opacity", 1);
                },
                "mouseleave": function() {
                    this.set("opacity", .7);
                },
                "click": function() {
                    $clear(delayTimer);
                    if (bubbleOptions.fadeFrom == bubbleOptions.fadeUntil && $("myBubble")) {
                        $("myBubble").dispose();
                        $("myBubbles").dispose();
                    }
                    else {
                        $("myBubble").fade(bubbleOptions.fadeFrom).removeEvents();
                        $("myBubbles").fade(bubbleOptions.shadowFadeFrom);
                        if ($("closeBubble"))
                           $("closeBubble").dispose();
                        (function() {
                            $("myBubble").dispose();
                            $("myBubbles").dispose();
                        }).delay(500);
                    }
                } // click
            },
            "styles": {
                "position": "absolute",
                "top": 2,
                "left": 148,
                "opacity": .7
            },
            "class": "cur"
        }).injectInside(bubble);

        delayTimer = (function() {
            if (bubbleOptions.fadeFrom == bubbleOptions.fadeUntil && $("myBubble")) {
                $("myBubble").dispose();
                $("myBubbles").dispose();
            }
            else {
                if ($("myBubble")) {
                    $("myBubble").fade(bubbleOptions.fadeFrom).removeEvents();
                    $("myBubbles").fade(bubbleOptions.shadowFadeFrom);
                    if ($("closeBubble"))
                        $("closeBubble").dispose();
                    (function() {
                        $("myBubble").dispose();
                        $("myBubbles").dispose();
                    }).delay(500);
                }
            }
        }).delay(bubbleOptions.delayLong);

        return this;
    },
    tooltip: function(message, options) {
        // a quick 'facebook' style tooltip
        // function version 2.2, 21/06/2009 20:04:16

        if (!$type(message))
            return false;

        if ($type(this.tipbody)== "element")
            this.tipbody.dispose();

        var options = $merge({
            eventStart: "mouseenter",
            eventEnd: "mouseleave",
            topOffset: 20,
            opacity: 1
        }, options);

        var coords = this.getCoordinates();

        this.tipbody = new Element("div", {
            styles: {
                background: "transparent url(/images/point_down.gif) no-repeat center bottom",
                float: "left",
                position: "absolute",
                left: 0,
                top: -100,
                opacity: options.opacity,
                height: 20
            }
        }).adopt(new Element("div", {
            styles: {
                background: "#4c4c4c",
                color: "#ffffe5",
                padding: 0,
                "padding-top": 2,
                "line-height": 10,
                margin: 0,
                "padding-left": 9,
                "padding-right": 9,
                display: "block",
                "margin-left": "auto",
                "margin-right": "auto",
                overflow: "hidden",
                "font-size": "10px",
                "font-family": "verdana",
                float: "left",
                height: 13
            },
            html: message.replace(/ /g, "&nbsp;")
        })).inject(document.body);

        this.addEvent(options.eventEnd, function() {
            if ($type(this.tipbody) == "element") {
                this.tipbody.dispose();
                this.tipbody = null;
                this.removeEvent(options.eventEnd);
            }
        }.bind(this));

        // var tipWidth = tipbody.getCoordinates();
        var t = this.tipbody.getSize();

        this.tipbody.setStyles({
            left: coords.left + (coords.width / 2).round() - (t.x / 2).round(),
            top: coords.top - options.topOffset,
            width: t.x
        });

        return this;
    },
    slidingTip: function(what, options) {
        // apple style tooltip
        var _this = this, coords = _this.getCoordinates(), options = $merge({
            eventEnd: "mouseleave",
            eventEndTrigger: _this,     // parent element or self
            topOffset: 90,             // offset when in full view
            topStartOffset: 100,        // offset for animation start
            opacity: .8,                // target opacity in full view
            className: "tooltip",       // linked to css
            morphOptions: {
                duration: 300,
                transition: Fx.Transitions.Sine.easeOut
            },
            delay: 0,                    // can dispose on a timer, in seconds
            id: "tid" + $random(100, 1000)
        }, options);

        // we only need one tip instance per parent element.
        if ($type(this.tip) != "element") {
            this.tip = new Element("div", {
                "class": options.className
                // id: options.id,
            });
        }

        // if it's an existing one, with an implicit id, pick it up
        if ($(options.id))
            this.tip = $(options.id);

        // set initial options and html
        this.tip.set({
            opacity: 0,
            html: "<div style='padding:20px'>" + what + "</div>",
            styles: {
                left: coords.left + coords.width / 2
            }
        }).inject(document.body).removeEvents();

        // show it. cancel any previous animation instances for tip
        if ($type(this.morph) == "object")
           this.morph.cancel();
        else
            this.morph = new Fx.Morph(this.tip, options.morphOptions);

        // show it. really.
        this.morph.start({
            opacity: options.opacity,
            top: [coords.top - _this.tip.getSize().y - options.topStartOffset, coords.top - options.topOffset]
        }).chain(function() {
            // once done, decide how to exit
            if (options.delay == 0) {
                // based on an event.
                options.eventEndTrigger.addEvent(options.eventEnd, function() {
                    _this.morph.cancel();
                    _this.slidingTipaway();
                });
            }
            else {
                // based on a timed delay
                (function() {
                    _this.slidingTipaway();
                }).delay(options.delay);
            }
        });

        // define the tooltip close animation morph object
        var closeAnimation = {
            opacity: 0,
            top: coords.top - _this.tip.getSize().y - options.topStartOffset
        }

        // ... and save it within the parent element
        this.store("closeAnimation", closeAnimation);

        return this;
    },
    slidingTipaway: function() {
        // animate an slidingTip, opposite on in method.
        if (this.tip) {
            this.morph.start(this.retrieve("closeAnimation"));
        }

        return this;
    }
}); // end element.implement


var toggleModal = function(backgroundColour, options) {
    // modal view for the whole screen
    // ver 2.02 17/10/2008 03:42:06
    // returns the $("modal") div as element.
    if ($("modal")) {
        $("modal").dispose();
        return false;
    }

    var options = $merge({
        zIndex: 10000000,
        opacity: .8,
        events: $empty()
    }, options);

    if (!$type(backgroundColour) && !$("modal"))
        return false;

    return new Element("div", {
        id: "modal",
        styles: {
            position: "absolute",
            top: 0,
            left: 0,
            width: window.getScrollWidth(),
            height: window.getScrollHeight(),
            background: backgroundColour,
            "z-index": options.zIndex
        },
        opacity: options.opacity,
        events: options.events
    }).inject(document.body);
} // end toggleModal

var centerBox = function(width, height) {
    // returns a coordinates JSON for positioning of a box in the centre of the browser
    var winSize = window.getSize();
    var winScroll = window.getScroll();
    var windowHeight = window.getScrollHeight()-1;

    var coords = {
        y: winScroll.y + (winSize.y / 2) - (height / 2),
        x: winSize.x / 2 - width / 2,
        width: width,
        height: height
    }

    if (coords.y + height + 20 > windowHeight)
        coords.y = windowHeight - 20 - height;

    if (coords.y <= 0) {
        coords.y = 20;
    }

    return coords;
}


var largerBox = {
    version: function() {
        return 2.1; // 17/05/2009 14:18:40
    },
    init: function(options) {
        // by default it works on image links with the class 'fullImage'

        // set other defaults.
        var options = $merge({
            selector: "a.fullImage",        // can pass on any A object or array of objects, need to contain links to images.
            styleBorder: "1px solid #000",  // border style around image
            useShadow: false,               // apply a css shadow after image is drawn
            useModal: true,                 // use a modal window (toggleModal function) or not
            useTooltip: true,               // uses element.tooltip prototype (needs it defined, set to false if not avail)
            colourModal: "#555",            // default colour for modal,
            imageBackground: "#fff",        // useful to have one for png or gif with transparency
            zIndex: 1000000000              // default z-index for the enlarged images
        }, options);

        // attach to selector
        $$(options.selector).addEvents({
            click: function(e) {
                this.fireEvent("mouseleave"); // get rid of tooltips, if any.

                // only one instance and do not go over other modals...
                if ($("largerBox") || $("modal"))
                    return false;

                var _this = this, myevents;
                var e = new Event(e).stop(); // stop default. can use e.preventDefault also but this has
                                             // issues in IE6
                if (options.useModal)
                    toggleModal(options.colourModal);

                var coords = this.getFirst().getCoordinates();

                // load the image
                new Asset.image(this.get("href"), {
                    onload: function() {
                        // insert it. use the centerBox function to get coordinates
                        var image = centerBox(this.width, this.height); // height and width are available after onload ends

                        this.set({
                            id: "largerBox",
                            styles: $merge({
                                zIndex: options.zIndex,
                                position: "absolute",
                                border: options.styleBorder,
                                background: options.imageBackground
                            }, coords),
                            "class": "cur",
                            events: {
                                click: function() {
                                    $clear(myevents);
                                    this.fireEvent("mouseleave");

                                    if (options.useShadow) {
                                        var rel = this.get("data-related");
                                        if ($(rel)) {
                                            $(rel).dispose();
                                        }
                                    }

                                    // close animation
                                    var myEffect = new Fx.Morph($("largerBox"), {
                                        transition: Fx.Transitions.Sine.easeOut,
                                        duration: 150
                                    }).start($merge({
                                        "border-width": 0,
                                        opacity: .4
                                    }, coords));

                                    this.fade(0);

                                    var _this = this;
                                    (function() {
                                        _this.smartDispose(); // also gets rid of a shadow if any.
                                        if (options.useModal && $("modal"))
                                            toggleModal();
                                    }).delay(500);
                                }
                            }
                        }).inject(document.body);


                        $clear(initCheck); // cancels out any older animations

                        var myEffect = new Fx.Morph($("largerBox"), {
                            transition: Fx.Transitions.Sine.easeOut,
                            duration: 300
                        }).start({
                            width: image.width,
                            height: image.height,
                            left: image.x,
                            top: image.y,
                            "border-width": "3px"
                        }).chain(function() {
                            if (options.useShadow)
                                $("largerBox").dropShadow();
                        });

                        var __this = this;

                        (function() {
                            // add close function after we're done animating
                            __this.addEvents({
                                mouseenter: function() {
                                    if ($type(__this.tooltip) == "function" && options.useTooltip)
                                        __this.tooltip("click to close");
                                }
                            });

                            if (options.useModal) {
                                // get rid of it by clicking on modal also
                                $("modal").addEvents({
                                    click: function() {
                                        __this.fireEvent("click");
                                    }
                                });
                            }
                        }).delay(300);

                        myevents = (function() {
                            __this.fireEvent("mouseenter");
                        }).delay(1000);
                    }
                });

                var initCheck = (function() {
                    _this.set("href", "#").removeEvents();
                    _this.getFirst().removeEvents();
                    if (!$("largerBox") && options.useModal)
                        toggleModal();
                }).delay(3500);
            },
            mouseenter: function() {
                // do something on parent element's mouseover.
            }

        });

    } // end init largerBox
}; // end largerBox namespace


var labels = {
    setLabels: function() {
        $$("code").each(function(el) {
            // attach a label in the top-left corner
            var $coords = el.getCoordinates();
            var blockQuote = el.getParent().getParent();
            var $coords = blockQuote.getCoordinates();
            var foo = new Element("div", {
                styles: {
                    border: "1px solid #727F8A",
                    background: "#ffffe5",
                    color: "#444",
                    "font-size": "9px",
                    "font-weight": 600,
                    "letter-spacing": 2,
                    "text-transform": "uppercase",
                    height: 14,
                    padding: 1,
                    "padding-top": 0,
                    position: "absolute",
                    "z-index": 1000000000,
                    left: $coords.left + 6,
                    top: $coords.top - 12
                },
                "class": "codeLabel",
                text: el.className
            }).inject(document.body);

            foo.clone().set({
                styles: {
                    left: $coords.left + 10,
                    top: $coords.top - 8,
                    opacity: .4,
                    background: "#000",
                    border: 0
                }
            }).injectBefore(foo);

            var c2c = new Element("div", {
                styles: {
                    border: "1px solid #727F8A",
                    background: "#ffffe5",
                    color: "#444",
                    "font-size": "9px",
                    "font-weight": 600,
                    "letter-spacing": 2,
                    "text-transform": "uppercase",
                    height: 14,
                    padding: 1,
                    "padding-top": 0,
                    position: "absolute",
                    left: $coords.right - 70,
                    top: $coords.top - 12
                },
                "class": "codeLabel cur",
                text: "copy"
            }).inject(document.body).addEvents({
                click: function() {
                    clipboard(el.get("text"));
                }
            });

            c2c.clone().set({
                styles: {
                    left: $coords.right - 66,
                    top: $coords.top - 8,
                    opacity: .4,
                    background: "#000",
                    border: 0
                }
            }).injectBefore(c2c);

            var foo2 = new Element("div", {
                styles: {
                    border: "1px solid #727F8A",
                    background: "#ffffe5",
                    color: "#444",
                    "font-size": "9px",
                    "font-weight": 600,
                    "letter-spacing": 2,
                    "text-transform": "uppercase",
                    height: 14,
                    padding: 1,
                    "padding-top": 0,
                    position: "absolute",
                    left: $coords.right - 20,
                    top: $coords.top - 12
                },
                "class": "codeLabel cur",
                text: "+"
            }).inject(document.body).addEvents({
                click: function() {
                    this.fireEvent("mouseleave");
                    var oldStyles = blockQuote.getStyles("width", "position", "z-index", "height", "left", "top");
                    if ($("codeBox") || $("modal"))
                        return false;

                    var _this = this, myevents;
                    toggleModal("#555", {
                        zIndex: 1000,
                        events: {
                            click: function() {
                                $("modal").dispose();
                                blockQuote.setStyles(oldStyles).set("id", "");
                                window.doresize = true;
                                labels.moveLabels();
                                blockQuote.removeEvents();
                            }
                        }
                    });

                    var coords = blockQuote.getCoordinates();

                    var winSize = window.getSize();
                    var winScroll = window.getScroll();
                    var windowHeight = window.getScrollHeight()-1;

                    blockQuote.set({
                        id: "codeBox",
                        styles: {
                            position: "absolute",
                            left: blockQuote.left,
                            top: blockQuote.top,
                            "z-index": 1000000000-1
                        },
                        events: {
                            click: function(e) {
                                e.stopPropagation();
                            }
                        }
                    });


                    blockQuoteHeight = blockQuote.getStyle("height").toInt();
                    var code = {
                        y: winScroll.y + (winSize.y / 2) - (blockQuoteHeight / 2),
                        x: winSize.x / 2 - 800 / 2,
                        width: 800,
                        height: (blockQuoteHeight > winSize.y) ? winSize.y - 40 : blockQuoteHeight
                    }

                    if (code.y + blockQuoteHeight + 20 > windowHeight)
                        code.y = windowHeight - 20 - blockQuoteHeight;

                    if (code.y <= 0)
                        code.y = 20;

                    var myEffect = new Fx.Morph(blockQuote, {
                        transition: Fx.Transitions.Sine.easeOut,
                        duration: 300
                    }).start({
                        width: code.width,
                        height: code.height,
                        left: code.x,
                        top: code.y,
                        "border-width": 0,
                        opacity: 1
                    }).chain(function() {
                        $$("div.codeLabel").dispose();
                        window.doresize = false;
                        blockQuote.addEvent("click", function() {
                            $("modal").fireEvent("click");
                        });
                    });

                },
                mouseenter: function() {
                    this.tooltip("expand code");
                }
            });

            foo2.clone().set({
                styles: {
                    left: $coords.right - 16,
                    top: $coords.top - 8,
                    opacity: .4,
                    background: "#000",
                    border: 0
                }
            }).injectBefore(foo2);

        });

    },
    moveLabels: function() {
        if (window.doresize == false)
            return false;
        $$("div.codeLabel").dispose();
        this.setLabels();
    },
    init: function() {
        this.moveLabels();
        window.addEvent("resize", function() {
            labels.moveLabels()
        });
    }
}


var C = {
    // console wrapper
    debug: true, // global debug on|off
    quietDismiss: false, // may want to just drop, or alert instead
    log: function() {
        if (!C.debug) return false;


        if (typeof console == 'object' && typeof console.log != "undefined")
            try {
                console.log.apply(this, arguments); // safari's console.log can't accept scope...
            } catch(e) {
                // so we loop instead.
                for (var i = 0, l = arguments.length; i < l; i++)
                    console.log(arguments[i]);
            }
        else
            if (!C.quietDismiss) {
                var result = "";
                for (var i = 0, l = arguments.length; i < l; i++)
                    result += arguments[i] + " ("+typeof arguments[i]+") ";

                alert(result);
            }
    }
}; // end console wrapper.

var clipboard = function(text2copy) {
    // this for ie...
    /*if(window.clipboardData && clipboardData.setData )    {
        clipboardData.setData("text", escape(text2copy));
        return false;
    }*/

    // else, use flash object instead.
    var clipObject = '<embed src="/_clipboard.swf" FlashVars="clipboard='+encodeURIComponent(text2copy)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>';


    if ($("flashcopier"))
        $("flashcopier").set("html", clipObject);
    else
        new Element("div", {
            id: "flashcopier",
            styles: {
                position: "absolute",
                top: -1,
                left: -1,
                width: 1,
                height: 1
            },
            html: clipObject
        }).inject(document.body);

};
