// ==UserScript==
// @name           InFullVolume
// @namespace      http://polog.org/
// @description    music visualizer for anywhere
// @include        *
// ==/UserScript==
// author: negipo
// SiteInfo class is based on Twitter Text Converter http://userscripts.org/scripts/show/22404
// thx youpy :D


var SWF_URL = "http://polog.org/lab/full-volume/test/mic.swf";
var SITE_INFO = [
    {
        name: 'demo page',
        pattern: 'http://polog.org/lab/in-full-volume/',
        elements: '//ol[@class="description-ol"]/li'
    }
];

var $X = function (exp, context) {
    if (!context) context = document;
    var resolver = function (prefix) {
        var o = document.createNSResolver(context)(prefix);
        return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
    }
    var exp = document.createExpression(exp, resolver);

    var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
    switch (result.resultType) {
    case XPathResult.STRING_TYPE : return result.stringValue;
    case XPathResult.NUMBER_TYPE : return result.numberValue;
    case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
    case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
        result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var ret = [];
        for (var i = 0, len = result.snapshotLength; i < len ; i++) {
            ret.push(result.snapshotItem(i));
        }
        return ret;
    }
    }
    return null;
}

var elementList = function(exp){
    this.objs = $X(exp);
    this.elements = [];
    for(var i = 0; i < this.objs.length; i+= 1){
        var obj = this.objs[i];
        this.elements.push(new elementSingle(obj));
        if(i != 0){
            this.elements[i - 1].setNextElement(this.elements[i]);
        }
    }
    var self = this;
    if(self.elements[0]){
        setInterval(function(){self.elements[0].triggerMove()}, 100);
    }
}

var elementSingle = function(element){
    this.element = element;
    this.buf_level = this.level = 1.0;
    var self = this;
    this.dimensions = {
        width: this.element.offsetWidth,
        height: this.element.offsetHeight
    };
    this.element.style.overflow = "hidden";
    this.element.style.height = this.dimensions.height + "px";
    this.effect = setInterval(function(){self.move()}, 50);
}
elementSingle.prototype.setNextElement = function(nextElement){
    this.nextElement = nextElement;
}
elementSingle.prototype.setLevel = function(level){
    var next = this.nextElement;
    if(next){
        var self = this;
        setTimeout(function(){next.setLevel(self.buf_level)}, 50);
    }
    this.buf_level = this.level = level;
}
elementSingle.prototype.move = function(){
    this.level = this.level - (this.level * 0.2);
    this.element.style.width = Math.round(this.dimensions.width * this.level) + 'px';
}
elementSingle.prototype.triggerMove = function(){
    var activityLevel = mic_activityLevel * 0.1;
    activityLevel = (activityLevel > 1) ? 1.0 : activityLevel;
    var ratio = this.element.offsetWidth / this.dimensions.width;
    if(activityLevel > ratio){
        this.setLevel(activityLevel);
    }
}

var div = unsafeWindow.document.createElement("div");
var init_embed = function(){
    var embed = unsafeWindow.document.createElement("embed");
    embed.src = SWF_URL;
    embed.width = "300px";
    embed.height = "200px";
    embed.name = "mic";
    embed.id = "mic";
    embed.setAttribute("allowScriptAccess", "always");
    embed.setAttribute("wmode", "transparent");
    div.style.zIndex = "10000";
    div.style.position = "absolute";
    div.style.top = "0px";
    div.style.right = "0px";
    div.appendChild(embed);
    unsafeWindow.document.body.appendChild(div);
}

var mic_activityLevel = 0.0;
unsafeWindow.mic_DoFSCommand = function(command, args) {
    if(!args.match(/^\d*$/)) return;
    mic_activityLevel = eval(args);
    if(mic_activityLevel && div.style.top != "-9999px"){
        div.style.top = "-9999px";
        div.style.left = "-9999px";
    }
}

unsafeWindow.InFullVolume_getMicActivityLevel = function(){
    return mic_activityLevel
}

var SiteInfo = function() {
    this.entries = [];
};

SiteInfo.prototype.get = function(url, callback) {
    var self = this;
    var cacheInfo;

    if(cacheInfo = Cache.get('cacheInfo')) {
        self.entries = cacheInfo;
        callback(self.entries);
    } else {
        GM_xmlhttpRequest({
            method : "GET",
            url : url,
            onload : function(res) {
                var hdoc = createHTMLDocumentByString(res.responseText)
                var entries = $X('//dl[@class="entry"]', hdoc);
                self.entries = entries.map(self.parseEntry);
                callback(self.entries);

                Cache.set('cacheInfo', self.entries, 24 * 60 * 60 * 1000) // 1 day
            }
        });
    }
};

SiteInfo.prototype.parseEntry = function(dl) {
    var entries = {};
    var dts = dl.getElementsByTagName('dt');

    Array.forEach(dts, function(dt) {
        var dd = dt.nextSibling;
        while(dd) {
            if(dd.tagName == 'DD') {
                entries[dt.textContent] = dd.textContent;
                break;
            }
            dd = dd.nextSibling;
        }
    });
    return entries;
};

SiteInfo.prototype.clearCache = function() {
    Cache.set('cacheInfo', null, 0);
};

var Cache = {};

Cache.set = function(key, value, expire) {
    var expire = new Date().getTime() + expire;
    GM_setValue(key, uneval({ value: value, expire: expire }));
}

Cache.get = function(key) {
    var cached = eval(GM_getValue(key));
    if(!cached) {
        return null;
    }

    if(cached.expire > new Date().getTime()) {
        return cached.value;
    }

    return null;
}

function createHTMLDocumentByString(str) {
    var html = str.replace(/<!DOCTYPE.*?>/, '').replace(/<html.*?>/, '').replace(/<\/html>.*/, '');
    var htmlDoc  = document.implementation.createDocument(null, 'html', null);
    var fragment = createDocumentFragmentByString(html);
    htmlDoc.documentElement.appendChild(fragment);
    return htmlDoc;
}

function createDocumentFragmentByString(str) {
    var range = document.createRange()
    range.setStartAfter(document.body)
    return range.createContextualFragment(str)
}

var elements;
var siteInfo = new SiteInfo();
unsafeWindow.addEventListener('load', function(){
    siteInfo.get('http://jottit.com/nspjq/', function(entries) {
        entries = SITE_INFO.concat(entries);
        entries.some(function(entry) {
            if(location.href.match(entry.pattern) &&
               $X(entry.elements)) {
                new elementList(entry.elements);
                init_embed();
                return false;
            }
        });
    });
}, false);

GM_registerMenuCommand('InFullVolume - clear cache', siteInfo.clearCache)
