/*!! uuAltCSS.js { version: "0.1", license: "MIT", author: "uupaa.js@gmail.com" } */

// === uuMeta ===
// depend: none
/*
window.UUMETA_DEBUG = 0;
window.UUMETA_IMAGE_DIR = "."
uuMeta.ie
uuMeta.iemode8
uuMeta.quirks
uuMeta.opera
uuMeta.gecko
uuMeta.gecko191 - Firefox3.5+
uuMeta.firefox
uuMeta.webkit
uuMeta.webkit522 - Safari3+, Chrome1+
uuMeta.webkit530 - Safari4+, Chrome2+
uuMeta.safari
uuMeta.chrome
uuMeta.iphone
uuMeta.uaver
uuMeta.slver
uuMeta.flashver
uuMeta.enginever
uuMeta.canvas
uuMeta.runstyle
uuMeta.imagedir
uuMeta.blackout
uuMeta.debug
uuMeta.hex
uuMeta.mix(base, flavor, aroma = undef, override = true) - return base
uuMeta.toArray(fake) - return array
-- element unique id ---
uuMeta.uid(elm) - return id
--- boot ---
window.boot - user defined boot loader
uuMeta.boot(callback, highPriority = false)
--- event ---
uuMeta.event.bind(elm, eventName, fn, capture)
uuMeta.event.unbind(elm, eventName, fn, capture)
uuMeta.event.stop(evt)
 */
(function() {
var _meta,  // inner namespace
    _uid,   // inner namespace
    _boot,  // inner namespace
    _event, // inner namespace
    _win = window,
    _doc = document,
    _debug = (_win.UUMETA_DEBUG || 0),
    _float = parseFloat,
    _nu = navigator.userAgent,
    _ie = !!_doc.uniqueID,
    _opera = !!_win.opera,
    _gecko = _nu.indexOf("Gecko/") > 0,
    _webkit = _nu.indexOf("WebKit") > 0,
    _chrome = _nu.indexOf("Chrome") > 0,
    _uaver = _opera ? (opera.version().replace(/\d$/, "") - 0) // Opera10 shock
                    : _float((/(?:IE |fox\/|ome\/|ion\/)(\d+\.\d)/.
                             exec(_nu) || [,0])[1]),
    _slver = (function() {
      try {
        var a = ["1.0", "2.0", "3.0"], i = 3, ok,
            o = _ie ? new ActiveXObject("AgControl.AgControl")
                    : navigator.plugins["Silverlight Plug-In"];
        while (i--) {
          ok = _ie ? o.IsVersionSupported(a[i])
                   : _float(/\d+\.\d+/.exec(o.description)[0]) >= _float(a[i]);
          if (ok) {
            return _float(a[i]);
          }
        }
      } catch(err) {}
      return 0;
    })(),
    _flashver = (function() {
      try {
        var o = _ie ? new ActiveXObject("ShockwaveFlash.ShockwaveFlash")
                    : navigator.plugins["Shockwave Flash"],
            v = _ie ? o.GetVariable("$version").replace(/,/g, ".")
                    : o.description,
            m = /\d+\.\d+/.exec(v);
        return m ? _float(m[0], 10) : 0;
      } catch(err) {}
      return 0;
    })(),
    _egver = _float(((/(?:rv\:|ari\/|sto\/)(\d+\.\d+(\.\d+)?)/.
      exec(_nu) || [,0])[1]).toString().
      replace(/[^\d\.]/g, "").replace(/^(\d+\.\d+)(\.(\d+))?$/, "$1$3")
    ),
    _hex = (function() {
      var r = [], i = 256;
      for(; i < 512; ++i) {
        r.push(i.toString(16).slice(1));
      }
      return r;
    })(),
    _mix = function(base,       // @param Hash: mixin base
                    flavor,     // @param Hash: add flavor
                    aroma,      // @param Hash(= undefined): add aroma
                    override) { // @param Boolean(= true): true is override
                                // @return Hash: base
      var i, ride = (override === void 0) || override;

      for (i in flavor) {
        if (ride || !(i in base)) {
          base[i] = flavor[i];
        }
      }
      return aroma ? _meta.mix(base, aroma, void 0, ride) : base;
    },
    _toArray = function(fake) { // @param NodeList/Array:
                                // @return Array:
      if (!_ie && (_opera && _uaver >= 9.5)) {
        return Array.prototype.slice.call(fake, 0);
      }
      var rv = [], ri = -1, i = 0, iz = fake.length;
      for (; i < iz; ++i) {
        rv[++ri] = fake[i];
      }
      return rv;
    },
    _bootFire = 0, // fired flag
    _bootOrder = [[], []], // [high, low]
    UNIQUEID = "uuqid";

_meta = {
  ie:         _ie,
  iemode8:    _ie && _doc.documentMode >= 8,
  quirks:     (_doc.compatMode || "") !== "CSS1Compat",
  opera:      _opera,
  gecko:      _gecko,
  gecko191:   (_gecko && _egver >= 1.91), // Firefox3.5+
  firefox:    _nu.indexOf("Firefox/") > 0,
  webkit:     _webkit,
  webkit522:  (_webkit && _egver >= 522), // Safari3+, Chrome1+
  webkit530:  (_webkit && _egver >= 530), // Safari4+, Chrome2+
  chrome:     _chrome,
  safari:     !_chrome && _nu.indexOf("Safari") > 0,
  iphone:     _webkit && /iPod|iPhone/.test(_nu),
  uaver:      _uaver,     // User Agent version
  slver:      _slver,     // Silverlight version
  flashver:   _flashver,  // Flash version(ver 7 later)
  enginever:  _egver,     // Render engine version
  canvas:     (_ie && _uaver >= 6) || // <canvas> support
              (_opera  && _uaver >= 9.2) ||
              (_gecko  && _egver >= 1.81) ||
              (_webkit && _egver >= 522),
  runstyle:   _ie ? "currentStyle"
                  : (_win.getComputedStyle ||
                     _doc.defaultView.getComputedStyle), // old WebKit
  imagedir:   (_win.UUMETA_IMAGE_DIR || ".").replace(/\/+$/, ""),
  blackout:   0, // 1: fire location.reload()
  debug:      _debug,
  hex:        _hex,
  mix:        _mix,
  toArray:    _toArray
};

// uuMeta.uid - get element unique id
_uid = function(elm) { // @param Node:
                       // @return String: "uuqid{n}", n from 1
  return elm[UNIQUEID] || (elm[UNIQUEID] = ++_win[UNIQUEID]);
};

// uuMeta.boot - catch DOMContentReady/window.onload event
_boot = function(callback,       // @param Function:
                 highPriority) { // @param Boolean(= false):
  (_bootFire && !_meta.blackout)
      ? callback() // run now
      : _bootOrder[highPriority ? 0 : 1].push(callback); // stock
};

_event = {
  // uuMeta.event.bind
  bind: function(elm,       // @param Node:
                 eventName, // @param String:
                 callback,  // @param Function: callback
                 capture) { // @param Boolean(= false):
    _ie ? elm.attachEvent("on" + eventName, callback)
        : elm.addEventListener(eventName, callback, capture || false);
  },
  // uuMeta.event.unbind
  unbind: function(elm,       // @param Node:
                   eventName, // @param String:
                   callback,  // @param Function: callback
                   capture) { // @param Boolean(= false):
    _ie ? elm.detachEvent("on" + eventName, callback)
        : elm.removeEventListener(eventName, callback, capture || false);
  },
  // uuMeta.event.stop
  stop: function(evt) { // @param EventObject(= undefined):
    evt = evt || _win.event;
    _ie ? (evt.cancelBubble = true) : evt.stopPropagation();
    _ie ? (evt.returnValue = false) : evt.preventDefault();
  }
};

function bootFire(v) {
  if (!_bootFire++) {
    if (_debug) {
      while ( (v = _bootOrder[0].shift()) ) { !_meta.blackout && v(); }
      while ( (v = _bootOrder[1].shift()) ) { !_meta.blackout && v(); }
      !_meta.blackout &&
          (typeof _win.boot === "function") && _win.boot();
    } else {
      try {
        // high priority
        while ( (v = _bootOrder[0].shift()) ) { !_meta.blackout && v(); }
        // low priority
        while ( (v = _bootOrder[1].shift()) ) { !_meta.blackout && v(); }
        !_meta.blackout &&
            (typeof _win.boot === "function") && _win.boot(); // boot loader
      } catch(err) {}
    }
  }
}

// --- initialize ---
// boot: windowReadyState and domReadyState
if (_ie) {
  _win.attachEvent("onload", bootFire);
  (function peek() {
    try {
      _doc.firstChild.doScroll("up"), bootFire();
    } catch(err) { setTimeout(peek, 16); }
  })();
} else { // WebKit, Firefox, Opera
  _win.addEventListener("load", bootFire, false);
  _doc.addEventListener("DOMContentLoaded", bootFire, false);
}

// prebuild element unique id ( node[UNIQUEID] )
_boot(function() {
  var node = _doc.getElementsByTagName("*"), v, i = 0, iz = node.length;

  for (; i < iz; ++i) {
    v = node[i];
    if (!_ie || v.nodeType === 1) {
      v[UNIQUEID] || (v[UNIQUEID] = ++_win[UNIQUEID]);
    }
  }
}, 1);

// init VML namespace
_ie && _boot(function() {
  var NS = 'urn:schemas-microsoft-com:', NSV = '#default#VML',
      ns = _doc.namespaces;

  if (!ns["v"]) {
    ns.add("v", NS + "vml", NSV);
    ns.add("o", NS + "office:office", NSV);
  }
  _doc.createStyleSheet().cssText =
    "canvas{display:inline-block;text-align:left;width:300px;height:150px}" +
    "v\:roundrect,v\:oval,v\:shape,v\:stroke,v\:fill,v\:textpath," +
    "v\:image,v\:line,v\:skew,v\:path,o\:opacity2" +
    "{behavior:url(" + NSV + ");display:inline-block}"; // inline-block [!]
});

// --- export ---
_win.uuMeta     = _meta;    // window.uuMeta
_win[UNIQUEID]  = 0;        // window.uuqid
_meta.uid       = _uid;     // window.uuMeta.uid
_meta.boot      = _boot;    // window.uuMeta.boot
_meta.event     = _event;   // window.uuMeta.event

})(); // uuMeta scope


// === uuToken ===
// depend: none

(function() {
var _token, // inner namespace
    TRIM = /^\s+|\s+$/g;

_token = {
  SPACE: " ", // split "rgba(,,,) url()"
  COMMA: ",", // split "url(), url(), ..."

  // uuToken.splitCSSValue - split CSS Value token
  //    splitCSSValue("url(...), -webkit-gradient(linear, 0 0, 0% 100%, ...)");
  //        v
  //    ["url(...)", "-webkit-gradient(linear, 0 0, 0% 100%, ...)"]
  splitCSSValue: function(expr,     // @param String: expression
                          splitter, // @param String(= " "):
                          notrim) { // @param Boolean(= false): trim space
                                    // @return Array: ["token", ...]
    splitter = splitter || " ";
    if (expr.indexOf(splitter) < 0) { return [expr]; }

    var rv = [], ary = expr.split(""), v, w, i = 0,
        nest = 0, quote = 0, q, tmp = [], ti = -1, esc = 0,
        hash = { "(": 2, ")": 3, '"': 4, "'": 4, "\\": 5 };

    hash[splitter] = 1;

    while ( (v = ary[i++]) ) {
      if (esc) {
        esc = 0;
        tmp[++ti] = v;
      } else {
        switch (hash[v] || 0) {
        case 0: tmp[++ti] = v;
                break;
        case 1: if (!quote) {
                  if (nest) {
                    tmp[++ti] = v;
                  } else {
                    w = tmp.join(""),
                    rv.push(notrim ? w : w.replace(TRIM, ""));
                    tmp = [];
                    ti = -1;
                  }
                } else {
                  tmp[++ti] = v;
                }
                break;
        case 2: tmp[++ti] = v;
                !quote && ++nest;
                break;
        case 3: tmp[++ti] = v;
                !quote && --nest;
                break;
        case 4: if (!quote) {
                  quote = 1;
                  q = v;
                } else if (v === q) {
                  quote = 0;
                }
                tmp[++ti] = v;
                break;
        case 5: esc = 1;
                tmp[++ti] = v;
        }
      }
    }
    // remain
    if (tmp.length) {
      w = tmp.join("");
      rv.push(notrim ? w : w.replace(TRIM, ""));
    }
    return rv;
  }
};

// --- initialize ---

// --- export ---
window.uuToken = _token;

})();

// === uuMeta.event.resize ===
// depend: uuMeta
/*
uuMeta.event.resize(callback, type = false) - return atom
 */
(function() {
var _mm = uuMeta,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _ieroot = _mm.quirks ? "body" : "documentElement",
    _db1 = { delay: 40, lock: 0, kill: 0, callback: [] },
    _db2 = { delay: 100, lock: 0, kill: 0, callback: [] };

function getInnerSize() {
  var root = _ie ? _doc[_ieroot] : _win;
  return { w: root.innerWidth  || root.clientWidth,
           h: root.innerHeight || root.clientHeight };
}

// event handler
function type1(callback) { // @param Function/0: callback function or zero
                           // @return Number: callback function atom
  if (!callback) {
    _db1.kill = 1;
    return 0;
  }
  !_db1.callback.length && _mm.event.bind(_win, "resize", ontype1); // init
  _db1.callback.push(callback); // regist
  return _db1.length - 1; // return atom
}

function ontype1() {
  if (_db1.kill) {
    _mm.event.unbind(_win, "resize", ontype1);
    _db1.kill = _db1.lock = 0;
    _db1.callback = []; // clear
    return;
  }
  if (!_db1.lock++) {
    setTimeout(function() {
      var i = 0, iz = _db1.callback.length;
      for (; i < iz; ++i) {
        (i in _db1.callback) && _db1.callback[i]();
      }
      setTimeout(function() { _db1.lock = 0; }, 0); // delay unlock
    }, _db1.delay);
  }
}

// resize agent
function type2(callback) { // @param Function/0: callback function or zero
                           // @return Number: callback function atom
  if (!callback) {
    _db2.kill = 1;
    return 0;
  }
  !_db2.callback.length && (_db2.size = getInnerSize()); // init
  _db2.callback.push(callback); // regist

  function loop() {
    if (_db2.kill) {
      _db2.kill = _db2.lock = 0;
      _db2.callback = []; // clear
      return;
    }
    if (!_db2.lock++) {
      var dim = getInnerSize(), i, iz;
      if (_db2.size.w !== dim.w || _db2.size.h !== dim.h) { // resized
        _db2.size = dim;
        for (i = 0, iz = _db2.callback.length; i < iz; ++i) {
          (i in _db2.callback) && _db2.callback[i]();
        }
      }
      setTimeout(function() { _db2.lock = 0; }, 0); // delay unlock
    }
    setTimeout(loop, _db2.delay);
  }
  setTimeout(loop, _db2.delay);

  return _db2.length - 1; // return atom
}

// --- initialize ---

// --- export ---
_mm.event.resize =
    function(callback, // @param Function/0: callback function or zero
             agent) {  // @param Boolean(= false): use resize agent
  return agent ? type2(callback)  // use resize agent
               : type1(callback); // use event handler
};

})();


// === uuColor ===
// depend: uuMeta
/*
uuColor.hash
uuColor.isColor(str) - return Boolean
uuColor.hex(rgba) - return HexColorString("#ffffff")
uuColor.rgba(rgba) - return RGBAColorString("rgba(0,0,0,0)")
uuColor.arrange(rgba, h = 0, s = 0, v = 0) - return RGBAHash
uuColor.comple(rgba) - return RGBAHash
uuColor.gray(rgba) - return RGBAHash
uuColor.sepia(rgba) - return RGBAHash
uuColor.parse(color, toRGBA = 0, valid = 0)
                         - return ["#ffffff", alpha] or RGBAHash or Boolean
uuColor.hex2rgba(hex) - return RGBAHash
uuColor.rgba2hsva(rgba) - return HSVAHash
uuColor.hsva2rgba(hsva) - return RGBAHash
uuColor.rgba2hsla(rgba) - return HSLAHash
uuColor.hsla2rgba(hsla) - return RGBAHash
 */
(function() {
var _color, // inner namespace
    _mm = uuMeta,
    _float = parseFloat,
    _math = Math,
    _round = _math.round,
    _hex = _mm.hex,
    PARSE_HEX = /^#(([\da-f])([\da-f])([\da-f]))(([\da-f])([\da-f]{2}))?$/,
    PARSE_PERCENT = /[\d\.]+%/g,
    PARSE_RGBA =
      /(rgba?)\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d\.]+))?\s*\)/;

_color = {
  // uuColor.hash - color hash
  hash: (function() { // Hash( { black: "#000000" } )
    var rv = [], v, i = 0, iz, item = (
// FAMICOM(R) Named Color(from "FC00" to "FC3F")
"7b7b7bfc00,0000fffc01,0000bdfc02,4229bdfc03,940084fc04,ad0021fc05,8c1000fc06,"+
"8c1000fc07,522900fc08,007300fc09,006b00fc0a,005a00fc0b,004252fc0c,000000fc0d,"+
"000000fc0e,000000fc0f,bdbdbdfc10,0073f7fc11,0052f7fc12,6b42fffc13,de00cefc14,"+
"e7005afc15,f73100fc16,e75a10fc17,ad7b00fc18,00ad00fc19,00ad00fc1a,00ad42fc1b,"+
"008c8cfc1c,000000fc1d,000000fc1e,000000fc1f,f7f7f7fc20,39bdfffc20,6b84fffc22,"+
"9473f7fc23,f773f7fc24,f75294fc25,f77352fc26,ffa542fc27,f7b500fc28,b5f710fc29,"+
"5ade52fc2a,52f794fc2b,00efdefc2c,737373fc2d,000000fc2e,000000fc2f,fffffffc30,"+
"a5e7fffc31,b5b5f7fc32,d6b5f7fc33,f7b5f7fc34,ffa5c6fc35,efceadfc36,ffe7adfc37,"+
"ffde7bfc38,d6f773fc39,b5f7b5fc3a,b5f7d6fc3b,00fffffc3c,f7d6f7fc3d,000000fc3e,"+
"000000fc3f,"+
// W3C Named Color
"000000black,888888gray,ccccccsilver,ffffffwhite,ff0000red,"+
"ffff00yellow,00ff00lime,00ffffaqua,00ffffcyan,0000ffblue,ff00fffuchsia,"+
"ff00ffmagenta,880000maroon,888800olive,008800green,008888teal,000088navy,"+
"880088purple,696969dimgray,808080gray,a9a9a9darkgray,c0c0c0silver,"+
"d3d3d3lightgrey,dcdcdcgainsboro,f5f5f5whitesmoke,fffafasnow,708090slategray,"+
"778899lightslategray,b0c4delightsteelblue,4682b4steelblue,5f9ea0cadetblue,"+
"4b0082indigo,483d8bdarkslateblue,6a5acdslateblue,7b68eemediumslateblue,"+
"9370dbmediumpurple,f8f8ffghostwhite,00008bdarkblue,0000cdmediumblue,"+
"4169e1royalblue,1e90ffdodgerblue,6495edcornflowerblue,87cefalightskyblue,"+
"add8e6lightblue,f0f8ffaliceblue,191970midnightblue,00bfffdeepskyblue,"+
"87ceebskyblue,b0e0e6powderblue,2f4f4fdarkslategray,00ced1darkturquoise,"+
"afeeeepaleturquoise,f0ffffazure,008b8bdarkcyan,20b2aalightseagreen,"+
"48d1ccmediumturquoise,40e0d0turquoise,7fffd4aquamarine,e0fffflightcyan,"+
"00fa9amediumspringgreen,7cfc00lawngreen,00ff7fspringgreen,7fff00chartreuse,"+
"adff2fgreenyellow,2e8b57seagreen,3cb371mediumseagreen,66cdaamediumaquamarine,"+
"98fb98palegreen,f5fffamintcream,006400darkgreen,228b22forestgreen,"+
"32cd32limegreen,90ee90lightgreen,f0fff0honeydew,556b2fdarkolivegreen,"+
"6b8e23olivedrab,9acd32yellowgreen,8fbc8fdarkseagreen,9400d3darkviolet,"+
"8a2be2blueviolet,dda0ddplum,d8bfd8thistle,8b008bdarkmagenta,9932ccdarkorchid,"+
"ba55d3mediumorchid,da70d6orchid,ee82eeviolet,e6e6falavender,"+
"c71585mediumvioletred,bc8f8frosybrown,ff69b4hotpink,ffc0cbpink,"+
"ffe4e1mistyrose,ff1493deeppink,db7093palevioletred,e9967adarksalmon,"+
"ffb6c1lightpink,fff0f5lavenderblush,cd5c5cindianred,f08080lightcoral,"+
"f4a460sandybrown,fff5eeseashell,dc143ccrimson,ff6347tomato,ff7f50coral,"+
"fa8072salmon,ffa07alightsalmon,ffdab9peachpuff,ffffe0lightyellow,"+
"b22222firebrick,ff4500orangered,ff8c00darkorange,ffa500orange,"+
"ffd700gold,fafad2lightgoldenrodyellow,8b0000darkred,a52a2abrown,"+
"a0522dsienna,b8860bdarkgoldenrod,daa520goldenrod,deb887burlywood,f0e68ckhaki,"+
"fffacdlemonchiffon,d2691echocolate,cd853fperu,bdb76bdarkkhaki,bdb76btan,"+
"eee8aapalegoldenrod,f5f5dcbeige,ffdeadnavajowhite,ffe4b5moccasin,"+
"ffe4c4bisque,ffebcdblanchedalmond,ffefd5papayawhip,fff8dccornsilk,"+
"f5deb3wheat,faebd7antiquewhite,faf0e6linen,fdf5e6oldlace,fffaf0floralwhite,"+
"fffff0ivory").split(",");

    for (iz = item.length; i < iz; ++i) {
      v = item[i];
      rv[v.slice(6)] = "#" + v.slice(0, 6);
    }
    return rv;
  })(),

  // uuColor.isColor - judege color
  isColor: function(str) { // @param String:
                           // @return Boolean:
    return _color.parse(str, 0, 2);
  },

  // uuColor.hex - return Hex Color String( "#ffffff" )
  hex: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                        // @return HexColorString( "#ffffff" )
    return ["#", _hex[rgba.r], _hex[rgba.g], _hex[rgba.b]].join("");
  },

  // uuColor.rgba - return RGBA Color String( "rgba(0,0,0,0)" )
  rgba: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                         // @return RGBAColorString( "rgba(0,0,0,0)" )
    return "rgba(" + [rgba.r, rgba.g, rgba.b, rgba.a].join(",") + ")";
  },

  // uuColor.arrange - arrangemented color(Hue, Saturation and Value)
  //    Hue is absolure value,
  //    Saturation and Value is relative value.
  arrange: function(rgba, // @param RGBAHash: Hash( { r,g,b,a })
                    h,    // @param Number(=0): Hue (from -360 to 360)
                    s,    // @param Number(=0): Saturation (from -100 to 100)
                    v) {  // @param Number(=0): Value (from -100 to 100)
                          // @return RGBAHash:
    var rv = _color.rgba2hsva(rgba), r = 360;
    rv.h += h, rv.h = (rv.h > r) ? rv.h - r : (rv.h < 0) ? rv.h + r : rv.h;
    rv.s += s, rv.s = (rv.s > 100) ? 100 : (rv.s < 0) ? 0 : rv.s;
    rv.v += v, rv.v = (rv.v > 100) ? 100 : (rv.v < 0) ? 0 : rv.v;
    return _color.hsva2rgba(rv);
  },

  // uuColor.comple - complementary color
  comple: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                           // @return RGBAHash:
    return { r: rgba.r ^ 255, g: rgba.g ^ 255, b: rgba.b ^ 255, a: rgba.a };
  },

  // uuColor.gray - gray color (G channel method)
  gray: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                         // @return RGBAHash:
    return { r: rgba.g, g: rgba.g, b: rgba.g, a: rgba.a };
  },

  // uuColor.sepia - sepia color
  sepia: function(rgba) { // @param RGBAHash: Hash( { r,g,b,a })
                          // @return RGBAHash:
    var r = rgba.r, g = rgba.g, b = rgba.b,
        y = 0.2990 * r + 0.5870 * g + 0.1140 * b, u = -0.091, v = 0.056;

    r = y + 1.4026 * v;
    g = y - 0.3444 * u - 0.7114 * v;
    b = y + 1.7330 * u;
    r *= 1.2;
    b *= 0.8;
    return { r: r < 0 ? 0 : r > 255 ? 255 : r | 0,
             g: g < 0 ? 0 : g > 255 ? 255 : g | 0,
             b: b < 0 ? 0 : b > 255 ? 255 : b | 0, a: rgba.a };
  },

  // uuColor.parse - parse color
  //    RGBAColorString is "rgba(0,0,0,0)" style color string
  //    HexColorString is "#ffffff" style color string
  //    W3CNamedColorString is "pink" style color string
  parse: function(
      color,    // @parem RGBAColorString/HexColorString
                //        /W3CNamedColorString
      toRGBA,   // @param Boolean(= false): true = return RGBAHash
                //                          false = return Array
      valid) {  // @param Number(= 0): 0 = silent mode
                //                     1 = validation mode(throw exception)
                //                     2 = validation mode(return false)
                // @return Array/RGBAHash/Boolean:
                //          [ HexColorString, Number(alpha) ]
                //          { r, g, b, a }
                //          false: invalid color
                //          true: valid color
    var c = color.toLowerCase(), rv, m, brv = (valid === 2);

    if (c !== "transparent") {
      if (c in _color.hash) {
        if (brv) { return true; }

        rv = _color.hash[c];
        return toRGBA ? _color.hex2rgba(rv) : [rv, 1];
      }
      if ( (m = PARSE_HEX.exec(c)) ) {
        if (brv) { return true; }

        rv = (c.length > 4) ? c
                            : ["#", m[2], m[2],
                                    m[3], m[3],
                                    m[4], m[4]].join("");
        return toRGBA ? _color.hex2rgba(rv) : [rv, 1];
      }
      c = c.replace(PARSE_PERCENT, function(n) {
        return _math.min((_float(n) || 0) * 2.55, 255) | 0
      });
      if ( (m = PARSE_RGBA.exec(c)) ) {
        if (brv) { return true; }

        return toRGBA ? { r: m[2] | 0, g: m[3] | 0, b: m[4] | 0,
                          a: m[1] === "rgb" ? 1 : _float(m[5]) }
                      : [["#", _hex[m[2]], _hex[m[3]], _hex[m[4]]].join(""),
                         m[1] === "rgb" ? 1 : _float(m[5])];
      }
      if (brv) { return false; }
      if (valid === 1) {
        throw color + " invalid color";
      }
    }
    if (brv) { return true; }

    return toRGBA ? { r: 0, g: 0, b: 0, a: 0 }
                  : ["#000000", 0];
  },

  // uuColor.hex2rgba - convert "#ffffff" to RGBAHash
  hex2rgba: function(hex) { // @param HexColorString: String( "#ffffff" )
                            // @return RGBAHash: Hash( { r,g,b,a } )
    var n = parseInt(hex.slice(1), 16);
    return { r: (n >> 16) & 0xff, g: (n >> 8) & 0xff, b: n & 0xff, a: 1 };
  },

  // uuColor.rgba2hsva
  rgba2hsva: function(rgba) { // @param RGBAHash:
                              // @return HSVAHash:
    var r = rgba.r / 255, g = rgba.g / 255, b = rgba.b / 255,
        max = _math.max(r, g, b), diff = max - _math.min(r, g, b),
        h = 0, s = max ? _round(diff / max * 100) : 0, v = _round(max * 100);
    if (!s) {
      return { h: 0, s: 0, v: v, a: rgba.a };
    }
    h = (r === max) ? ((g - b) * 60 / diff) :
        (g === max) ? ((b - r) * 60 / diff + 120)
                    : ((r - g) * 60 / diff + 240);
    // HSVAHash( { h:360, s:100, v:100, a:1.0 } )
    return { h: (h < 0) ? h + 360 : h, s: s, v: v, a: rgba.a };
  },

  // uuColor.hsva2rgba
  hsva2rgba: function(hsva) { // @param HSVAHash:
                              // @return RGBAHash:
    var h = (hsva.h >= 360) ? 0 : hsva.h,
        s = hsva.s / 100,
        v = hsva.v / 100,
        a = hsva.a,
        h60 = h / 60, matrix = h60 | 0, f = h60 - matrix,
        v255, p, q, t, w;
    if (!s) {
      h = _round(v * 255);
      return { r: h, g: h, b: h, a: a };
    }
    v255 = v * 255,
    p = _round((1 - s) * v255),
    q = _round((1 - (s * f)) * v255),
    t = _round((1 - (s * (1 - f))) * v255),
    w = _round(v255);
    switch (matrix) {
      case 0: return { r: w, g: t, b: p, a: a };
      case 1: return { r: q, g: w, b: p, a: a };
      case 2: return { r: p, g: w, b: t, a: a };
      case 3: return { r: p, g: q, b: w, a: a };
      case 4: return { r: t, g: p, b: w, a: a };
      case 5: return { r: w, g: p, b: q, a: a };
    }
    return { r: 0, g: 0, b: 0, a: a };
  },

  // uuColor.rgba2hsla
  rgba2hsla: function(rgba) { // @param RGBAHash:
                              // @return HSLAHash:
    var r = rgba.r / 255,
        g = rgba.g / 255,
        b = rgba.b / 255,
        max = Math.max(r, g, b),
        min = Math.min(r, g, b),
        diff = max - min,
        h = 0, s = 0, l = (min + max) / 2;

    if (l > 0 && l < 1) {
      s = diff / (l < 0.5 ? l * 2 : 2 - (l * 2));
    }
    if (diff > 0) {
      if (max === r && max !== g) {
        h += (g - b) / diff;
      } else if (max === g && max !== b) {
        h += (b - r) / diff + 2;
      } else if (max === b && max !== r) {
        h += (r - g) / diff + 4;
      }
      h *= 60;
    }
    return { h: h, s: _round(s * 100), l: _round(l * 100), a: rgba.a };
  },

  // uuColor.hsla2rgba - ( h: 0-360, s: 0-100, l: 0-100, a: alpha )
  hsla2rgba: function(hsla) { // @param HSLAHash:
                              // @return RGBAHash:
    var h = (hsla.h === 360) ? 0 : hsla.h,
        s = hsla.s / 100,
        l = hsla.l / 100,
        r, g, b, s1, s2, l1, l2;

    if (h < 120) {
      r = (120 - h) / 60, g = h / 60, b = 0;
    } else if (h < 240) {
      r = 0, g = (240 - h) / 60, b = (h - 120) / 60;
    } else {
      r = (h - 240) / 60, g = 0, b = (360 - h) / 60;
    }
    s1 = 1 - s;
    s2 = s * 2;

    r = s2 * (r > 1 ? 1 : r) + s1;
    g = s2 * (g > 1 ? 1 : g) + s1;
    b = s2 * (b > 1 ? 1 : b) + s1;

    if (l < 0.5) {
      r *= l, g *= l, b *= l;
    } else {
      l1 = 1 - l;
      l2 = l * 2 - 1;
      r = l1 * r + l2;
      g = l1 * g + l2;
      b = l1 * b + l2;
    }
    return { r: _round(r * 255), g: _round(g * 255),
             b: _round(b * 255), a: hsla.a };
  }
};

// --- initialize ---

// --- export ---
window.uuColor = _color; // window.uuColor

})(); // uuColor scope

// === uuCodec ===
// depend: none
/*
uuCodecDataURI.decode(str) - return { mime, data }
uuCodecHex.decode("%00%01") - return [0x00, 0x01]
uuCodecBase64.encode(ByteArray, safe64) - return Base64String/URLSafe64String
uuCodecBase64.decode(str) - return ByteArray
 */
(function() {
var _datauri,   // inner namespace
    _hex,       // inner namespace
    _base64,    // inner namespace
    _b64code =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
    _b64hash = { "=": 0 };

_datauri = {
  // uuCodecDataURI.decode
  //    data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAA...
  //    data:text/css,.picture%20%7B%20background%3A%20none%3B%20%7D
  decode: function(str) { // @param String:
                          // @return Hash: { mime, data }
    var m, mime, data;

    if ( (m = /^data:([\w\/]+)(;base64)?,/.exec(str)) ) {
      mime = m[1];
      data = str.slice(m[0].length);
      data = m[2] ? _base64.decode(decodeURIComponent(data)) // base64
                  : _hex.decode(data); // decodeURI weak("%00")
    }
    return { mime: mime || "", data: data || [] };
  }
};

_hex = {
  // uuCodecHex.decode - "%00%01" to [0x00, 0x01]
  decode: function(hex) { // @param HexString: "%00" + ASCII string
                          // @return ByteArray:
    var rv = [], ri = -1, c = 0, i = 0, iz = hex.length,
        mark = "%".charCodeAt(0);

    if (!hex.length) {
      return [];
    }
    if (hex.length >= 3) {
      if (hex.charAt(hex.length - 1) === "%" || // tail "...%"
          hex.charAt(hex.length - 2) === "%") { // tail "..%x"
        return []; // bad data
      }
    }
    while (i < iz) {
      c = hex.charCodeAt(i++);
      if (c === mark) {
        rv[++ri] = parseInt(hex.charAt(i) + hex.charAt(i + 1), 16);
        i += 2;
      } else {
        rv[++ri] = c;
      }
    }
    return rv;
  }
};

_base64 = {
  // uuCodecBase64.encode - ByteArray to Base64String
  encode: function(ary,         // @param ByteArray: array( [0x0, ... ] )
                   URLSafe64) { // @param Boolean(= true): true = URLSafe
                                // @return Base64String/URLSafe64String:
    var rv = [], pad = 0, code = _b64code, c = 0, i = 0, iz;

    switch (ary.length % 3) {
    case 1: ary.push(0); ++pad;
    case 2: ary.push(0); ++pad;
    }
    iz = ary.length;

    while (i < iz) {
      c = (ary[i++] << 16) | (ary[i++] << 8) | (ary[i++]);
      rv.push(code.charAt((c >>> 18) & 0x3f),
              code.charAt((c >>> 12) & 0x3f),
              code.charAt((c >>>  6) & 0x3f),
              code.charAt( c         & 0x3f));
    }
    switch (pad) {
    case 2: rv[rv.length - 2] = "=";
    case 1: rv[rv.length - 1] = "=";
    }

    if (URLSafe64 === void 0 || URLSafe64) {
      return rv.join("").replace(/\+/g, "-").
                         replace(/\//g, "_").replace(/=/g, "");
    }
    return rv.join("");
  },

  // uuCodecBase64.decode - Base64String to ByteArray
  decode: function(b64) { // @param Base64String/URLSafe64String:
                          // @return ByteArray:
    if (typeof b64 !== "string" || !b64.length) {
      return []; // empty
    }

    // URLBase64Charcter("-", "_") convert to ("+", "/")
    b64 = b64.replace(/-/g, "+").replace(/_/g, "/").replace(/=/g, "");

    if (/[^A-Za-z0-9\+\/]/.test(b64)) {
      return []; // bad data
    }

    var rv = [], pad = 0, hash = _b64hash, c = 0, i = 0, iz;

    switch (b64.length % 4) { // pad length( "=" or "==" or "" )
    case 2: b64 += "="; ++pad;
    case 3: b64 += "="; ++pad;
    }

    iz = b64.length;
    while (i < iz) {                    // 00000000|00000000|00000000
      c = (hash[b64.charAt(i++)] << 18) // 111111  |        |
        | (hash[b64.charAt(i++)] << 12) //       11|1111    |
        | (hash[b64.charAt(i++)] <<  6) //         |    1111|11
        |  hash[b64.charAt(i++)]        //         |        |  111111
      rv.push((c >>> 16) & 0xff, (c >>> 8) & 0xff, c & 0xff);
    }
    rv.length -= [0,1,2][pad]; // cut tail
    return rv;
  }
};

// --- initialize ---
(function() {
  // make hash
  for (var i = 0, iz = _b64code.length; i < iz; ++i) {
    _b64hash[_b64code.charAt(i)] = i;
  }
})();

// --- export ---
window.uuCodecDataURI = _datauri;   // window.uuCodecDataURI
window.uuCodecHex     = _hex;       // window.uuCodecHex
window.uuCodecBase64  = _base64;    // window.uuCodecBase64

})(); // uuCodec scope

// === uuStyle ===
// depend: uuMeta, uuColor
/*
uuStyle.toPixel(elm, value) - return pixel value
uuStyle.getPixel(elm, prop) - return pixel value
uuStyle.getBGImg(elm) - return "http://..." or ""
uuStyle.getBGColor(elm, ancestor = false, toRGBA = false)
                                        - return [color, alpha] or { r,g,b,a }
uuStyle.getOpacity(elm) - return 0.0 ~ 1.0
uuStyle.setOpacity(elm, opacity = 1.0, diff = false)
uuStyle.getViewport() - return { w, h, sx, sy }
uuStyle.getRect(elm) - return { x, y, w, h, ow, oh }
uuStyle.setRect(elm, { x, y, w, h })
 */
(function() {
var _style, // inner namespace
    _mm = uuMeta,
    _win = window,
    _doc = document,
    _int = parseInt,
    _float = parseFloat,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _webkit = _mm.webkit,
    _ieroot = _mm.quirks ? "body" : "documentElement",
    _runstyle = _mm.runstyle,
    TRANSPARENT = "transparent",
    IMPORTANT = "important",
    POSITION = "position",
    ABSOLUTE = "absolute",
    DISPLAY = "display",
    BLOCK = "block",
    LEFT = "left";

_style = {
  // uuStyle.toPixel - covert unit
  //    toPixel(node, 123)    -> 123
  //    toPixel(node, "12px") -> 12
  //    toPixel(node, "12pt") -> 16
  //    toPixel(node, "12em") -> 192
  toPixel: function(elm,          // @param Node: context
                    value,        // @param String/Number:
                    accurately) { // @param Boolena(= false): false = quick
                                  // @return Number: pixel value
    var rv, st, rs, mem1, mem2, mem3, fontSize;

    if (typeof value === "string") {
      if (value.lastIndexOf("px") > 0) { // value is pixel unit
        return _int(value) || 0;
      }

      if (!accurately) {
        // quick
        rv = _float(value);
        if (value.lastIndexOf("pt") > 0) {
          rv *= 4 / 3; // 1.333...
        } else if (value.lastIndexOf("em") > 0) {
          fontSize = (_ie ? elm[_runstyle]
                          : _runstyle(elm, "")).fontSize;
          if (fontSize.lastIndexOf("pt") > 0) { // 12pt * 1.333 = 16px
            rv *= _float(fontSize) * 4 / 3;
          } else {
            rv *= _float(fontSize); // 12px
          }
        }
        return _int(rv) || 0;
      }

      st = elm.style, mem1 = st[LEFT];
      if (_ie) {
        rs = elm.runtimeStyle, mem2 = rs[LEFT]; // keep !important value
        // overwrite
        rs[LEFT] = elm[_runstyle][LEFT];
        st[LEFT] = value;
        // get pixel
        value = st.pixelLeft;
        // restore
        st[LEFT] = mem1;
        rs[LEFT] = mem2;
      } else {
        // overwrite
        if (_webkit) {
          mem2 = st.getPropertyValue(POSITION);
          mem3 = st.getPropertyValue(DISPLAY);
          st.setProperty(POSITION, ABSOLUTE, IMPORTANT);
          st.setProperty(DISPLAY,  BLOCK,    IMPORTANT);
        }
        st.setProperty(LEFT, value, IMPORTANT);
        // get pixel
        value = _int(_runstyle(elm, "")[LEFT]);
        // restore
        st.removeProperty(LEFT);
        st.setProperty(LEFT, mem1, "");
        if (_webkit) {
          st.removeProperty(POSITION);
          st.removeProperty(DISPLAY);
          st.setProperty(POSITION, mem2, "");
          st.setProperty(DISPLAY,  mem3, "");
        }
      }
    }
    return value || 0;
  },

  // uuStyle.getPixel - get pixel value
  //    getPixel(node, "left")
  //    getPixel(node, "width")
  getPixel: function(elm,    // @param Node:
                     prop) { // @param String: style property name
                             // @return Number: pixel value
    function dim(horizontal) {
      var r = elm.getBoundingClientRect();
      return horizontal ? (r.right - r.left) : (r.bottom - r.top);
    }
    var rv;

    if (_ie) {
      switch (prop) {
      case "width":  return elm.clientWidth  || dim(1);
      case "height": return elm.clientHeight || dim(0);
      }
      rv = elm[_runstyle][prop];
      (rv === "auto") && (rv = _style.toPixel(elm, rv));
    } else {
      rv = _runstyle(elm, "")[prop];
    }
    return _int(rv) || 0;
  },

  // uuStyle.getBGImg - get background-image url
  getBGImg: function(elm) { // @param Node:
                            // @return String: "http://..." or ""
    var bg = "backgroundImage", m,
        url = _ie ? (elm.style[bg] || elm[_runstyle][bg])
                  : _runstyle(elm, "")[bg];
    if (url.indexOf(",") < 0) { // skip CSS3 multiple background-image
      if (url) {
        if ( (m = /^url\((.*)\)$/.exec(url)) ) {
          return m[1].replace(/^\s*[\"\']?|[\"\']?\s*$/g, ""); // trim quote
        }
      }
    }
    return "";
  },

  // uuStyle.getBGColor - get background-color (from ancestor)
  // depend: uuColor
  getBGColor:
      function(elm,      // @param Node:
               ancestor, // @param Boolean(= false):
               toRGBA) { // @param Boolean(= false): true = return RGBAHash
                         //                          false = return Array
                         // @return Array: [ColorString, Alpha]
    var bgc = "backgroundColor", n = elm, color = TRANSPARENT;

    if (!ancestor) {
      return _ie ? (n.style[bgc] || n[_runstyle][bgc])
                 : _runstyle(n, "")[bgc];
    }
    while (n && n !== _doc && color === TRANSPARENT) {
      if ((_ie && n[_runstyle]) || !_ie) {
        color = _ie ? n[_runstyle][bgc]
                    : _runstyle(n, "")[bgc];
      }
      n = n.parentNode;
    }
    if (toRGBA) {
      return (color === TRANSPARENT) ? { r: 255, g: 255, b: 255, a: 1 }
                                     : uuColor.parse(color, 1);
    }
    return (color === TRANSPARENT) ? ["white", 1]
                                   : uuColor.parse(color);
  },

  // uuStyle.getOpacity - get opacity value(from 0.0 to 1.0)
  getOpacity: function(elm) { // @param Node:
                              // @return Number: float(from 0.0 to 1.0)
    var v = elm.style.opacity,
        ftm = (v === "" || v === void 0); // first time

    return _float(ftm ? (_ie ? 1 : _runstyle(elm, "").opacity) : v);
  },

  // uuStyle.setOpacity - set opacity value(from 0.0 to 1.0)
  setOpacity: function(elm,    // @param Node:
                       opa,    // @param Number(= 1.0): float(from 0.0 to 1.0)
                       diff) { // @param Boolean(= false):
    opa = (opa === void 0) ? 1 : _float(opa);
    var st = elm.style, v = st.opacity,
        ftm = (v === "" || v === void 0); // first time

    v = _float(ftm ? (_ie ? 1 : _runstyle(elm, "").opacity) : v);
    diff && (opa += v);
    if (opa > 0.999) {
      opa = 1;
    } else if (opa < 0.001) {
      opa = 0;
    }

    if (v !== opa) {
      st.opacity = opa; // update / bond
      if (_ie) {
        if (ftm) {
          st.filter += " alpha(opacity=0)";
          st.zoom = st.zoom || "1"; // IE6, IE7: force "hasLayout"
        }
        elm.filters.alpha.opacity = (opa * 100) | 0;

        if (v === 1) { // curt === 1 and opa !== 1
          elm.filters.alpha.enabled = true;
        } else if (opa === 1) { // curt !== 1 and opa === 1
          elm.filters.alpha.enabled = false;
        }
      }
    }
  },

  // uuStyle.getViewport - get viewport dimension and scroll offset
  getViewport: function() { // @return { w, h, sx, sy }
    var e = _win;

    if (_ie) {
      e = _doc[_ieroot];
      return { w: e.clientWidth  || e.scrollWidth,
               h: e.clientHeight || e.scrollHeight,
               sx: e.scrollLeft,
               sy: e.scrollTop };
    }
    // "window.pageXOffset" alias "window.scrollX" in gecko, webkit
    return { w: e.innerWidth,
             h: e.innerHeight,
             sx: e.pageXOffset,
             sy: e.pageYOffset };
  },

  // uuStyle.getRect - get element absolute position and rectangle
  getRect: function(elm) { // @param Node:
                           // @return Hash: { x, y, w, h, ow, oh }
    var e, x = 0, y = 0, w = 0, h = 0, fix = 0, rect, vp;

    if (elm.getBoundingClientRect) {
      // get relative position
      rect = elm.getBoundingClientRect();
      fix = (_ie && elm.parentNode === _doc.body) ? 2 : 0;
      // to absolute position
      vp = _style.getViewport();
      x = rect.left + vp.sx - fix;
      y = rect.top  + vp.sy - fix;
      w = rect.right - rect.left;
      h = rect.bottom - rect.top;
    } else {
      // get absolute position(for Fx2)
      e = elm;
      while (e) {
        x += e.offsetLeft || 0;
        y += e.offsetTop  || 0;
        e  = e.offsetParent;
      }
    }
    return {
      // element position(absolute)
      x: x,
      y: y,
      // element dimension(style.width + padding)
      w: elm.clientWidth  || w,
      h: elm.clientHeight || h,
      // element dimension(style.width + padding + border)
      ow: elm.offsetWidth,
      oh: elm.offsetHeight
    };
  },

  // uuStyle.setRect - set element rect
  setRect: function(elm,    // @param Node:
                    rect) { // @param Hash: { x, y, w, h }
    var s = elm.style;

    if (_ie || _opera) {
      if ("x" in rect) { s.pixelLeft   = rect.x; }
      if ("y" in rect) { s.pixelTop    = rect.y; }
      if ("w" in rect) { s.pixelWidth  = rect.w > 0 ? rect.w : 0; }
      if ("h" in rect) { s.pixelHeight = rect.h > 0 ? rect.h : 0; }
    } else {
      if ("x" in rect) { s.left   = rect.x + "px"; }
      if ("y" in rect) { s.top    = rect.y + "px"; }
      if ("w" in rect) { s.width  = (rect.w > 0 ? rect.w : 0) + "px"; }
      if ("h" in rect) { s.height = (rect.h > 0 ? rect.h : 0) + "px"; }
    }
  }
};

// --- initialize ---

// --- export ---
_win.uuStyle = _style; // window.uuStyle

})(); // uuStyle scope

// === uuImage ===
// depend: none
/*
uuImage.load(url, callback)
uuImage.getActualDimension(image) - return { w, h }
 */

(function() {
var _image, // inner namespace
    _ie = document.uniqueID;

_image = {
  // uuImage.load - delay loader
  load: function(url,        // @param String:
                 callback) { // @param Function: callback(img, state, dim)
                             //     img: image object
                             //     state: 0(loading...), 1(loaded), -1(error)
                             //     dim: { w, h }
    var img = new Image();
    img.state = 0; // bond

    img.clear = function() { // bond
      img.onerror = "";
      img.onload = "";
      img = void 0; // fix memory leak in IE6
    };
    img.onerror = function() {
      img.state = -1; // error
      callback &&
          callback(img, img.state, { w: 0, h: 0 });
    };
    img.onload = function() {
      if (img.complete ||
          img.readyState === "complete") { // IE8
        img.state = 1; // ok
        setTimeout(function() {
          callback &&
              callback(img, img.state, { w: img.width,
                                         h: img.height });
        }, 0);
      }
    };
    img.setAttribute("src", url);
  },

  // uuImage.getActualDimension
  // http://d.hatena.ne.jp/uupaa/20090602/1243933843
  getActualDimension: function(image) { // @param HTMLImageElement
                                        // @return Hash: { w, h }
    var run, mem, w, h, key = "actual";

    // for Firefox, Safari, Chrome
    if ("naturalWidth" in image) {
      return { w: image.naturalWidth, h: image.naturalHeight };
    }

    if ("src" in image) { // HTMLImageElement
      if (image[key] && image[key].src === image.src) {
        return image[key];
      }
      if (_ie) { // for IE
        run = image.runtimeStyle;
        mem = { w: run.width, h: run.height }; // keep runtimeStyle
        run.width  = "auto"; // override
        run.height = "auto";
        w = image.width;
        h = image.height;
        run.width  = mem.w; // restore
        run.height = mem.h;
      } else { // for Opera
        mem = { w: image.width, h: image.height }; // keep current style
        image.removeAttribute("width");
        image.removeAttribute("height");
        w = image.width;
        h = image.height;
        image.width  = mem.w; // restore
        image.height = mem.h;
      }
      return image[key] = { w: w, h: h, src: image.src }; // bond
    }
    // HTMLCanvasElement
    return { w: image.width, h: image.height };
  }
};

// --- initialize ---

// --- export ---
window.uuImage = _image;

})();


// === uuStyleSheet ===
// depend: uuMeta
/*
uuStyleSheet.create(id) - return object
uuStyleSheet.insertRule(id, expr, decl, index = last) - return index
uuStyleSheet.removeRule(id, index = last)
uuStyleSheet.removeAllRules(id)
 */
(function() {
var _ss, // inner namespace
    _mm = uuMeta,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _sheet = {}, // private style sheet
    TRIM = /^\s+|\s+$/g;

_ss = {
  // uuStyleSheet.create - create StyleSheet
  create: function(id) { // @param String: StyleSheet id
                         // @return StyleSheet: new or already object
    if (id in _sheet) { // already exists
      return _sheet[id];
    }
    if (_ie) {
      _sheet[id] = _doc.createStyleSheet();
    } else {
      var elm = _doc.createElement("style");
      elm.appendChild(_doc.createTextNode(""));
      _sheet[id] = _doc.getElementsByTagName("head")[0].appendChild(elm);
    }
    return _sheet[id];
  },

  // uuStyleSheet.insertRule - insert CSS rule
  insertRule: function(id,      // @param String: StyleSheet id
                       expr,    // @param String: css selector
                       decl,    // @param String: css declaration
                       index) { // @param Number(= last): insertion position
                                // @return Number: inserted rule index
                                //                 or -1(error)
    if (!(id in _sheet)) { return -1; }

    var r = _sheet[id];
    if (_ie) {
      index = index === void 0 ? r.rules.length : index;
      r.addRule(expr.replace(TRIM, ""), decl.replace(TRIM, ""), index);
    } else {
      index = index === void 0 ? r.sheet.cssRules.length : index;
      index = r.sheet.insertRule(expr + "{" + decl + "}", index);
      if (_mm.opera && _mm.uaver < 9.5) { // Opera90 bug
        index = r.sheet.cssRules.length - 1;
      }
    }
    return index;
  },

  // uu.style.removeRule - remove CSS rule
  removeRule: function(id,      // @param String: StyleSheet id
                       index) { // @param Number(= last): deletion position
    if (!(id in _sheet)) { return; }

    var r = _sheet[id];
    if (_ie) {
      index = (index === void 0) ? r.rules.length - 1 : index;
      (index >= 0) && r.removeRule(index);
    } else {
      index = (index === void 0) ? r.sheet.cssRules.length - 1 : index;
      (index >= 0) && r.sheet.deleteRule(index);
    }
  },

  // uuStyleSheet.removeAllRules - remove all CSS rules
  removeAllRules: function(id) {
    if (!(id in _sheet)) { return; }

    var r = _sheet[id],
        i = _ie ? r.rules.length
                : r.sheet.cssRules.length;
    while (i--) {
      _ie ? r.removeRule(i)
          : r.sheet.deleteRule(i);
    }
  }
};

// --- initialize ---

// --- export ---
_win.uuStyleSheet = _ss;

})(); // uuStyleSheet scope

// === uuQuery ===
// depend: uuMeta, uuStyleSheet, [uuStyle], [uuColor]
/*
uuQuery(expr, context = document) - return NodeArray
uuQuery.id(expr) - return Node
uuQuery.tag(expr, context = document) - return NodeArray
uuQuery.className(expr, context = document) - return NodeArray
 */
(function() {
var _query, // inner namespace
    _mm = uuMeta,
    _ss = uuStyleSheet,
    _win = window,
    _doc = document,
    _visited = !!_win.UUQUERY_ENABLE_VISITED, // 1 = :visited activate
    _int = parseInt,
    _float = parseFloat,
    _ie = _mm.ie,
    _gecko = _mm.gecko,
    _iemode8 = _mm.iemode8,
    _innerText = _gecko ? "textContent" : "innerText",
    _runstyle = _mm.runstyle,
    _ssid = "uuQuery", // StyleSheet ID
    _toArray = _mm.toArray,
    // root - ref document root element (<html>)
    _rootElement = _doc.documentElement ||
                   _doc.getElementsByTagName("html")[0],
    _headElement = _doc.getElementsByTagName("head")[0],
    // --- content-type cache (1: HTML, 2: XML) ---
    _contentTypeCache = { /* quid: contentType */ },
    // tag dict( { a: "A", A: "A", ... } )
    _htmlTag = {},
    _xmlTag = {},
    UNIQUEID = "uuqid",
    QUICK_STATIC = {
      "*":      function(ctx) { return _query.tag("*", ctx); },
      "*:root": function() { return [_rootElement]; }, // fix #27 (*:root)
      ":root":  function() { return [_rootElement]; }, // fix #27 (*:root)
      "* :root":function() { return []; }, // fix #27b (* :root)
      "* html": function() { return []; }, // fix #27b (* html) IE6 CSS Star Hack
      html:     function() { return [_rootElement]; },
      head:     function() { return [_headElement]; },
      body:     function() { return [_doc.body]; },
      ":link":  function() { return _toArray(_doc.links); } // spoof
    },
    // :after :before :contains :digit :first-letter :first-line :link
    // :negative :playing :target :visited  !=  ?=  /=  <=  >=  &=  {  }
    REJECT_API  = /(:(a|b|co|dig|first-l|li|ne|p|t|v))|!=|\/=|<=|>=|&=|\{/, // }
    TRIM_QUOTE  = /^\s*["']?|["']?\s*$/g,
    QUICK_QUERY = /^(?:\*?(\.|#)([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)|(\w+)(?:\s*,\s*(\w+)(?:\s*,\s*(\w+))?)?|(\w+)\s+(\w+)(?:\s+(\w+))?)$/i,
    QUICK_HEAD  = /^#([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)\b(?![#\.:\[])|^((?:\.[a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)+)$/i, // ]
    QUICK_COMMA = /^[^"'\(\)]*,/,
    ROOT_REJECT = /[a-z]+\-(child|type)$/,
    ID_OR_CLASS = /^[#\.]([a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*)/i,
    CHILD       = /^\s*(?:([>+~])\s*)?(\*|\w*)/,
    GROUP       = /^\s*,\s*/,
    PSEUDO      = /^(?::(not)\((?:(\*)|(\w+)|[#\.][a-z_\u00C0-\uFFEE\-][\w\u00C0-\uFFEE\-]*|\[\s*(?:[^~\^$*|=!\/\s]+\s*[~\^$*|!\/]?\=\s*(["'])?.*?\4i?|[^\]\s]+)\s*\]|:contains\((["'])?.*?\5\)|::?[\w\-]+(?:\([^\u0029]+\))?)\)|:contains\((["'])?(.*?)\6\)|::?([\w\-]+)(?:\((.*?)\))?)/i,
    ATTR        = /^\[\s*(?:([^~\^$*|=!\/\s]+)\s*([~\^$*|!\/]?\=)\s*(["'])?(.*?)\3(i?)|([^\]\s]+))\s*\]/,
    STYLE       = /^\{\s*([^\^$*=!<>&\/\s]+)\s*(:|[\^$*!<>&\/]?\=)\s*(["'])?(.*?)\3(i?)\s*\}/,
    NTH_ANB     = /^((even)|(odd)|(1n\+0|n\+0|n)|(\d+)|((-?\d*)n([+\-]?\d*)))$/,
    JOINT1      = { ">": 1, "+": 2, "~": 3 },
    JOINT2      = { "#": 1, ".": 2, ":": 3, "[": 4, "{": 5 }, // }]
    ATTR_ALIAS  = { "class": "className", "for": "htmlFor" },
    ATTR_IE_BUG = { href: 1, src: 1 },
    ATTR_OPERATOR = { "=": 1, "!=": 2, "*=": 3, "^=": 4,
                              "$=": 5, "~=": 6, "|=": 7, "/=": 8 },
    ATTR_CASESENS = { title: 0, id: 0, name: 0, "class": 0, "for": 0 },
    EX_PROP_KIND= { color: 1, backgroundColor: 1, opacity: 2, width: 3, height: 4,
                    left: 5, top: 5, right: 5, bottom: 5, backgroundImage: 6 },
    EX_OPERATOR = { ":": 1,     // E{prop : value}             match
                    "=": 1,     // E{prop = value}             match
                    "!=": 2,    // E{prop != value}            not match
                    "*=": 3,    // E{prop *= value}            match somewhere
                    "^=": 4,    // E{prop ^= value}            match head
                    "$=": 5,    // E{prop $= value}            match tail
                    "/=": 8,    // E{prop /= "value"}          match Regexp
                    ">=": 9,    // E{prop >= value}            more than
                    "<=": 10,   // E{prop <= value}            less than
                    "&=": 11    // E{prop &= value1 ~ value2}  from value1 to value2
                  },
    DUMMY = function() { return []; },
    filters = {
      "first-child":    [0x01, childFilter],
      "last-child":     [0x02, childFilter],
      "only-child":     [0x03, childFilter],
      "nth-child":      [0x04, nthChildFilter],
      "nth-last-child": [0x05, nthChildFilter],
      "nth-of-type":    [0x06, nthOfTypeFilter],
      "nth-last-of-type":
                        [0x07, nthOfTypeFilter],
      "first-of-type":  [0x09, ofTypeFilter],
      "last-of-type":   [0x0a, ofTypeFilter],
      enabled:          [0x0b, simpleFilter],
      disabled:         [0x0c, simpleFilter],
      checked:          [0x0d, simpleFilter],
      link:             [0x0e, _visited ? visitedFilter : link],
      visited:          [0x0f, _visited ? visitedFilter : DUMMY],
      hover:            [0x10, actionFilter],
      focus:            [0x11, actionFilter],
      empty:            [0x12, empty],
      lang:             [0x13, lang],
      "only-of-type":   [0x14, onlyOfType],
      // [0x15] reserved.
      root:             [0x16, root],
      target:           [0x17, target],
      contains:         [0x18, contains],
      digit:            [0x40, extendFilter],
      negative:         [0x41, extendFilter],
      tween:            [0x42, extendFilter],
      playing:          [0x43, extendFilter],
      // bit information
      //    0x100: use flag
      //    0x200: none operation flag
      //    0x400: :not exclude flag
      //    0x800: need double-semicolon(::) flag
      before:           [0xf00, null],
      after:            [0xf00, null],
      "first-letter":   [0xf00, null],
      "first-line":     [0xf00, null]
    };

if (_visited) {
  delete QUICK_STATIC[":link"];
}

// uuQuery - query css
_query = function(expr,      // @param String: "css > rule"
                  context) { // @param Node(= document): query context
                             // @return NodeArray( [Node, Node, ...] )
                             //         /EmptyArray( [] )
  if (_doc.querySelectorAll) {
    if (!REJECT_API.test(expr)) {
      try {
        return _toArray((context || _doc).querySelectorAll(expr));
      } catch(err) {} // case: extend pseudo class / operators
    }
  }
  return querySelectorAll(expr.replace(/^\s+|\s+$/g, ""), context || _doc);
};

// uuQuery.id - query id
_query.id = function(expr) { // @param String: id
                             // @return Node/null
  return _doc.getElementById(expr);
};

// uuQuery.tag - query tagName
_query.tag = (function() {
  if (!_ie) {
    return function(expr,      // @param String: "*" or "tag"
                    context) { // @param Node(= document): query context
                               // @return NodeArray( [Node, Node, ...] )
                               //         /EmptyArray( [] )
      return Array.prototype.slice.call(
                  (context || _doc).getElementsByTagName(expr));
    }
  }
  return function(expr, context) {
    var nodeList = (context || _doc).getElementsByTagName(expr),
        rv = [], ri = -1, v, i = 0;
    if (expr !== "*") {
      while ( (v = nodeList[i++]) ) {
        rv[++ri] = v;
      }
    } else { // ie: getElementsByTagName("*") has comment nodes
      while ( (v = nodeList[i++]) ) {
        (v.nodeType === 1) && (rv[++ri] = v);
      }
    }
    return rv;
  };
})();

// uuQuery.className - query className
_query.className = (function() {
  if (_doc.getElementsByClassName) {
    return function(expr,      // @param JointString: "class", "class1, ..."
                    context) { // @param Node(= document): query context
                               // @return NodeArray( [Node, Node, ...] )
                               //         /EmptyArray( [] )
      return Array.prototype.slice.call(
                  (context || _doc).getElementsByClassName(expr));
    };
  }
  return function(expr, context) {
    var nodeList = (context || _doc).getElementsByTagName("*"),
        name = expr.replace(/^\s+|\s+$/g, "").split(/\s+/),
        rv = [], ri = -1, v, match, c, i = 0, nz = name.length, rex,
        urv = [], uri = -1, unq = {}, u = 0;

    if (nz > 1) { // #fix 170b
      while ( (v = name[u++]) ) {
        if (!(v in unq)) {
          unq[v] = 1;
          urv[++uri] = v;
        }
      }
      name = urv, nz = uri + 1;
    }
    // /(?:^| )(AA|BB|CC)(?:$|(?= ))/g
    rex = RegExp("(?:^| )(" + name.join("|") + ")(?:$|(?= ))", "g");

    while ( (v = nodeList[i++]) ) {
      c = v.className;
      if (c) {
        match = c.match(rex); // NG: match = rex.exec(c);
        (match && match.length >= nz) && (rv[++ri] = v);
      }
    }
    return rv;
  };
})();

function quickQuery(expr, match, context) {
  var rv = [], ri = -1, unq = {}, uid,
      m1, m2, m3, nodeList1, nodeList2, nodeList3,
      v, i, j, k, iz, jz, kz;

  // "#id" or ".class"
  if (match[1]) {
    if (match[1] === ".") {
      return _query.className(match[2], context);
    }
    nodeList1 = (context.ownerDocument || _doc).getElementById(match[2]);
    return nodeList1 ? [nodeList1] : [];
  }

  // "E" or "E,F" or "E,F,G"
  if (match[3]) {
    m1 = match[3], m2 = match[4], m3 = match[5];
    if (/^\d+$/.test(m1) || /^\d+$/.test(m2) || /^\d+$/.test(m3)) {
      throw expr + " syntax error";
    }

    unq[m1] = 1, nodeList1 = _toArray(context.getElementsByTagName(m1));
    if (m2 && !(m2 in unq)) {
      unq[m2] = 1, nodeList2 = _toArray(context.getElementsByTagName(m2));
    }
    if (m3 && !(m3 in unq)) {
      unq[m3] = 1, nodeList3 = _toArray(context.getElementsByTagName(m3));
    }
    return [].concat(nodeList1, nodeList2 || [], nodeList3 || []);
  }

  // "E F" or "E F G"
  m1 = match[6], m2 = match[7], m3 = match[8];

  nodeList1 = context.getElementsByTagName(m1); // "E"
  for (i = 0, iz = nodeList1.length; i < iz; ++i) {
    nodeList2 = nodeList1[i].getElementsByTagName(m2); // "E F"
    for (j = 0, jz = nodeList2.length; j < jz; ++j) {
      if (m3) {
        nodeList3 = nodeList2[j].getElementsByTagName(m3); // "E F G"
        for (k = 0, kz = nodeList3.length; k < kz; ++k) {
          v = nodeList3[k];
          uid = v[UNIQUEID] || (v[UNIQUEID] = ++_win.uuqid);
          if (!(uid in unq)) {
            rv[++ri] = v;
            unq[uid] = 1;
          }
        }
      } else {
        v = nodeList2[j];
        uid = v[UNIQUEID] || (v[UNIQUEID] = ++_win.uuqid);
        if (!(uid in unq)) {
          rv[++ri] = v;
          unq[uid] = 1;
        }
      }
    }
  }
  return rv;
}

function querySelectorAll(expr, context) {
  var _contentType, _tags, // alias
      // --- double registration guard ---
      uid,        // unique-id
      guard = {}, // global guard
      unq   = {}, // local guard
      mixed = 0,  // 1: mixed
      // --- result and context elements ---
      rv  = [], ri, r,
      ctx = [context],
      // --- loop out flag --
      lastExpr1,  // last expr for outer loop
      lastExpr2,  // last expr for inner loop
      // --- iterator and loop counter ---
      i, j, iz,
      // --- work ---
      withComma = expr.indexOf(",") >= 0, // with comma(",")
      tag,        // the E or F or universal selector("*")
      isUniversal,// true: tag is universal selector("*")
      joint,      // >+~_#.:[     // ]
      nodeList, needle, pseudo, v, w, operator, match, negate = 0;

  if (/^[>+~]|[>+~*]{2}|[>+~]$/.test(expr)) {
    throw expr + " syntax error";
  }

  // --- Quick phase ---
  if (!withComma && expr in QUICK_STATIC) { // "*" ":root" "body" ...
    return QUICK_STATIC[expr](context);
  }
  if ( (match = QUICK_QUERY.exec(expr)) ) { // "#id" ".class" "E" "E F" "E,F"...
    return quickQuery(expr, match, context);
  }
  if (withComma && QUICK_COMMA.test(expr)) { // split("#id, .class, E")
    w = expr.split(","); // "expr, expr, expr"
    for (i = 0, iz = w.length; i < iz; ++i) {
      v = w[i].replace(/^\s+|\s+$/g, "");
      if (!v) {
        throw expr + " syntax error";
      }
      r = querySelectorAll(v, context);
      mixin(r, rv, unq);
    }
    return rv;
  }
  if (!withComma) {
    if ( (match = QUICK_HEAD.exec(expr)) ) {
      if (match[1]) {
        w = _doc.getElementById(match[1]);
        ctx = w ? [w] : [];
      } else {
        v = match[2].replace(/\./g, " "); // ".class.class" -> " class class"
        return _query.className(v, context);
      }
      expr = expr.slice(match[0].length);
    }
  }

  // init tag set
  uid = context[UNIQUEID] || (context[UNIQUEID] = ++_win.uuqid);
  _contentType = _contentTypeCache[uid] ||
                    (_contentTypeCache[uid] = getContentType(context));
  _tags = _contentType === 1 ? _htmlTag : _xmlTag;

  // --- Generic phase ---
  while (expr && expr !== lastExpr1) { // outer loop
    lastExpr1 = expr;

    r = [], ri = -1, unq = {}, i = 0, iz = ctx.length;

    // "E > F"  "E + F"  "E ~ F"  "E"  "E F" phase
    if ( (match = CHILD.exec(expr)) ) {
      tag = match[2];
      tag = tag ? (_tags[tag] || addTag(tag, _contentType)) : "*";
      isUniversal = tag === "*"; // true: tag is universal selector

      if (match[1]) {
        joint = JOINT1[match[1]];

        if (joint === 1) { // 1: "E > F"
          for (; i < iz; ++i) {
            for (v = ctx[i].firstChild; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {
                  r[++ri] = v;
                }
              }
            }
          }
        } else if (joint === 2) { // 2: "E + F"
          for (; i < iz; ++i) {
            for (v = ctx[i].nextSibling; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                w = v.tagName;
                if (_ie && !w.indexOf("/")) { continue; } // fix #25
                if (isUniversal || w === tag) {
                  r[++ri] = v;
                }
                break;
              }
            }
          }
        } else { // 3: "E ~ F"
          for (; i < iz; ++i) {
            for (v = ctx[i].nextSibling; v; v = v.nextSibling) {
              if (v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {

                  uid = v[UNIQUEID] || (v[UNIQUEID] = ++_win.uuqid);
                  if (uid in unq) {
                    break;
                  } else {
                    r[++ri] = v;
                    unq[uid] = 1;
                  }
                }
              }
            }
          }
        }
      } else {
        // >+~ is not found
        if (iz === 1) {
          r = _query.tag(tag, ctx[0]);
        } else {
          for (; i < iz; ++i) {
            nodeList = ctx[i].getElementsByTagName(tag);

            // tag("*") has text/comment node(in IE)
            j = 0;
            while ( (v = nodeList[j++]) ) {
              if (!_ie || !isUniversal || v.nodeType === 1) {
                if (isUniversal || v.tagName === tag) {

                  uid = v[UNIQUEID] || (v[UNIQUEID] = ++_win.uuqid);
                  if (!(uid in unq)) {
                    r[++ri] = v;
                    unq[uid] = 1;
                  }
                }
              }
            }
          }
        }
      }
      ctx = r;
      expr = expr.slice(match[0].length);
    }

    // Attribute, Class, Pseudo, ID phase
    while (expr && expr !== lastExpr2) { // inner loop
      lastExpr2 = expr;
      match = null;

      r = [], ri = -1, i = 0;

      joint = JOINT2[expr.charAt(0)] || 9; // 9: dummy

      switch (joint) {
      case 1: // 1: "#id"
        if ( (match = ID_OR_CLASS.exec(expr)) ) {
          needle = match[1]; // "id"

          if (_contentType === 1) { // 1:html (match id or name)
            while ( (v = ctx[i++]) ) {
              if (((w = v.id || v.name) && (w === needle)) ^ negate) {
                r[++ri] = v;
              }
            }
          } else { // 2: xml (match id)
            while ( (v = ctx[i++]) ) {
              if (((w = v.id) && (w === needle)) ^ negate) {
                r[++ri] = v;
              }
            }
          }
        }
        break;

      case 2: // 2: ".class"
        if ( (match = ID_OR_CLASS.exec(expr)) ) {
          needle = (" " + match[1] + " "); // " className "

          while ( (v = ctx[i++]) ) {
            if (((w = v.className) &&
                ((" " + w + " ").indexOf(needle) >= 0)) ^ negate) {
              r[++ri] = v;
            }
          }
        }
        break;

      case 3: // 3: pseudo
        if ( (match = PSEUDO.exec(expr)) ) {
          if ( (iz = ctx.length) ) {
            if (match[1]) { // :not(...)
              if (negate) {
                throw ":not(:not(...)) syntax error";
              }
              if (match[2]) { // :not(*)
                break;
              }
              if (match[3]) { // ':not(div)' -> match[3] = "div"
                tag = match[3];
                tag = _tags[tag] || addTag(tag, _contentType);
                while ( (v = ctx[i++]) ) {
                  (v.tagName !== tag) && (r[++ri] = v);
                }
                break;
              }
              w = expr.slice(match[0].length); // remain expr
              expr = match[0].replace(/^:not\(\s*|\s*\)$/g, "") + w;
              ++negate;
              continue;
            } else {
              pseudo = match[8] || "contains";

              // ":root:xxx-child" or ":root:xxx-type" -> not match
              // ":root:not(:first-child)"             -> match root element
              if (iz === 1 && ctx[0] === _rootElement
                           && ROOT_REJECT.test(pseudo)) {
                r = negate ? [_rootElement] : [];
              } else {
                if ( !(v = filters[pseudo]) ) {
                  throw ":" + pseudo + " unsupported";
                }
                w = v[0];
                if (w & 0x100) {
                  if ((w & 0x800) && !/^::/.test(expr)) {
                    throw match[0] + " syntax error(need ::)";
                  }
                  if ((w & 0x400) && negate) {
                    throw ":not(" + match[0] + ") syntax error" +
                          "(exclude pseudo-element)";
                  }
                  if (w & 0x200) { // 0x100 is none operation
                    r = negate ? [] : ctx;
                    break;
                  }
                }
                r = v[1].call(this, w, negate, ctx, pseudo,
                              match[9] || match[7], _tags, _contentType);
              }
            }
          }
        }
        break;

      case 4: // 4: Attr "[A=V]" or "[A]"
        if ( (match = ATTR.exec(expr)) ) {
          if (match[6]) { // "[A]"
            needle = match[6];

            while ( (v = ctx[i++]) ) {
              if (_ie) {
                w = v.getAttributeNode(needle);
                if ((w && w.specified) ^ negate) {
                  r[++ri] = v;
                }
              } else if (v.hasAttribute(needle) ^ negate) {
                r[++ri] = v;
              }
            }
          } else { // "[A=V]"
            // fix #Acid2 [class=second two]
            if (!match[3] &&
                match[4].indexOf(" ") >= 0 &&
                match[4].replace(/\\ /, "").indexOf(" ") >= 0) {
              throw match[0] + " syntax error";
            }
            needle = match[4].replace(/^\s*["']|["']\s*$/g, "");
            operator = ATTR_OPERATOR[match[2]];
            if (!operator) {
              throw match[0] + " unsupported";
            }
            w = match[5] || ""; // regexp flag

            if (operator === 8) { // 8: "/=" is regexp operator
              needle = RegExp(needle, w);
            } else {
              // fix [class=i] -> match[4] = "", match[5] = "i"
              w && (needle += w);
            }
            r = judgeAttr(negate, ctx, match[1], operator, needle);
          }
        }
        break;

      case 5: // 5: Style "{S=V}" or "{S}"
        if (_win.uuStyle) {
          if ( (match = STYLE.exec(expr)) ) {
            r = styleQuery(negate, ctx, match);
          }
        }
      }

      if (match) {
        ctx = r;
        expr = expr.slice(match[0].length);
        negate = 0;
      }
    }

    // "E,F" phase
    if (withComma && expr && (match = GROUP.exec(expr)) ) {
      ++mixed;
      mixin(ctx, rv, guard);
      ctx = [context];
      lastExpr1 = lastExpr2 = "";
      expr = expr.slice(match[0].length);
    }
  }

  if (expr.length) {
    throw expr + " unsupported";
  }
  return mixed ? mixin(ctx, rv, guard) : ctx;
}

// mix results
function mixin(ctx, rv, guard) {
  var ri = rv.length - 1, i = 0, v, uid;

  while ( (v = ctx[i++]) ) {
    uid = v[UNIQUEID] || (v[UNIQUEID] = ++_win.uuqid);

    if (!(uid in guard)) {
      rv[++ri] = v;
      guard[uid] = 1;
    }
  }
  return rv;
}

function getContentType(context) {
  var owner = context.ownerDocument || _doc,
      p = owner.createElement("p"),
      P = owner.createElement("P");
  // see http://d.hatena.ne.jp/uupaa/20081010/1223630689 [THX! id:os0x]
  return p.tagName === P.tagName ? 1 : 2; // 1: HTMLDocument, 2: XMLDocument
}

function addTag(tag, contentType) {
  var lo = tag.toLowerCase(),
      up = tag.toUpperCase();
  if (!(lo in _htmlTag)) {
    _xmlTag[up] = _htmlTag[lo] = _htmlTag[up] = up;
    _xmlTag[lo] = lo;
  }
  return contentType === 1 ? up : tag;
}

// [attr operator "value"]
function judgeAttr(negate, elms, attr, operator, value) {
  var rv = [], ri = -1, r, e, v = value, i = 0, rex,
      attrFlag = 0, // attrFlag: ie only
      isInsens = !(attr in ATTR_CASESENS); // true: case insensitive

  if (_ie) {
    if (_iemode8 || ATTR_IE_BUG[attr]) { // fix a[href^="#"]
      attrFlag = 2;
    } else {
      attr = ATTR_ALIAS[attr] || attr;
    }
  }

  if (operator < 3) { // [attr = value] or [attr != value]
    --operator;
    v = v.replace(/\\/, ""); // fix #Acid2 [class=second\ two]
    if (isInsens) {
      v = v.toLowerCase();
    }
    while ( (e = elms[i++]) ) {
      if ( (r = e.getAttribute(attr, attrFlag)) ) {
        if (isInsens) {
          r = (r + "").toLowerCase();
        }
        ((v == r) ^ operator ^ negate) && (rv[++ri] = e);
      }
    }
  } else {
    switch (operator) {
    case 3: rex = v; break;                           // [attr *= value]
    case 4: rex = "^" + v; break;                     // [attr ^= value]
    case 5: rex = v + "$"; break;                     // [attr $= value]
    case 6: if (v.indexOf(" ") >= 0) { return rv; }   // fix #7b
            rex = "(?:^| )" + v + "(?:$| )"; break;   // [attr ~= value]
    case 7: rex = "^" + v + "\\-|^" + v + "$"; break; // [attr |= value]
    }
    if (rex) {
      v = RegExp(rex, isInsens ? "i": "");
    }
    while ( (e = elms[i++]) ) {
      r = e.getAttribute(attr, attrFlag);
      if ((r && v.test(r)) ^ negate) {
        rv[++ri] = e;
      }
    }
  }
  return rv;
}

// :first-child  :last-child  :only-child
function childFilter(fid, negate, elms) {
  var rv = [], ri = -1, i = 0, v, c, f,
      iter1 = "previousSibling",
      iter2 = "nextSibling";

  while ( (v = elms[i++]) ) {
    f = 0;
    // first-child
    if (fid & 1) {
      for (c = v[iter1]; c; c = c[iter1]) {
        if (c.nodeType === 1) {
          ++f;
          break;
        }
      }
    }
    // last-child
    if (!f && fid & 2) {
      for (c = v[iter2]; c; c = c[iter2]) {
        if (c.nodeType === 1) {
          ++f;
          break;
        }
      }
    }
    if ((!f) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :nth-child  :nth-last-child
function nthChildFilter(fid, negate, elms, pseudo, value, tags, contentType) {
  if (value === "n") {
    return negate ? [] : elms;
  }
  // 0x4 = nth-child, 0x5 = nth-last-child
  var v = elms[0].tagName,
      tag = tags[v] || addTag(v, contentType),
      rv = [], ri = -1, i = 0, iz = elms.length, uid, unq = {},
      pn, cn, idx, ok,
      iter1 = (fid === 0x5) ? "lastChild" : "firstChild",
      iter2 = (fid === 0x5) ? "previousSibling" : "nextSibling",
      f = nth(value), a = f.a, b = f.b, k = f.k;

  for (; i < iz; ++i) {
    pn = elms[i].parentNode;
    uid = pn[UNIQUEID] || (pn[UNIQUEID] = ++_win.uuqid);

    if (!(uid in unq)) {
      unq[uid] = 1;
      idx = 0;

      for (cn = pn[iter1]; cn; cn = cn[iter2]) {
        if (cn.nodeType === 1) {
          ++idx;

          ok = 0;
          switch (k) {
          case 1:  ok = (idx === b); break;
          case 2:  ok = (idx >= b); break;
          case 3:  ok = (!((idx - b) % a) && (idx - b) / a >= 0); break;
          default: ok = (idx <= b);
          }
          (ok ^ negate) && cn.tagName === tag && (rv[++ri] = cn);
        }
      }
    }
  }
  return rv;
}

// :nth-of-type  :nth-last-of-type
function nthOfTypeFilter(fid, negate, elms, pseudo, value) {
  if (fid === 0x07) { // 0x07: nth-last-of-type
    elms.reverse();
  }

  var rv = [], ri = -1, v, i = 0, unq = {},
      idx, pn, currentParent = null, tagName, ok,
      f = nth(value), a = f.a, b = f.b, k = f.k;

  while ( (v = elms[i++]) ) {
    pn = v.parentNode;
    if (pn !== currentParent) {
      currentParent = pn;
      unq = {};
    }

    tagName = v.tagName;
    if (tagName in unq) {
      ++unq[tagName];
    } else {
      unq[tagName] = 1;
    }
    idx = unq[tagName];
    ok = 0;

    switch (k) {
    case 1:  ok = (idx === b); break;
    case 2:  ok = (idx >=  b); break;
    case 3:  ok = (!((idx - b) % a) && (idx - b) / a >= 0); break;
    default: ok = (idx <=  b);
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :first-of-type  :last-of-type
function ofTypeFilter(fid, negate, elms) {
  if (fid === 0x0a) { // 0x0a: last-of-type
    elms.reverse();
  }
  var rv = [], ri = -1, v, i = 0, unq = {},
      pn, currentParent = null;

  while ( (v = elms[i++]) ) {
    pn = v.parentNode;
    if (pn !== currentParent) {
      currentParent = pn;
      unq = {};
    }
    if (v.tagName in unq) {
      ++unq[v.tagName];
    } else {
      unq[v.tagName] = 1;
    }
    if ((unq[v.tagName] === 1) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :enabled  :disabled  :checked
function simpleFilter(fid, negate, elms) {
  var rv = [], ri = -1, v, i = 0, ok, needValidate,
      rex = /^(input|button|select|option|textarea)$/i;

  while ( (v = elms[i++]) ) {
    needValidate = ok = 0;
    switch (fid) {
    case 0x0b: ++needValidate; ok = !v.disabled; break;  // 0x0b: enabled
    case 0x0c: ++needValidate; ok = !!v.disabled; break; // 0x0c: disabled
    case 0x0d: ++needValidate; ok = !!v.checked; break;  // 0x0d: checked
    }

    if (needValidate && !rex.test(v.tagName)) { // fix #144
      if (negate) {
        rv[++ri] = v;
      }
    } else if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :root
function root(fid, negate, elms) {
  if (!negate) {
    return [_rootElement];
  }
  var rv = [], ri = -1, v, i = 0;
  while ( (v = elms[i++]) ) {
    if (v !== _rootElement) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :target
function target(fid, negate, elms, pseudo, value, tags, contentType) {
  var rv = [], ri = -1, i = 0, v, needle = location.hash.slice(1);

  if (needle) {
    if (contentType === 1) { // 1: html
      while ( (v = elms[i++]) ) {
        (((v.id || v.name) === needle) ^ negate) && (rv[++ri] = v);
      }
    } else { // 2: xml
      while ( (v = elms[i++]) ) {
        ((v.id === needle) ^ negate) && (rv[++ri] = v);
      }
    }
  }
  return rv;
}

// :contains
function contains(fid, negate, elms, pseudo, value) {
  valie = value.replace(TRIM_QUOTE, "");
  var rv = [], ri = -1, v, i = 0;

  while ( (v = elms[i++]) ) {
    if ((v[_innerText].indexOf(value) >= 0) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :link
function link(fid, negate, elms) {
  var rv = [], ri = -1, ary = _toArray(_doc.links), v, i = 0,
      j = 0, jz = elms.length, hit;
  while ( (v = ary[i++]) ) {
    for (hit = -1, j = 0; j < jz; ++j) {
      if (elms[j] === v) {
        hit = j;
        break;
      }
    }
    if ((hit >= 0) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :empty
function empty(fid, negate, elms) {
  var rv = [], ri = -1, i = 0, v, c, missMatch = 0;
  while ( (v = elms[i++]) ) {
    missMatch = 0;
    for (c = v.firstChild; c; c = c.nextSibling) {
      if (c.nodeType === 1) {
        ++missMatch;
        break;
      }
    }
    // touch(v.textContent) very slowly
    if ((!missMatch && !v[_innerText]) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// :lang
function lang(fid, negate, elms, pseudo, value) {
  var rv = [], ri = -1, v, i = 0, iz = elms.length,
      rex = RegExp("^(" + value + "$|" + value + "-)", "i");

  for (; i < iz; ++i) { // don't touch me
    v = elms[i];
    while (v && v !== _doc && !v.getAttribute("lang")) {
      v = v.parentNode;
    }
    if (((v && v !== _doc) && rex.test(v.getAttribute("lang"))) ^ negate) {
      rv[++ri] = elms[i];
    }
  }
  return rv;
}

// :only-of-type
function onlyOfType(fid, negate, elms, pseudo, value, tags, contentType) {
  var rv = [], ri = -1, v, i = 0, c, f, t, tagName,
      iter1 = "nextSibling",
      iter2 = "previousSibling";

  while ( (v = elms[i++]) ) {
    f = 0;
    tagName = v.tagName,
    t = tags[tagName] || addTag(tagName, contentType);
    for (c = v[iter1]; c; c = c[iter1]) {
      if (c.nodeType === 1 && c.tagName === t) {
        ++f;
        break;
      }
    }
    if (!f) {
      for (c = v[iter2]; c; c = c[iter2]) {
        if (c.nodeType === 1 && c.tagName === t){
          ++f;
          break;
        }
      }
    }
    if ((!f) ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

// parse :nth-xxx(an+b)
function nth(anb) {
  var a, b, c, match = NTH_ANB.exec(anb);

  if (!match) { throw anb + " unsupported"; }
  if (match[2]) { return { a: 2, b: 0, k: 3 }; } // nth(even)
  if (match[3]) { return { a: 2, b: 1, k: 3 }; } // nth(odd)
  if (match[4]) { return { a: 0, b: 0, k: 2 }; } // nth(1n+0), nth(n+0), nht(n)
  if (match[5]) { return { a: 0, b: _int(match[5], 10), k: 1 }; } // nth(1)
  a = (match[7] === "-" ? -1 : match[7] || 1) - 0;
  b = (match[8] || 0) - 0;
  c = a < 2;
  return {
    a: c ? 0 : a,
    b: b,
    k: c ? a + 1 : 3
  };
}

// === uuQuery Selectors ==================================
// :digit(0x40)  :negative(0x41)  :tween(0x42)  :playing(0x43)
function extendFilter(fid, negate, elms) {
  var rv = [], ri = -1, v, i = 0, ok,
      DIGIT = /^\s*(?:[\-+]?)[\d,\.]+\s*$/,
      NEGATIVE = /^\s*\-[\d,\.]+\s*$/;

  while ( (v = elms[i++]) ) {
    ok = 0;
    switch (fid) {
    case 0x40: ok = DIGIT.test(v[_innerText] || ""); break;
    case 0x41: ok = NEGATIVE.test(v[_innerText] || ""); break;
    case 0x42: ok = !!v.tween; break;
    case 0x43: ok = v.tween && v.tween.playing(); break;
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  return rv;
}

function parseColor(color) { // @param HexColorString: "#000000" style only
                             // @return Number: color number
  var rv = color;
  if (typeof rv === "string") {
    if (_win.uuColor) {
      rv = uuColor.parse(rv)[0];
      rv = _int(rv.replace(/^#/, "0x"), 16);
    }
  }
  return rv;
}

function styleQuery(negate, elms, match) {
  var value = match[4].replace(/^\s*["']|["']\s*$/g, ""), // trim quote
      _style = uuStyle,
      prop, propKind,
      operator = EX_OPERATOR[match[2]], w,
      rv = [], ri = -1, ary, ok, r, e, v1, v2 = 0, i = 0,
      hasRange = 0, unitExchanged = 0;

  if (!operator) {
    throw match[0] + " unsupported";
  }
  w = match[5] || ""; // regexp flag

  if (operator === 8) { // 8: "/=" is regexp operator
    value = RegExp(value, w); // build regexp object
  } else {
    // fix {class=i} -> match[4] = "", match[5] = "i"
    w && (value += w);
  }

  prop = match[1];

  v1 = value;
  if (operator === 11) { // 11: "&="
    ary = v1.split(/\s*\~\s*/); // {prop&=0x0~0xf}
    if (ary.length !== 2) {
      throw "[" + prop + "&=" + v1 + "-???] syntax error";
    }
    v1 = ary[0];
    v2 = ary[1];
    hasRange = 1;
  }

  // pre-filter
  propKind = EX_PROP_KIND[prop] || 0;
  switch (propKind) {
  case 1: // 1: color, backgroundColor to number
    v1 = parseColor(v1);
    hasRange && (v2 = parseColor(v2));
    break;
  case 2: // 2: opacity
    v1 = _float(v1);
    hasRange && (v2 = _float(v2));
  }

  // range normalize
  if (hasRange && v1 > v2) { // {prop&=1~0} -> {prop&=0~1}
    r = v2, v2 = v1, v1 = r; // swap(v1, v2);
  }

  while ( (e = elms[i++]) ) {
    switch (propKind) {
    case 1: // color, backgroundColor
      r = parseColor((_ie ? e[_runstyle]
                          : _runstyle(e, ""))[prop]);
      break;
    case 2: // opacity
      r = _ie ? (e.filters.alpha ? e.style.opacity : 1.0)
              : _float(_runstyle(e, "").opacity);
      break;
    case 3: // width
      r = _style.getPixel(e, "width");

      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 4: // height
      r = _style.getPixel(e, "height");

      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 5: // top, right, bottom, left
      r = _style.toPixel(e, (_ie ? e[_runstyle]
                                  : _runstyle(e, ""))[prop]);
      if (!unitExchanged) {
        ++unitExchanged;
        v1 = _style.toPixel(e, v1);
        hasRange && (v2 = _style.toPixel(e, v1));
      }
      break;
    case 6: // backgroundImage
      r = _style.getBGImg(e) || "none"; // "http://..." or "none"
      break;
    default:
      w = (_ie ? e[_runstyle]
               : _runstyle(e, ""))[prop];
      r = (operator >= 9) ? _float(w) : w;
    }

    ok = 0;
    switch (operator) {
    case 1: ok = v1 == r; break;            // {prop = value} or {prop: value}
    case 2: ok = v1 != r; break;            // {prop != value}
    case 3: ok = r.indexOf(v1) >= 0; break; // {prop *= value}
    case 4: ok = !r.indexOf(v1); break;     // {prop ^= value}
    case 5: ok = (r.lastIndexOf(v1) + v1.length) === r.length; break;
                                            // {prop $= value}
    case 8: ok = v1.test(r); break;         // {prop /= "regexp"ig}
    case 9: ok = r >= v1; break;            // {prop >= value}
    case 10: ok = r <= v1; break;           // {prop <= value}
    case 11: ok = r >= v1 && r <= v2;       // {prop &= #000000~#ffffff}
    }
    if (ok ^ negate) {
      rv[++ri] = e;
    }
  }
  return rv;
}

// === Action Selectors ====================================
function visitedFilter(fid, negate, elms) {
  // :link(0x0e)  :visited(0x0f)
  var rv = [], ri = -1, v, i = 0, ok, cs, idx;

  // http://d.hatena.ne.jp/uupaa/20080928/1222543331
  _ss.create(_ssid);
  idx = _ss.insertRule(_ssid, "a:visited", _ie ? "ruby-align:center"
                                               : "outline:0 solid #000");
  while ( (v = elms[i++]) ) {
    if (v.tagName === "A") {
      if (_ie) {
        ok = (v.currentStyle.rubyAlign === "center") ? 1 : 0;
      } else {
        cs = _runstyle(v, "");
        ok = (cs.outlineWidth === "0px" &&
              cs.outlineStyle === "solid") ? 1 : 0;
      }
      if (fid === 0x0e) {
        if ((!ok) ^ negate) {
          rv[++ri] = v;
        }
      } else {
        if (ok ^ negate) {
          rv[++ri] = v;
        }
      }
    }
  }
  _ss.removeRule(_ssid, idx);
  return rv;
}

function actionFilter(fid, negate, elms, pusedo) {
  // :hover(0x10)  :focus(0x11)
  var rv = [], ri = -1, v, i = 0, ok, cs, idx;

  // http://d.hatena.ne.jp/uupaa/20080928/1222543331
  _ss.create(_ssid);
  idx = _ss.insertRule(_ssid, ":" + pusedo, _ie ? "ruby-align:center"
                                                : "outline:0 solid #000");
  while ( (v = elms[i++]) ) {
    if (_ie) {
      ok = (v.currentStyle.rubyAlign === "center") ? 1 : 0;
    } else {
      cs = _runstyle(v, "");
      ok = (cs.outlineWidth === "0px" &&
            cs.outlineStyle === "solid") ? 1 : 0;
    }
    if (ok ^ negate) {
      rv[++ri] = v;
    }
  }
  _ss.removeRule(_ssid, idx);
  return rv;
}

// --- initialize ---
(function() {
  // create tag dict.
  var ary = ("*,div,p,a,ul,ol,li,span,td,tr,dl,dt,dd,h1,h2,h3,h4," +
             "iframe,form,input,textarea,select,body,style,script").split(","),
      i = 0, iz = ary.length;
  for (; i < iz; ++i) {
    addTag(ary[i]);
  }
})();

// --- export ---
_win.uuQuery = _query;            // window.uuQuery
_query.filters = filters;         // window.uuQuery.filters
_query.childFilter = childFilter; // window.uuQuery.childFilter

})(); // uuQuery scope


// === uuCanvas ===
// depend: uuMeta, uuColor, uuStyle, uuImage
/*
uuCanvas.init(canvas, vml = false) - return new canvas element
uuCanvas.ready(callback)
uuCanvas.already() - return true is already
uuCanvas.expire()
uuCanvas.SL2D
uuCanvas.VML2D
 */
(function() {
var _canvas, // inner namespace
    _mm = uuMeta,
    _style = uuStyle,
    _image = uuImage,
    _win = window,
    _doc = document,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _gecko = _mm.gecko,
    _webkit = _mm.webkit,
    _chrome = _mm.chrome,
    _slver = _mm.slver,
    _uaver = _mm.uaver,
    _egver = _mm.enginever,
    _int = parseInt,
    _float = parseFloat,
    _math = Math,
    _round = _math.round,
    _ceil = _math.ceil,
    _sin = _math.sin,
    _cos = _math.cos,
    _max = _math.max,
//  _rad = _math.PI / 180, // Math.toRadians
    _deg = 180 / _math.PI, // Math.toDegrees
    _runstyle = _mm.runstyle,
    _mix = _mm.mix,
    _hex = _mm.hex,
    _uid = 0, // cache uid
    // ---
    _canvasReady = 0,
    _crc2d = _ie ? 0 : CanvasRenderingContext2D.prototype,
    _metric, // Text Metric Element
    _matrix,
    _zoom = 10,
    _halfZoom = 5,
    _slHostCount = 0,
    _super,
    _shadowWidth = 4,
    _fontCache = {},  // { uid: { font: fontString } }
    _unitCache = {},  // { uid: { pt, em } }
    _colorCache = {}, // { color: ["#ffffff", alpha] }
    _parseColor = function(c) {
      return _colorCache[c] = uuColor.parse(c); // add cache
    },
    // property alias
    GLOBAL_ALPHA    = "globalAlpha",
    GLOBAL_COMPO    = "globalCompositeOperation",
    STROKE_STYLE    = "strokeStyle",
    FILL_STYLE      = "fillStyle",
    LINE_WIDTH      = "lineWidth",
    LINE_CAP        = "lineCap",
    LINE_JOIN       = "lineJoin",
    MITER_LIMIT     = "miterLimit",
    SHADOW_OFFSET_X = "shadowOffsetX",
    SHADOW_OFFSET_Y = "shadowOffsetY",
    SHADOW_BLUR     = "shadowBlur",
    SHADOW_COLOR    = "shadowColor",
    SHADOWS         = [SHADOW_COLOR, SHADOW_OFFSET_X, SHADOW_OFFSET_Y,
                       SHADOW_BLUR],
    FONT            = "font",
    TEXT_ALIGN      = "textAlign",
    TEXT_BASELINE   = "textBaseline",
    MEASURE_STYLE   = "position:absolute;border:0 none;margin:0;padding:0;",
    TRANSPARENT     = "transparent",
    // property sets
    HIT_PROPS       = { width: 1, height: 1 },
    HIT_PROPS2      = { width: 1, height: 1,
                        display: 2, visibility: 2, opacity: 2 },
    COMPOSITES      = { "source-over": 0, "destination-over": 4, copy: 10 },
    SAVE_PROPS      = { strokeStyle: 1, fillStyle: 1, globalAlpha: 1,
                        lineWidth: 1, lineCap: 1, lineJoin: 1, miterLimit: 1,
                        shadowOffsetX: 1, shadowOffsetY: 1, shadowBlur: 1,
                        shadowColor: 1, globalCompositeOperation: 1, font: 1,
                        textAlign: 1, textBaseline: 1, _lineScale: 1,
                        _scaleX: 1, _scaleY: 1, _efx: 1, _clipPath: 1 },
    CAPS            = { square: "square", butt: "flat", round: "round" },
    FONT_SIZES      = { "xx-small": 0.512, "x-small": 0.64, smaller: 0.8,
                        small: 0.8, medium: 1, large: 1.2, larger: 1.2,
                        "x-large": 1.44, "xx-large": 1.728 },
    FONT_STYLES     = { normal: "Normal", italic: "Italic", oblique: "Italic" },
    FONT_WEIGHTS    = { normal: "Normal", bold: "Bold", bolder: "ExtraBold",
                        lighter: "Thin", "100": "Thin", "200": "ExtraLight",
                        "300": "Light", "400": "Normal", "500": "Medium",
                        "600": "SemiBold", "700": "Bold", "800": "ExtraBold",
                        "900": "Black" },
    FONT_SCALES     = { ARIAL: 1.55, "ARIAL BLACK": 1.07,
                        "COMIC SANS MS": 1.15, "COURIER NEW": 1.6,
                        GEORGIA: 1.6, "LUCIDA GRANDE": 1,
                        "LUCIDA SANS UNICODE": 1, "TIMES NEW ROMAN": 1.65,
                        "TREBUCHET MS": 1.55, VERDANA: 1.4, "MS UI GOTHIC": 2,
                        "MS PGOTHIC": 2, MEIRYO: 1,
                        "SANS-SERIF": 1, SERIF: 1, MONOSPACE: 1,
                        FANTASY: 1, CURSIVE: 1 },
    FUNCS           = { 1: "_lfill", 2: "_rfill", 3: "_pfill" },
    // fragments
    SL_FILL         = '" Fill="',
    SL_STROKE       = '" Stroke="',
    SL_DATA         = '" Data="',
    SL_PATH_OPACITY = '<Path Opacity="',
    SL_CANVAS_ZINDEX= '<Canvas Canvas.ZIndex="',
    SL_CANVAS_LEFT  = '" Canvas.Left="',
    SL_CANVAS_TOP   = '" Canvas.Top="',
    VML_COORD       = '" coordsize="100,100',
    VML_FILL        = '" filled="t" stroked="f',
    VML_STROKE      = '" filled="f" stroked="t',
    VML_PATH        = '" path="',
    VML_COLOR       = '" color="',
    VML_COLORS      = '" colors="',
    VML_OPACITY     = '" opacity="',
    VML_ANGLE       = '" angle="',
    VML_FILLTYPE_HEAD = ' filltype="',
    VML_TYPE_HEAD   = ' type="',
    VML_COLOR_HEAD  = ' color="',
    VML_BASE_STYLE  = ' style="position:absolute;z-index:',
    VML_SHAPE_STYLE =
          '<v:shape style="position:absolute;width:10px;height:10px;z-index:',
    VML_END_SHAPE   = '" /></v:shape>',
    VML_VSTROKE     = '"><v:stroke',
    VML_VFILL       = '"><v:fill',
    DX_PFX          = 'progid:DXImageTransform.Microsoft';

_canvas = {
  // uuCanvas.init - initialize a canvas made dynamically
  init: function(canvas, // @param Node: canvas element
                 vml) {  // @param Boolean(= false): true = force VML
    return canvas.getContext ? canvas // already initialized
                             : (vml || !_slver) ? VMLInit(canvas)
                                                : SLInit(canvas);
  },

  // uuCanvas.ready
  ready: function(callback) { // @param Function:
    var lp = function() {
      (_ie ? _canvas.already()
           : _canvasReady) ? callback() : setTimeout(lp, 64);
    }
    setTimeout(lp, 16);
  },

  // uuCanvas.already
  already: function() { // @return Boolean: true is already
    if (!_ie) { return !!_canvasReady; }
    var node = _doc.getElementsByTagName("canvas"), i = node.length;
    while (i--) {
      if (!("uuCanvasType" in node[i])) {
        return false;
      }
    }
    return true;
  },

  // uuCanvas.expire - expire cache
  expire: function() {
    _fontCache = {};
    _unitCache = {};
    _colorCache = {};
  }
};

// 2D Matrix
_matrix = {
  multiply: function(a, b) {
    return [a[0] * b[0] + a[1] * b[3] + a[2] * b[6],  // m11
            a[0] * b[1] + a[1] * b[4] + a[2] * b[7],  // m12
            0,                                        // m13
            a[3] * b[0] + a[4] * b[3] + a[5] * b[6],  // m21
            a[3] * b[1] + a[4] * b[4] + a[5] * b[7],  // m22
            0,                                        // m23
            a[6] * b[0] + a[7] * b[3] + a[8] * b[6],  // m31(dx)
            a[6] * b[1] + a[7] * b[4] + a[8] * b[7],  // m32(dy)
            a[6] * b[2] + a[7] * b[5] + a[8] * b[8]]; // m33
  },

  translate: function(x, y) {
    return [1, 0, 0,  0, 1, 0,  x, y, 1];
  },

  rotate: function(angle) {
    var c = _cos(angle), s = _sin(angle);
    return [c, s, 0,  -s, c, 0,  0, 0, 1];
  },

  scale: function(x, y) {
    return [x, 0, 0,  0, y, 0,  0, 0, 1];
  },

  transform: function(m11, m12, m21, m22, dx, dy) {
    return [m11, m12, 0,  m21, m22, 0,  dx, dy, 1];
  }
};

function detectDrawImageArg(image) {
  var a = arguments, az = a.length,
      dim = _image.getActualDimension(image);

  if (az < 9) {
    return {
      az: az, dim: dim,
      sx: 0, sy: 0, sw: dim.w, sh: dim.h,
      dx: a[1], dy: a[2], dw: a[3] || dim.w, dh: a[4] || dim.h
    };
  } else if (az === 9) {
    return {
      az: az, dim: dim,
      sx: a[1], sy: a[2], sw: a[3], sh: a[4],
      dx: a[5], dy: a[6], dw: a[7], dh: a[8]
    };
  }
  throw "";
}

function toHTMLEntity(str) {
  return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
}

// measure text rect(width, height)
function getTextMetric(text, font) {
  if (!_metric) {
    _metric = _doc.createElement("div");
    // "left:-10000px" is fixed word wrap
    _metric.style.cssText =
        MEASURE_STYLE +
        "top:-10000px;left:-10000px;text-align:left;visibility:hidden";
    _doc.body.appendChild(_metric);
  }
  _metric.style.font = font;
  _ie ? (_metric.innerText = text)
      : (_metric.textContent = text);

  var w = 0, h = 0, rect;
  if (_metric.getBoundingClientRect) {
    rect = _metric.getBoundingClientRect();
    w = rect.right - rect.left, h = rect.bottom - rect.top;
  }
  return { w: _metric.clientWidth || w, h: _metric.clientHeight || h };
}

// parse CSS::font style
function parseFont(font, embase) {
  var rv = {}, w, sz, dummy, style, uid, key = "uuCanvasID";

  function measureUnit(elm) {
    var node = elm.appendChild(_doc.createElement("div")), pt, em;
    node.style.cssText = MEASURE_STYLE + "width:12pt;height:12em";
    pt = node.clientWidth  / 12;
    em = node.clientHeight / 12;
    elm.removeChild(node);
    return { pt: pt, em: em };
  }

  uid = embase[key] || (embase[key] = ++_uid);

  if (uid in _fontCache) {
    if (font in _fontCache[uid]) {
      return _fontCache[uid][font];
    }
  } else {
    _fontCache[uid] = {};
  }

  // computed font style by CSS parser
  dummy = _doc.createElement("div");
  style = dummy.style;
  try {
    style.font = font;
  } catch (err) {}

  sz = style.fontSize;

  if ( (w = FONT_SIZES[sz]) ) {
    w *= 16;
  } else {
    w = _float(sz);
    if (/pt$/.test(sz)) { // "12.3pt"
      w *= 1.33; // 1.3333...
    } else if (/em$/.test(sz)) { // "10.5em"
      if (!(uid in _unitCache)) {
        _unitCache[uid] = measureUnit(embase);
      }
      w *= _unitCache[uid].em;
    }
  }
  rv.size = _float(w);
  rv.style = style.fontStyle;
  rv.weight = style.fontWeight;
  rv.variant = style.fontVariant;
  rv.rawfamily = style.fontFamily.replace(/[\"\']/g, "");
  rv.family = "'" + rv.rawfamily.replace(/\s*,\s*/g, "','") + "'";
  rv.formal = [rv.style, rv.variant, rv.weight, rv.size.toFixed(2) + "px",
               rv.family].join(" ");
  return _fontCache[uid][font] = rv;
}

function applyCanvasSize(elm) {
  var e = elm, attr = e.attributes;
  if (attr.width && attr.width.specified) {
    e.style.pixelWidth = _int(attr.width.nodeValue);
  } else {
    e.width = e.clientWidth;
  }
  if (attr.height && attr.height.specified) {
    e.style.pixelHeight = _int(attr.height.nodeValue);
  } else {
    e.height = e.clientHeight;
  }
}

function strokeProps(obj, vml) {
  var cap = CAPS[obj[LINE_CAP]],
      join = obj[LINE_JOIN],
      width = (obj[LINE_WIDTH] * obj._lineScale).toFixed(2),
      miter = obj[MITER_LIMIT];
  if (!vml) {
    return ['" StrokeLineJoin="', join,
            '" StrokeMiterLimit="', miter,
            '" StrokeThickness="', width,
            '" StrokeStartLineCap="', cap,
            '" StrokeEndLineCap="', cap].join("");
  }
  return ['" weight="', width, 'px" endcap="', cap,
          '" joinstyle="', join,
          '" miterlimit="', miter].join("");
}

_super = {
  save: function() {
    var prop = {}, i;
    for (i in SAVE_PROPS) {
      prop[i] = this[i];
    }
    this._stack.push([prop, _mix([], this._mtx),
                      this._clipPath ? String(this._clipPath) : null]);
  },

  restore: function() {
    if (!this._stack.length) { return; }

    var last = this._stack.pop(), i;
    for (i in SAVE_PROPS) {
      this[i] = last[0][i];
    }
    this._mtx = last[1];
    this._clipPath = last[2];
  },

  scale: function(x, y) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([x, 0, 0,  0, y, 0,  0, 0, 1], this._mtx);
    this._scaleX *= x;
    this._scaleY *= y;
    this._lineScale = (this._mtx[0] + this._mtx[4]) / 2;
  },

  rotate: function(angle) {
    this._efx = 1;
    var c = _cos(angle), s = _sin(angle);
    // inlining
    this._mtx = _matrix.multiply([c, s, 0,  -s, c, 0,  0, 0, 1], this._mtx);
  },

  translate: function(x, y) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([1, 0, 0,  0, 1, 0,  x, y, 1], this._mtx);
  },

  transform: function(m11, m12, m21, m22, dx, dy) {
    this._efx = 1;
    // inlining
    this._mtx = _matrix.multiply([m11, m12, 0,  m21, m22, 0,  dx, dy, 1],
                                 this._mtx);
  },

  setTransform: function(m11, m12, m21, m22, dx, dy) {
    // reset _efx flag
    this._efx = (m11 === 1 && !m12 &&
                 !m21 && m22 === 1 && !dx && !dy) ? 0 : 1;
    this._mtx = _matrix.transform(m11, m12, m21, m22, dx, dy);
  },

  strokeRect: function(x, y, w, h) {
    this.fill(1, this._rect(x, y, w, h));
  },

  beginPath: function() {
    this._path = [];
  },

  arcTo: function(x1, y1, x2, y2, radius) {
    // not impl
  },

  stroke: function() {
    this.fill(1);
  },

  isPointInPath: function(x, y) {
    // not impl
  },

  strokeText: function(text, x, y, maxWidth) {
    this.fillText(text, x, y, maxWidth, 1);
  },

  measureText: function(text) {
    var metric = getTextMetric(text, this[FONT]);
    return new TextMetrics(metric.w, metric.h);
  },

  createImageData: function(sw, sh) {
    // not impl
  },

  getImageData: function(sx, sy, sw, sh) {
    // not impl
  },

  putImageData: function(imagedata, dx, dy, dirtyX, dirtyY,
                         dirtyWidth, dirtyHeight) {
    // not impl
  },

  _initSurface: function(resize) {
    _mix(this, {
      // --- compositing ---
      globalAlpha:    1.0,
      globalCompositeOperation: "source-over",
      // --- colors and styles ---
      strokeStyle:    "#000000", // black
      fillStyle:      "#000000", // black
      // --- line caps/joins ---
      lineWidth:      1,
      lineCap:        "butt",
      lineJoin:       "miter",
      miterLimit:     10,
      // --- shadows ---
      shadowOffsetX:  0,
      shadowOffsetY:  0,
      shadowBlur:     0,
      shadowColor:    TRANSPARENT, // transparent black
      // --- text ---
      font:           "10px sans-serif",
      textAlign:      "start",
      textBaseline:   "alphabetic",
      // --- extend properties ---
      xMissColor:     "#000000", // black
      xTextMarginTop: 1.3, // for VML
      xClipStyle:     0, // for VML
      xImageRender:   0, // 0: normal, 1: vml:image
      xFlyweight:     0, // for Silverlight, VML
      xShadowOpacityFrom:  0.01, // for Silverlight, VML
      xShadowOpacityDelta: 0.05, // for Silverlight, VML
      // --- hidden properties ---
      _lineScale:     1,
      _scaleX:        1,
      _scaleY:        1,
      _zindex:        -1,
      _efx:           0 // 1: matrix effected
    });

    this._mtx = [1, 0, 0,  0, 1, 0,  0, 0, 1]; // Matrix.identity
    this._history = []; // canvas rendering history
    this._stack = []; // matrix and prop stack.
    this._path = []; // current path
    this._clipPath = null; // clipping path

    if (this.canvas.uuCanvasType === "VML2D") {
      this._shadow = ["#000", 0, 0, 0];
      this._px = 0; // current position x
      this._py = 0; // current position y
      if (resize) {
        this._elm.style.pixelWidth = this.canvas.width;
        this._elm.style.pixelHeight = this.canvas.height;
      }
    } else {
      this._shadow = ["#000", 0, 0, 0];
      this.xShadowBlur = _slver >= 3 ? 1 : 0;
      this.xTiling = 1; // 1 = TileBrush simulate(slow)
      this._clipRect = null; // clipping rect
    }
    return this;
  }
};

function onPropertyChange(evt) {
  var tgt, name = evt.propertyName;
  if (HIT_PROPS[name]) {
    tgt = evt.srcElement;
    tgt.style[name] = _max(_int(tgt.attributes[name].nodeValue), 0) + "px";
    tgt.uuCanvasType && tgt.getContext()._initSurface(1)._clear();
  }
}

function TextMetrics(w, h) { // for measureText
  this.width = w;
  this.height = h;
}

function Patt(image, repetition) { // for createPattern
  repetition = repetition || "repeat";
  switch (repetition) {
  case "repeat": break;
  default: throw "";
  }

  if (!("src" in image)) { // HTMLCanvasElement unsupported
    throw "";
  }
  this._src = image.src; // HTMLImageElement
  this._dim = _image.getActualDimension(image);
  this._type = 3; // 3:tile
  this._repeat = repetition;
}

function Grad(type, param, vml) { // for create(Linear|Radial)Gradient
  this._vml = vml;
  this._type = type;
  this._param = param;
  this._colorStop = [];
}

Grad.prototype.addColorStop = function(offset, color) {
  function fn(a, b) {
    return a.offset - b.offset;
  }

  var c = _colorCache[color] || _parseColor(color),
      v, i = 0, iz;

  if (!this._vml) { // SL
    this._colorStop.push({ offset: offset, color: c });
  } else { // VML
    // collision of the offset is evaded
    for (iz = this._colorStop.length; i < iz; ++i) {
      v = this._colorStop[i];
      if (v.offset === offset) {
        if (offset < 1 && offset > 0) {
          offset += iz / 1000; // collision -> +0.001
        }
      }
    }
    this._colorStop.push({ offset: 1 - offset, color: c });
  }
  this._colorStop.sort(fn); // sort offset
};

function removeFallbackContents(elm) {
  if (!elm.parentNode) {
    return elm;
  }
  var rv = _doc.createElement(elm.outerHTML),
      endTags = _doc.getElementsByTagName("/CANVAS"),
      idx = elm.sourceIndex,
      v, w, i = 0, iz = endTags.length;
  for (; i < iz; ++i) {
    if (idx < endTags[i].sourceIndex &&
        elm.parentNode === endTags[i].parentNode) {
      v = _doc.all[endTags[i].sourceIndex];
      do {
        w = v.previousSibling; // keep previous
        v.parentNode.removeChild(v);
        v = w;
      } while (v !== elm);
      break;
    }
  }
  elm.parentNode.replaceChild(rv, elm);
  return rv;
}

// --- Silverlight ---
function SLInit(elm) {
  var e = removeFallbackContents(elm),
      onload = "_sl" + (++_slHostCount) + "_onload";

  applyCanvasSize(e);
  e.getContext = function() { return e._ctx2d; };
  e._ctx2d = new SL2D(e);
  _win[onload] = function(sender) {
    e.uuCanvasType = "SL2D"; // canvas.already mark
    // lazy detection
    e.style.direction = e.currentStyle.direction;
    // sender is <Canvas> element
    // sender.getHost() is <object> element
    e._ctx2d._view = sender.children;
    e._ctx2d._content = sender.getHost().content;
    _win[onload] = void 0; // free event-hander
  };
  e.innerHTML = [
    '<object type="application/x-silverlight" width="100%" height="100%">',
      '<param name="background" value="#00000000" />',  // transparent
      '<param name="windowless" value="true" />',
      '<param name="source" value="#xaml" />',          // XAML ID
      '<param name="onLoad" value="', onload, '" />',   // bond to global
    '</object>'].join("");
  e.attachEvent("onpropertychange", onPropertyChange);
  return e;
}

// Silverlight 2D
function SL2D(elm) {
  this.canvas = elm;
  this._initSurface();
  this._elm = elm;
  this._view = null;
  this._content = null;
};

_mix(SL2D.prototype, _super, {
  _rect: function(x, y, w, h) {
    if (this._efx) {
      var c0 = this._map(x, y),
          c1 = this._map(x + w, y),
          c2 = this._map(x + w, y + h),
          c3 = this._map(x, y + h);
      return [" M", c0.x, " ", c0.y,
              " L", c1.x, " ", c1.y,
              " L", c2.x, " ", c2.y,
              " L", c3.x, " ", c3.y,
              " Z"].join("");
    }
    return [" M", x,     " ", y,
            " L", x + w, " ", y,
            " L", x + w, " ", y + h,
            " L", x,     " ", y + h,
            " Z"].join("");
  },

  _map: function(x, y) {
    var m = this._mtx;
    return {
      x: x * m[0] + y * m[3] + m[6], // x * m11 + y * m21 + dx
      y: x * m[1] + y * m[4] + m[7]  // x * m12 + y * m22 + dy
    }
  },

  // === State =============================================
  // === Transformations ===================================
  // === Rects =============================================
  clearRect: function(x, y, w, h) {
    w = _int(w), h = _int(h);
    if ((!x && !y &&
         w == this.canvas.width &&
         h == this.canvas.height)) {
      this._clear(); // clear all
    } else {
      var zindex = 0, c = _style.getBGColor(this._elm, 1), xaml;

      switch (COMPOSITES[this[GLOBAL_COMPO]]) {
      case  4: zindex = --this._zindex; break;
      case 10: this._clear();
      }

      xaml = [SL_PATH_OPACITY, c[1] * this[GLOBAL_ALPHA],
              '" Canvas.ZIndex="', zindex,
              SL_FILL, c[0],
              SL_DATA, this._rect(x, y, w, h), '" />'].join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    }
  },

  _clear: function(x, y, w, h) {
    this._history = [];
    this._zindex = 0;
    this._view && this._view.clear(); // fix for IE8
  },

  fillRect: function(x, y, w, h) {
    this.fill(0, this._rect(x, y, w, h));
  },

  // === Path API ==========================================
  closePath: function() {
    this._path.push(" Z");
  },

  moveTo: function(x, y) {
    if (this._efx) {
      var m = this._mtx; // inlining: this._map(x, y)
      this._path.push(" M", x * m[0] + y * m[3] + m[6], " ",
                            x * m[1] + y * m[4] + m[7]);
    } else {
      this._path.push(" M", x, " ", y);
    }
  },

  lineTo: function(x, y) {
    if (this._efx) {
      var m = this._mtx; // inlining: this._map(x, y)
      this._path.push(" L", x * m[0] + y * m[3] + m[6], " ",
                            x * m[1] + y * m[4] + m[7]);
    } else {
      this._path.push(" L", x, " ", y);
    }
  },

  quadraticCurveTo: function(cpx, cpy, x, y) {
    if (this._efx) {
      var c0 = this._map(cpx, cpy), c1 = this._map(x, y);
      cpx = c0.x, cpy = c0.y, x = c1.x, y = c1.y;
    }
    this._path.push(" Q", cpx, " ", cpy, " ", x, " ", y);
  },

  bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
    if (this._efx) {
      var c0 = this._map(cp1x, cp1y), c1 = this._map(cp2x, cp2y),
          c2 = this._map(x, y);
      cp1x = c0.x, cp1y = c0.y, cp2x = c1.x, cp2y = c1.y, x = c2.x, y = c2.y;
    }
    this._path.push(" C", cp1x, " ", cp1y, " ", cp2x, " ", cp2y, " ",
                          x, " ", y);
  },

  rect: function(x, y, w, h) {
    this._path.push(this._rect(x, y, w, h));
  },

  arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
    var deg1 = startAngle * _deg,
        deg2 = endAngle * _deg,
        isLargeArc = 0, magic = 0.0001570796326795,
        sweepDirection = anticlockwise ? 0 : 1,
        sx, sy, ex, ey, rx, ry, c0;

    // angle normalize
    if (deg1 < 0)   { deg1 += 360; }
    if (deg1 > 360) { deg1 -= 360; }
    if (deg2 < 0)   { deg2 += 360; }
    if (deg2 > 360) { deg2 -= 360; }

    // circle
    if (deg1 + 360 == deg2 || deg1 == deg2 + 360) {
      if (sweepDirection) {
        endAngle -= magic;
      } else {
        endAngle += magic;
      }
      isLargeArc = 1;
    } else if (sweepDirection) {
      if (deg2 - deg1 > 180) {
        isLargeArc = 1;
      }
    } else {
      if (deg1 - deg2 > 180) {
        isLargeArc = 1;
      }
    }

    rx = this._scaleX * radius;
    ry = this._scaleY * radius;

    sx = x + (_cos(startAngle) * radius);
    sy = y + (_sin(startAngle) * radius);
    ex = x + (_cos(endAngle) * radius);
    ey = y + (_sin(endAngle) * radius);

    // add <PathFigure StartPoint="..">
    this._path.length ? this.lineTo(sx, sy)
                      : this.moveTo(sx, sy);
    if (this._efx) {
      c0 = this._map(ex, ey);
      ex = c0.x;
      ey = c0.y;
    }
    this._path.push(" A", rx, " ", ry, " 0 ", isLargeArc, " ",
                    sweepDirection, " ", ex, " ", ey);
  },

  fill: function(wire, path) {
    path = path || this._path.join("");

    var rv = [], xaml, zindex = 0, mix, c,
        style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    if ( (mix = COMPOSITES[this[GLOBAL_COMPO]]) ) {
      (mix === 4) ? (zindex = --this._zindex) : this._clear();
    }

    if (typeof style === "string") {
      c = _colorCache[style] || _parseColor(style);

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (sc[1] && !this.xShadowBlur) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(SL_PATH_OPACITY, so.toFixed(2),
                  SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                  SL_DATA, path,
                  wire ? strokeProps(this) : "",
                  wire ? SL_STROKE : SL_FILL, sc[0], '" />');
        }
      }
      rv.push(SL_PATH_OPACITY, c[1] * this[GLOBAL_ALPHA],
              SL_DATA, path,
              wire ? strokeProps(this) : "",
              wire ? SL_STROKE : SL_FILL, c[0], '">',
              (sc[1] && this.xShadowBlur) ? this._blur("Path", sc) : "",
              '</Path></Canvas>');
      xaml = rv.join("");
    } else {
      xaml = this[FUNCS[style._type]](style, path, wire, mix, zindex, sc);
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
    this._view.add(this._content.createFromXaml(xaml, false));
  },

  // LinearGradient fill
  _lfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        fp = style._param,
        color = this._lcolor(style._colorStop),
        prop = wire ? "Stroke" : "Fill",
        c0 = this._map(fp.x0, fp.y0), c1 = this._map(fp.x1, fp.y1),
        // for shadow
        si = 0, siz = _shadowWidth, sc, so = 0, sd = 0, shx = 0, shy = 0;

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sc = this._lcolor([
        { offset: 0.0, color: shadowColor },
        { offset: 1.0, color: shadowColor }
      ]);
      shx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      shy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }
      for (; si < siz; so += sd, --shx, --shy, ++si) {
        rv.push(SL_PATH_OPACITY, so.toFixed(2),
                SL_CANVAS_LEFT, shx, SL_CANVAS_TOP, shy,
                SL_DATA, path,
                wire ? strokeProps(this) : "", '"><Path.', prop,
                '><LinearGradientBrush MappingMode="Absolute" StartPoint="',
                c0.x, ",", c0.y,
                '" EndPoint="', c1.x, ",", c1.y, '">', sc,
                '</LinearGradientBrush></Path.', prop, '></Path>');
      }
    }

    rv.push(SL_PATH_OPACITY, this[GLOBAL_ALPHA],
            SL_DATA, path,
            wire ? strokeProps(this) : "", '"><Path.', prop,
            '><LinearGradientBrush MappingMode="Absolute" StartPoint="',
            c0.x, ",", c0.y,
              '" EndPoint="', c1.x, ",", c1.y, '">', color,
            '</LinearGradientBrush></Path.', prop, '>',
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Path", shadowColor) : "",
            '</Path></Canvas>');
    return rv.join("");
  },

  // RadialGradient fill
  _rfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], prop = wire ? "Stroke" : "Fill",
        fp = style._param,
        zindex2 = 0,
        color = this._rcolor(style),
        rr = fp.r1 * 2,
        x = fp.x1 - fp.r1,
        y = fp.y1 - fp.r1,
        gx = (fp.x0 - (fp.x1 - fp.r1)) / rr,
        gy = (fp.y0 - (fp.y1 - fp.r1)) / rr,
        m = _matrix.multiply(_matrix.translate(x, y), this._mtx),
        tmpmtx = this._trns('Ellipse', m),
        v, bari = "",
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }

      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push('<Ellipse Opacity="', so.toFixed(2),
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                '" Width="', rr, '" Height="', rr,
                wire ? strokeProps(this) : "",
                wire ? SL_STROKE : SL_FILL, shadowColor[0],
                '">', tmpmtx, '</Ellipse>');
      }
    }

    if (!wire) {
      // fill outside
      if (style._colorStop.length) {
        v = style._colorStop[style._colorStop.length - 1];
        if (v.color[1] > 0.001) {
          if (mix === 4) { zindex2 = --this._zindex; }
          bari =  [ SL_PATH_OPACITY, this[GLOBAL_ALPHA],
                    '" Canvas.ZIndex="', zindex2,
                    SL_DATA, path, SL_FILL, '#',
                    _hex[_float(v.color[1] / (1 / 255))] +
                    v.color[0].substring(1),
                    '" />'].join("");
          !this.xFlyweight &&
            this._history.push(this._clipPath ? (bari = this._clippy(bari))
                                              : bari);
          this._view.add(this._content.createFromXaml(bari, false));
        }
      }
    }

    rv.push('<Ellipse Opacity="', this[GLOBAL_ALPHA],
            '" Width="', rr, '" Height="', rr,
            wire ? strokeProps(this) : "",
            '"><Ellipse.', prop, '><RadialGradientBrush GradientOrigin="',
            gx, ',', gy,
            '" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">', color,
            '</RadialGradientBrush></Ellipse.', prop, '>',
              tmpmtx,
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Ellipse", shadowColor) : "",
            '</Ellipse></Canvas>');
    return rv.join("");
  },

  // Pattern fill
  _pfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], prop = wire ? "Stroke" : "Fill",
        zindex2 = 0,
        sw, sh, xz, yz, x, y, // use tile mode
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0;

    if (!wire && this.xTiling) {
      x  = 0;
      y  = 0;
      sw = style._dim.w;
      sh = style._dim.h;
      xz = _ceil(_int(this.canvas.width)  / sw);
      yz = _ceil(_int(this.canvas.height) / sh);

      if (mix === 4) { zindex2 = --this._zindex; }

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (shadowColor[1]) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(SL_PATH_OPACITY, so.toFixed(2),
                  SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                  SL_DATA, path, wire ? strokeProps(this) : "",
                  SL_FILL, shadowColor[0],
                  '" />');
        }
      }

      rv.push(SL_CANVAS_ZINDEX, zindex2, '" Clip="', path, '">');
      for (y = 0; y < yz; ++y) {
        for (x = 0; x < xz; ++x) {
          rv.push('<Image Opacity="', this[GLOBAL_ALPHA],
                  SL_CANVAS_LEFT, x * sw, SL_CANVAS_TOP, y * sh,
                  '" Source="', style._src, '">',
//                  (shadowColor[1] &&
//                   this.xShadowBlur) ? this._blur("Image", shadowColor) : "",
                  '</Image>');
        }
      }
      rv.push('</Canvas></Canvas>');

      return rv.join("");
    }

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (shadowColor[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push(SL_PATH_OPACITY, so.toFixed(2),
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                SL_DATA, path, wire ? strokeProps(this) : "",
                '"><Path.', prop, '><ImageBrush Stretch="None" ImageSource="',
                style._src,
                '" /></Path.', prop, '></Path>');
      }
    }

    rv.push(SL_PATH_OPACITY, this[GLOBAL_ALPHA],
            wire ? strokeProps(this) : "",
            SL_DATA, path,
            '"><Path.', prop, '><ImageBrush Stretch="None" ImageSource="',
            style._src,
            '" /></Path.', prop, '>',
              (shadowColor[1] &&
               this.xShadowBlur) ? this._blur("Path", shadowColor) : "",
            '</Path></Canvas>');
    return rv.join("");
  },

  clip: function() {
    this._clipPath = this._path.join("");
  },

  _clippy: function(xaml) {
    return ['<Canvas Clip="', this._clipPath, '">', xaml,
            '</Canvas>'].join("");
  },

  // === Text ==============================================
  fillText: function(text, x, y, maxWidth, wire) {
    text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");
    var style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        types = (typeof style === "string") ? 0 : style._type,
        rv = [], xaml, c, fp, c0, c1, zindex = 0, mtx, rgx, rgy,
        font = parseFont(this[FONT], this.canvas),
        metric = getTextMetric(text, font.formal),
        offX = 0, align = this[TEXT_ALIGN], dir = "ltr",
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (align) {
    case "end": dir = "rtl"; // break;
    case "start":
      align = this._elm.style.direction === dir ? "left" : "right"
    }
    if (align === "center") {
      offX = (metric.w - 4) / 2; // -4: adjust
    } else if (align === "right") {
      offX = metric.w;
    }

    mtx = this._trns('TextBlock',
                     _matrix.multiply(_matrix.translate(x - offX, y),
                                      this._mtx));

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    rv.push(SL_CANVAS_ZINDEX, zindex, '">');

    if (sc[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = _max(this.xShadowOpacityFrom + 0.9, 1);
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push('<TextBlock Opacity="', so.toFixed(2),
                '" Foreground="', sc[0],
                SL_CANVAS_LEFT, sx, SL_CANVAS_TOP, sy,
                '" FontFamily="', font.rawfamily,
                '" FontSize="', font.size.toFixed(2),
                '" FontStyle="', FONT_STYLES[font.style] || "Normal",
                '" FontWeight="', FONT_WEIGHTS[font.weight] || "Normal",
                '">', toHTMLEntity(text), mtx, '</TextBlock>');
      }
    }

    if (!types) {
      c = _colorCache[style] || _parseColor(style);
      rv.push('<TextBlock Opacity="', c[1] * this[GLOBAL_ALPHA],
              '" Foreground="', c[0]);
    } else {
      rv.push('<TextBlock Opacity="', this[GLOBAL_ALPHA]);
    }
    rv.push('" FontFamily="', font.rawfamily,
            '" FontSize="', font.size.toFixed(2),
            '" FontStyle="', FONT_STYLES[font.style] || "Normal",
            '" FontWeight="', FONT_WEIGHTS[font.weight] || "Normal",
            '">', toHTMLEntity(text), mtx,
              (sc[1] && this.xShadowBlur) ? this._blur("TextBlock", sc) : "");

    switch (types) {
    case 1: c = this._lcolor(style._colorStop);
            fp = style._param;
            c0 = this._map(fp.x0, fp.y0), c1 = this._map(fp.x1, fp.y1),
            rv.push('<TextBlock.Foreground>',
                    '<LinearGradientBrush MappingMode="Absolute" StartPoint="',
                    c0.x, ",", c0.y,
                    '" EndPoint="', c1.x, ",", c1.y, '">', c,
                    '</LinearGradientBrush></TextBlock.Foreground>');
            break;
    case 2: c = this._rcolor(style);
            fp = style._param,
            rgx = (fp.x0 - (fp.x1 - fp.r1)) / (fp.r1 * 2),
            rgy = (fp.y0 - (fp.y1 - fp.r1)) / (fp.r1 * 2),
            rv.push('<TextBlock.Foreground>',
                    '<RadialGradientBrush GradientOrigin="', rgx, ',', rgy,
                    '" Center="0.5,0.5" RadiusX="0.5" RadiusY="0.5">', c,
                    '</RadialGradientBrush></TextBlock.Foreground>');
            break;
    case 3: rv.push('<TextBlock.Foreground>',
                    '<ImageBrush Stretch="None" ImageSource="', style._src,
                    '" /></TextBlock.Foreground>');
    }
    rv.push('</TextBlock></Canvas>');
    xaml = rv.join("");
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
    this._view.add(this._content.createFromXaml(xaml, false));
  },

  // === Drawing images ====================================
  // drawImage(image, dx, dy)
  // drawImage(image, dx, dy, dw, dh)
  // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
  drawImage: function(image) {
    var info = detectDrawImageArg.apply(this, arguments),
        dx = info.dx,
        dy = info.dy,
        dw = info.dw,
        dh = info.dh,
        sx = info.sx,
        sy = info.sy,
        sw = info.sw,
        sh = info.sh,
        iw = info.dim.w,
        ih = info.dim.h,
        rv = [], xaml,
        bw, bh, w, h, x, y, // slice
        m, tmpmtx, size = "", clip = "",
        zindex = 0, sclip = "",
        i = 0, iz, // for copy canvas
        // for shadow
        si = 0, so = 0, sd = 0, shx = 0, shy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    if ("src" in image) { // image is HTMLImageElement
      switch (info.az) {
      case 3:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        break;
      case 5:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        size = ['" Width="', dw, '" Height="', dh].join("");
        break;
      case 9:
        // TODO: image ratio
        //
        bw = dw / sw; // bias width
        bh = dh / sh; // bias height
        w = bw * iw;
        h = bh * ih;
        x = dx - (bw * sx);
        y = dy - (bh * sy);

        m = _matrix.multiply(_matrix.translate(x, y), this._mtx);

        size = ['" Width="', w, '" Height="', h].join("");
        clip = ['<Image.Clip><RectangleGeometry Rect="',
                  [dx - x, dy - y, dw, dh].join(" "),
                '" /></Image.Clip>'].join("");
        if (sc[1] && !this.xShadowBlur) {
          sclip = ['<Rectangle.Clip><RectangleGeometry Rect="',
                    [dx - x, dy - y, dw, dh].join(" "),
                   '" /></Rectangle.Clip>'].join("");
        }
      }

      rv.push(SL_CANVAS_ZINDEX, zindex, '">');

      if (sc[1] && !this.xShadowBlur) {
        shx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        shy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;
        tmpmtx = this._trns('Rectangle', m);

        for (; si < _shadowWidth; so += sd, --shx, --shy, ++si) {
          rv.push('<Rectangle Opacity="', so.toFixed(2),
                  SL_CANVAS_LEFT, shx, SL_CANVAS_TOP, shy,
                  size, SL_FILL, sc[0], '">', sclip,
                  tmpmtx,
                  '</Rectangle>');
        }
      }

      rv.push('<Image Opacity="', this[GLOBAL_ALPHA],
              '" Source="', image.src, size, '">',
              clip, this._trns('Image', m),
              (sc[1] && this.xShadowBlur) ? this._blur("Image", sc) : "",
              '</Image></Canvas>');
      xaml = rv.join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    } else { // HTMLCanvasElement
      iz = image._ctx2d._history.length;
      switch (info.az) {
      case 3:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        break;
      case 5:
        m = _matrix.multiply(_matrix.translate(dx, dy), this._mtx);
        m = _matrix.multiply(_matrix.scale(dw / iw, dh / ih), m);
        break;
      case 9:
        bw = dw / sw; // bias width
        bh = dh / sh; // bias height
        w = bw * iw;
        h = bh * ih;
        x = dx - (bw * sx);
        y = dy - (bh * sy);

        m = _matrix.multiply(_matrix.translate(x, y), this._mtx);
        m = _matrix.multiply(_matrix.scale(bw, bh), m);

        clip = ['<Canvas.Clip><RectangleGeometry Rect="',
                  [(dx - x) / bw, (dy - y) / bh, dw / bw, dh / bh].join(" "),
                '" /></Canvas.Clip>'].join("");
//        if (sc[1] && !this.xShadowBlur) {
//          sclip = ['<Rectangle.Clip><RectangleGeometry Rect="',
//                   [(dx - x) / bw, (dy - y) / bh, dw / bw, dh / bh].join(" "),
//                   '" /></Rectangle.Clip>'].join("");
//        }
      }

      // shadow not impl

      rv.push(SL_CANVAS_ZINDEX, zindex,
              '" Opacity="', this[GLOBAL_ALPHA], // image._ctx2d[GLOBAL_ALPHA],
              size, '">',
              clip, this._trns('Canvas', m),
//              (sc[1] && this.xShadowBlur) ? this._blur("Canvas", sc) : "",
              '<Canvas>');

      for (; i < iz; ++i) {
        rv.push(image._ctx2d._history[i]);
      }
      rv.push('</Canvas></Canvas>');

      xaml = rv.join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (xaml = this._clippy(xaml)) : xaml);
      this._view.add(this._content.createFromXaml(xaml, false));
    }
  },

  // === Pixel manipulation ================================
  // === Gradient ==========================================
  createLinearGradient: function(x0, y0, x1, y1) {
    return new Grad(1, // 1:LinearGradient
                    { x0: x0, y0: y0, x1: x1, y1: y1 });
  },

  createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
    return new Grad(2, // 2:RadialGradient
                    { x0: x0, y0: y0, r0: r0, x1: x1, y1: y1, r1: r1 });
  },

  createPattern: function(image, repetition) {
    return new Patt(image, repetition);
  },

  // build Linear Color
  _lcolor: function(ary) {
    var rv = [], v, i = 0, iz = ary.length, n = 1 / 255;
    for (; i < iz; ++i) {
      v = ary[i];
      rv.push('<GradientStop Color="#',
                _hex[_float(v.color[1] / n)],
                v.color[0].substring(1),
                '" Offset="', v.offset, '" />');
    }
    return rv.join("");
  },

  // build Radial Color
  _rcolor: function(style) {
    var rv = [],
        fp = style._param, n = 1 / 255,
        r0 = fp.r0 / fp.r1,
        remain = 1 - r0,
        v,
        i = 0,
        iz = style._colorStop.length;
    if (!iz) { return ""; }

    rv.push('<GradientStop Color="#',
              _hex[_float(style._colorStop[0].color[1] / n)],
              style._colorStop[0].color[0].substring(1),
              '" Offset="', 0, '" />');
    for (i = 0; i < iz; ++i) {
      v = style._colorStop[i];
      rv.push('<GradientStop Color="#',
                _hex[_float(v.color[1] / n)],
                v.color[0].substring(1),
                '" Offset="', (v.offset * remain + r0), '" />');
    }
    return rv.join("");
  },

  // build MatrixTransform
  _trns: function(type, m) {
    return [
      '<', type,
      '.RenderTransform><MatrixTransform><MatrixTransform.Matrix><Matrix M11="',
                 m[0], '" M21="', m[3], '" OffsetX="', m[6],
      '" M12="', m[1], '" M22="', m[4], '" OffsetY="', m[7],
      '" /></MatrixTransform.Matrix></MatrixTransform></', type,
      '.RenderTransform>'].join("");
  },

  // build Shadow Blur
  _blur: function(type, shadowColor) {
    var sdir = 0, sdepth = 0,
        sx = this[SHADOW_OFFSET_X],
        sy = this[SHADOW_OFFSET_Y];

    if (shadowColor[1]) {
      sdir = (sx > 0 && sy < 0) ?  45 :
             (!sx    && sy < 0) ?  90 :
             (sx < 0 && sy < 0) ? 135 :
             (sx < 0 && !sy   ) ? 180 :
             (sx < 0 && sy > 0) ? 225 :
             (!sx    && sy > 0) ? 270 :
             (sx > 0 && sy > 0) ? 315 : 0;
      sdepth = _max(_math.abs(sx), _math.abs(sy)) * 1.2;
      return ['<', type, '.Effect><DropShadowEffect Opacity="', 1.0,
              '" Color="', shadowColor[0],
              '" BlurRadius="', this[SHADOW_BLUR] * 1.2,
              '" Direction="', sdir,
              '" ShadowDepth="', sdepth,
              '" /></', type, '.Effect>'].join("");
    }
    return "";
  }
});

// --- VML ---
function VMLInit(elm) {
  var e = removeFallbackContents(elm);

  applyCanvasSize(e);
  e.getContext = function() { return e._ctx2d; }
  e._ctx2d = new VML2D(e);
  e.attachEvent("onpropertychange", onPropertyChange);
  return e;
}

function VML2D(elm) {
  this.canvas = elm;
  elm.uuCanvasType = "VML2D";
  this._initSurface();
  this._elm = elm.appendChild(_doc.createElement("div"));
  this._elm.style.pixelWidth = elm.width;
  this._elm.style.pixelHeight = elm.height;
  this._elm.style.overflow = "hidden";
  this._elm.style.position = "absolute";
  this._elm.uuCanvasDirection = elm.currentStyle.direction;
  this._elm.style.direction = "ltr";
  this._clipRect = this._rect(0, 0, this.canvas.width, this.canvas.height);
};

_mix(VML2D.prototype, _super, {
  _rect: function(x, y, w, h) {
    var c0 = this._map(x, y),
        c1 = this._map(x + w, y),
        c2 = this._map(x + w, y + h),
        c3 = this._map(x, y + h);
    return [" m", c0.x, " ", c0.y,
            " l", c1.x, " ", c1.y,
            " l", c2.x, " ", c2.y,
            " l", c3.x, " ", c3.y,
            " x"].join("");
  },

  _map: function(x, y) {
    var m = this._mtx;
    return { x: _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom),
             y: _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom) };
  },

  // === State =============================================
  // === Transformations ===================================
  // === Rects =============================================
  clearRect: function(x, y, w, h) {
    w = _int(w), h = _int(h);
    if ((!x && !y &&
         w == this.canvas.width &&
         h == this.canvas.height)) {
      this._clear();
    } else {
      var zindex = 0, c = _style.getBGColor(this._elm, 1), vml;

      switch (COMPOSITES[this[GLOBAL_COMPO]]) {
      case  4: zindex = --this._zindex; break;
      case 10: this._clear();
      }

      vml =  [VML_SHAPE_STYLE, zindex,
              VML_FILL, VML_COORD, VML_PATH, this._rect(x, y, w, h),
              VML_VFILL, VML_TYPE_HEAD, 'solid',
              VML_COLOR, c[0], VML_OPACITY, c[1] * this[GLOBAL_ALPHA],
              VML_END_SHAPE].join("");
      !this.xFlyweight &&
        this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
      this._elm.insertAdjacentHTML("BeforeEnd", vml);
    }
  },

  _clear: function() {
    this._history = [];
    this._elm.innerHTML = ""; // clear all
    this._zindex = 0;
  },

  fillRect: function(x, y, w, h) {
    var path = this._rect(x, y, w, h);
    this._px = x;
    this._py = y;

    // When all canvases are painted out,
    // the fillStyle(background-color) is preserved.
    if (path === this._clipRect) { // full size path
      if (typeof this[FILL_STYLE] === "string") {
        this.xClipStyle = this[FILL_STYLE]; // keep bgcolor
      }
    }
    this.fill(0, path);
  },

  // === Path API ==========================================
  closePath: function() {
    this._path.push(" x");
  },

  moveTo: function(x, y) {
    var m = this._mtx; // inlining: this._map(x, y)
    this._path.push(
      "m ", _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom), " ",
            _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom));
    this._px = x;
    this._py = y;
  },

  lineTo: function(x, y) {
    var m = this._mtx; // inlining: this._map(x, y)
    this._path.push(
      "l ", _round((x * m[0] + y * m[3] + m[6]) * _zoom - _halfZoom), " ",
            _round((x * m[1] + y * m[4] + m[7]) * _zoom - _halfZoom));
    this._px = x;
    this._py = y;
  },

  quadraticCurveTo: function(cpx, cpy, x, y) {
    var cp1x = this._px + 2.0 / 3.0 * (cpx - this._px),
        cp1y = this._py + 2.0 / 3.0 * (cpy - this._py),
        cp2x = cp1x + (x - this._px) / 3.0,
        cp2y = cp1y + (y - this._py) / 3.0,
        c0 = this._map(x, y),
        c1 = this._map(cp1x, cp1y),
        c2 = this._map(cp2x, cp2y);
    this._path.push("c ", c1.x, " ", c1.y, " ",
                          c2.x, " ", c2.y, " ",
                          c0.x, " ", c0.y);
    this._px = x;
    this._py = y;
  },

  bezierCurveTo: function(cp1x, cp1y, cp2x, cp2y, x, y) {
    var c0 = this._map(x, y),
        c1 = this._map(cp1x, cp1y),
        c2 = this._map(cp2x, cp2y);
    this._path.push("c ", c1.x, " ", c1.y, " ",
                          c2.x, " ", c2.y, " ",
                          c0.x, " ", c0.y);
    this._px = x;
    this._py = y;
  },

  rect: function(x, y, w, h) {
    this._path.push(this._rect(x, y, w, h));
    this._px = x;
    this._py = y;
  },

  arc: function(x, y, radius, startAngle, endAngle, anticlockwise) {
    radius *= _zoom;
    var x1 = x + (_cos(startAngle) * radius) - _halfZoom,
        y1 = y + (_sin(startAngle) * radius) - _halfZoom,
        x2 = x + (_cos(endAngle)   * radius) - _halfZoom,
        y2 = y + (_sin(endAngle)   * radius) - _halfZoom,
        c0, c1, c2, rx, ry;

    if (!anticlockwise) {
      // fix "wa" bug
      (x1.toExponential(5) === x2.toExponential(5)) && (x1 += 0.125);
      (y1.toExponential(5) === y2.toExponential(5)) && (y1 += 0.125);
    }
    c0 = this._map(x, y),
    c1 = this._map(x1, y1),
    c2 = this._map(x2, y2),
    rx = this._scaleX * radius,
    ry = this._scaleY * radius;
    this._path.push(anticlockwise ? "at " : "wa ",
                    c0.x - rx, " ", c0.y - ry, " ",
                    c0.x + rx, " ", c0.y + ry, " ",
                    c1.x, " ", c1.y, " ",
                    c2.x, " ", c2.y);
  },

  fill: function(wire, path) {
    path = path || this._path.join("");

    var rv = [], vml, zindex = 0, mix, c,
        style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    if ( (mix = COMPOSITES[this[GLOBAL_COMPO]]) ) {
      (mix === 4) ? (zindex = --this._zindex) : this._clear();
    }

    if (typeof style === "string") {
      c = _colorCache[style] || _parseColor(style);

      if (sc[1]) {
        sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
        sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
        so = this.xShadowOpacityFrom;
        sd = this.xShadowOpacityDelta;

        for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
          rv.push(VML_SHAPE_STYLE, zindex,
                  ';left:', sx,
                  'px;top:', sy, 'px',
                  wire ? VML_STROKE : VML_FILL,
                  VML_COORD, VML_PATH, path,
                  wire ? VML_VSTROKE : VML_VFILL,
                  VML_COLOR_HEAD, sc[0],
                  VML_OPACITY, so.toFixed(2),
                  wire ? strokeProps(this, 1) : "",
                  VML_END_SHAPE);
        }
      }

      rv.push(VML_SHAPE_STYLE, zindex,
              wire ? VML_STROKE : VML_FILL,
              VML_COORD, VML_PATH, path,
              wire ? VML_VSTROKE : VML_VFILL,
              VML_COLOR_HEAD, c[0],
              VML_OPACITY, c[1] * this[GLOBAL_ALPHA],
              wire ? strokeProps(this, 1) : "",
              VML_END_SHAPE);

      vml = rv.join("");
    } else {
      vml = this[FUNCS[style._type]](style, path, wire, mix, zindex, sc);
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },

  _lfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        fp = style._param,
        c0 = this._map(fp.x0, fp.y0),
        c1 = this._map(fp.x1, fp.y1),
        angle = _math.atan2(c1.x - c0.x, c1.y - c0.y) * _deg,
        color = this._gcolor(style._colorStop),
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    (angle < 0) && (angle += 360);

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }
      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push(VML_SHAPE_STYLE, zindex,
                ';left:', sx, 'px;top:', sy, 'px',
                VML_COORD, wire ? VML_STROKE : VML_FILL,
                VML_PATH, path,
                  // brush
                  wire ? VML_VSTROKE : VML_VFILL,
                  wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD,
                  wire ? 'solid' : 'gradient" method="sigma" focus="0%',
                  VML_COLOR, shadowColor[0],
                  VML_OPACITY, so.toFixed(2),
                  VML_ANGLE, angle,
                  wire ? strokeProps(this, 1) : "",
                VML_END_SHAPE);
      }
    }
    rv.push(VML_SHAPE_STYLE, zindex,
            VML_COORD, wire ? VML_STROKE : VML_FILL,
            VML_PATH, path,
              // brush
              wire ? VML_VSTROKE : VML_VFILL,
              wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD,
              wire ? 'solid' : 'gradient" method="sigma" focus="0%',
              wire ? VML_COLOR : VML_COLORS,
              wire ? _parseColor(this.xMissColor)[0] : color,
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" o:opacity2="', this[GLOBAL_ALPHA], // fill only
              VML_ANGLE, angle,
              wire ? strokeProps(this, 1) : "",
            VML_END_SHAPE);
    return rv.join("");
  },

  _rfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [], brush, v,
        fp = style._param, fsize, fposX, fposY, focusParam = "",
        color = this._gcolor(style._colorStop),
        zindex2 = 0,
        x = fp.x1 - fp.r1,
        y = fp.y1 - fp.r1,
        r1x = fp.r1 * this._scaleX,
        r1y = fp.r1 * this._scaleY,
        c0 = this._map(x, y),
        // for shadow
        si = 0, siz = _shadowWidth, so = 0, sd = 0, sx = 0, sy = 0;

    // focus
    if (!wire) {
      fsize = (fp.r0 / fp.r1);
      fposX = (1 - fsize + (fp.x0 - fp.x1) / fp.r1) / 2; // forcus position x
      fposY = (1 - fsize + (fp.y0 - fp.y1) / fp.r1) / 2; // forcus position y
    }

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      if (wire) {
        siz = this[LINE_WIDTH];
        sd = 0.2 / siz; // opacity from 0.05 to 0.25
      }

      if (wire) {
        focusParam = [VML_VSTROKE, VML_FILLTYPE_HEAD, 'tile',
                      strokeProps(this, 1)].join("");
      } else {
        focusParam = [VML_VFILL, VML_TYPE_HEAD,
                      'gradientradial" method="sigma" focussize="',
                      fsize, ',', fsize,
                      '" focusposition="', fposX, ',', fposY].join("");
      }
      for (; si < siz; so += sd, --sx, --sy, ++si) {
        rv.push('<v:oval', VML_BASE_STYLE, zindex,
                ';left:', _round(c0.x / _zoom) + sx,
                'px;top:', _round(c0.y / _zoom) + sy,
                'px;width:', r1x, 'px;height:', r1y,
                'px', wire ? VML_STROKE : VML_FILL,
                '" coordsize="11000,11000',
                focusParam, VML_OPACITY, so.toFixed(2),
                VML_COLOR, shadowColor[0],
                '" /></v:oval>');
      }
    }

    if (wire) {
      // VML has not stroke gradient
      brush = [VML_VSTROKE, VML_FILLTYPE_HEAD, 'tile', strokeProps(this, 1),
               VML_OPACITY, this[GLOBAL_ALPHA],
               VML_COLOR, _parseColor(this.xMissColor)[0]].join("");
    } else {
      // fill outside
      if (style._colorStop.length) {
        v = style._colorStop[0]; // 0 = outer color
        if (v.color[1] > 0.001) {
          if (mix === 4) { zindex2 = --this._zindex; }
          rv.push(VML_SHAPE_STYLE, zindex2,
                  VML_FILL, VML_COORD, VML_PATH, path,
                  VML_VFILL, VML_TYPE_HEAD, 'solid',
                  VML_COLOR, v.color[0],
                  VML_OPACITY, v.color[1] * this[GLOBAL_ALPHA],
                  VML_END_SHAPE);
        }
      }
      brush = [VML_VFILL, VML_TYPE_HEAD,
               'gradientradial" method="sigma" focussize="',
               fsize , ',', fsize,
               '" focusposition="', fposX, ',', fposY,
               VML_OPACITY, this[GLOBAL_ALPHA],
               '" o:opacity2="', this[GLOBAL_ALPHA],
               VML_COLORS, color].join("");
    }
    rv.push('<v:oval', VML_BASE_STYLE, zindex, // need z-index
            ';left:', _round(c0.x / _zoom),
            'px;top:', _round(c0.y / _zoom),
            'px;width:', r1x, 'px;height:', r1y, 'px',
            wire ? VML_STROKE : VML_FILL,
            '" coordsize="11000,11000', brush,
            '" /></v:oval>');
    return rv.join("");
  },

  _pfill: function(style, path, wire, mix, zindex, shadowColor) {
    var rv = [],
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0;

    if (shadowColor[1]) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = this.xShadowOpacityFrom;
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push(VML_SHAPE_STYLE, zindex,
                ';left:', sx, 'px;top:', sy, 'px',
                VML_COORD,
                wire ? VML_STROKE : VML_FILL,
                VML_PATH, path,
                  // brush
                  wire ? VML_VSTROKE: VML_VFILL,
                  wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD, 'solid',
                  wire ? strokeProps(this, 1) : "",
                  VML_COLOR, shadowColor[0],
                  VML_OPACITY, so.toFixed(2),
                VML_END_SHAPE);
      }
    }

    rv.push(VML_SHAPE_STYLE, zindex,
            VML_COORD,
            wire ? VML_STROKE : VML_FILL,
            VML_PATH, path,
              // brush
              wire ? VML_VSTROKE : VML_VFILL,
              wire ? VML_FILLTYPE_HEAD : VML_TYPE_HEAD, 'tile',
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" src="', style._src,
              wire ? strokeProps(this, 1) : "",
            VML_END_SHAPE);

    return rv.join("");
  },

  clip: function() {
    this._clipPath = this._clipRect + " x " + this._path.join("");
  },

  _clippy: function(vml) {
    if (!this.xClipStyle) {
      var bg = _style.getBGColor(this._elm, 1);
      this.xClipStyle = bg[0];
    }
    return [vml, '<v:shape style="position:absolute;width:10px;height:10px',
            VML_FILL, VML_COORD, VML_PATH, this._clipPath,
            VML_VFILL, VML_TYPE_HEAD, 'solid', VML_COLOR, this.xClipStyle,
            VML_END_SHAPE].join("");
  },

  // === Text ==============================================
  fillText: function(text, x, y, maxWidth, wire) {
    text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");
    var style = wire ? this[STROKE_STYLE] : this[FILL_STYLE],
        types = (typeof style === "string") ? 0 : style._type,
        rv = [], vml, align = this[TEXT_ALIGN], dir = "ltr", c,
        font = parseFont(this[FONT], this.canvas),
        m = this._mtx, zindex = 0,
        fp, c0, c1, // for grad
        skew = [m[0].toFixed(3) + ',' + m[3].toFixed(3) + ',' +
                m[1].toFixed(3) + ',' + m[4].toFixed(3) + ',0,0'].join(""),
        skewOffset,
        delta = 1000, left = 0, right = delta,
        offset = { x: 0, y: 0 },
        // for shadow
        si = 0, so = 0, sd = 0, sx = 0, sy = 0,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    switch (align) {
    case "end": dir = "rtl"; // break;
    case "start":
      align = this._elm.uuCanvasDirection === dir ? "left" : "right"
    }
    switch (align) {
    case "center": left = right = delta / 2; break;
    case "right": left = delta, right = 0.05;
    }
    if (this[TEXT_BASELINE] === "top") {
      // text margin-top fine tuning
      offset.y = font.size /
          (FONT_SCALES[font.rawfamily.split(",")[0].toUpperCase()] ||
           this.xTextMarginTop);
    }
    skewOffset = this._map(x + offset.x, y + offset.y);

    if (sc[1] && !this.xShadowBlur) {
      sx = _shadowWidth / 2 + this[SHADOW_OFFSET_X];
      sy = _shadowWidth / 2 + this[SHADOW_OFFSET_Y];
      so = _max(this.xShadowOpacityFrom + 0.9, 1);
      sd = this.xShadowOpacityDelta;

      for (; si < _shadowWidth; so += sd, --sx, --sy, ++si) {
        rv.push('<v:line',
                VML_BASE_STYLE, zindex, ';width:1px;height:1px;left:', sx,
                'px;top:', sy, 'px',
                VML_FILL, '" from="', -left, ' 0" to="', right,
                ' 0.05" coordsize="100 100">',
                '<v:fill color="', sc[0],
                '" opacity="', so.toFixed(2), '" />',
                '<v:skew on="t" matrix="', skew ,'" ',
                ' offset="', _round(skewOffset.x / _zoom), ',',
                             _round(skewOffset.y / _zoom),
                '" origin="', left ,' 0" />',
                '<v:path textpathok="t" />',
                '<v:textpath on="t" string="', toHTMLEntity(text),
                '" style="v-text-align:', align,
                ';font:', toHTMLEntity(font.formal),
                '" /></v:line>');
      }
    }

    rv.push('<v:line',
            VML_BASE_STYLE, zindex, ';width:1px;height:1px',
            VML_FILL, '" from="', -left, ' 0" to="', right,
            ' 0.05" coordsize="100 100">');

    switch (types) {
    case 0:
      c = _colorCache[style] || _parseColor(style);
      rv.push('<v:fill color="', c[0],
              '" opacity="', c[1] * this[GLOBAL_ALPHA], '" />');
      break;
    case 1:
    case 2:
      fp = style._param;
      c0 = this._map(fp.x0, fp.y0);
      c1 = this._map(fp.x1, fp.y1);
      rv.push('<v:fill type="gradient" method="sigma" focus="0%',
              VML_COLORS, this._gcolor(style._colorStop),
              VML_OPACITY, this[GLOBAL_ALPHA],
              '" o:opacity2="', this[GLOBAL_ALPHA],
              VML_ANGLE,
              _math.atan2(c1.x - c0.x, c1.y - c0.y) * _deg,
              '" />');
      break;
    case 3:
      rv.push('<v:fill position="0,0" type="tile" src="',
              style._src, '" />');
      break;
    }
    rv.push('<v:skew on="t" matrix="', skew ,'" ',
            ' offset="', _round(skewOffset.x / _zoom), ',',
                         _round(skewOffset.y / _zoom),
            '" origin="', left ,' 0" />',
            '<v:path textpathok="t" />',
            '<v:textpath on="t" string="', toHTMLEntity(text),
            '" style="v-text-align:', align,
            ';font:', toHTMLEntity(font.formal),
            '" /></v:line>');
    vml = rv.join("");
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },
  // drawing images
  // drawImage(image, dx, dy)
  // drawImage(image, dx, dy, dw, dh)
  // drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
  drawImage: function(image) {
    var info = detectDrawImageArg.apply(this, arguments),
        method = info.az === 3 ? "image" : "scale",
        dx = info.dx,
        dy = info.dy,
        dw = info.dw,
        dh = info.dh,
        sx = info.sx,
        sy = info.sy,
        sw = info.sw,
        sh = info.sh,
        iw = info.dim.w,
        ih = info.dim.h,
        rv = [], vml, m,
        frag = [], sfrag, tfrag, // code fragment
        i = 0, iz, me = this, c0, zindex = 0,
        ieMode8 = _doc.documentMode >= 8,
        prefix = ieMode8 ? "-ms-filter:'" : "filter:", // filter prefix
        postfix = ieMode8 ? "'" : "",
        sizeTrans, // 0: none size transform, 1: size transform
        // for shadow
        si = 0, so = 0, sd = 0, shx = 0, shy = 0, shw = _shadowWidth,
        sc = _colorCache[this[SHADOW_COLOR]] ||
             _parseColor(this[SHADOW_COLOR]);

    function trans(m, x, y, w, h) {
      var c1 = me._map(x, y),
          c2 = me._map(x + w, y),
          c3 = me._map(x + w, y + h),
          c4 = me._map(x, y + h);
      return [";padding:0 ",
              _round(_max(c1.x, c2.x, c3.x, c4.x) / _zoom), "px ",
              _round(_max(c1.y, c2.y, c3.y, c4.y) / _zoom), "px 0;",
              prefix, DX_PFX, ".Matrix(M11=", m[0], ",M12=", m[3],
                ",M21=", m[1], ",M22=", m[4],
                ",Dx=", _round(c1.x / _zoom),
                ",Dy=", _round(c1.y / _zoom), ")", postfix].join("");
    }

    switch (COMPOSITES[this[GLOBAL_COMPO]]) {
    case  4: zindex = --this._zindex; break;
    case 10: this._clear();
    }

    if ("src" in image) { // image is HTMLImageElement
      c0 = this._map(dx, dy);

      if (this.xImageRender) {
        rv.push(
          '<v:image', VML_BASE_STYLE, zindex,
          ';width:',    dw,
          'px;height:', dh,
          'px;left:', _round(c0.x / _zoom),
          'px;top:',  _round(c0.y / _zoom),
          'px" coordsize="100,100',
          '" src="', image.src,
          '" cropleft="',   sx / iw,
          '" croptop="',    sy / ih,
          '" cropright="',  (iw - sx - sw) / iw,
          '" cropbottom="', (ih - sy - sh) / ih,
          '" />');
      } else {
        sizeTrans = (sx || sy); // 0: none size transform, 1: size transform
        tfrag = this._efx ? trans(this._mtx, dx, dy, dw, dh) : '';

        frag = [
          // shadow only
          [ '<div', VML_BASE_STYLE, zindex - 10,
              ';left:$1px;top:$2px', tfrag, '">'].join(""),
          [ '<div style="position:relative;overflow:hidden;width:',
              _round(dw), 'px;height:', _round(dh), 'px">'].join(""),
          !sizeTrans ? "" : [
            '<div style="width:', _ceil(dw + sx * dw / sw),
              'px;height:', _ceil(dh + sy * dh / sh),
              'px;',
              prefix, DX_PFX,
              '.Matrix(Dx=', (-sx * dw / sw).toFixed(3),
                     ',Dy=', (-sy * dh / sh).toFixed(3), ')',
              postfix, '">'].join(""),
          [ '<div style="width:', _round(iw * dw / sw),
              'px;height:', _round(ih * dh / sh),
              'px;'].join(""),
          // shadow only
          [ 'background-color:', sc[0], ';',
            prefix, DX_PFX, '.Alpha(opacity=$3)', postfix].join(""),
          // alphaloader
          [ prefix, DX_PFX, '.AlphaImageLoader(src=',
            image.src, ',SizingMethod=',
            method, ')', postfix].join(""),
          [ '"></div>',
              sizeTrans ? '</div>' : '', '</div></div>'].join("")
        ];

        if (sc[1]) {
          shx = shw / 2 + this[SHADOW_OFFSET_X];
          shy = shw / 2 + this[SHADOW_OFFSET_Y];
          so = this.xShadowOpacityFrom;
          sd = this.xShadowOpacityDelta;

          sfrag = [frag[0], frag[1], frag[2], frag[3],
                   frag[4], frag[6]].join("");
          for (; si < shw; so += sd, --shx, --shy, ++si) {
            rv.push(
              sfrag.replace(/\$1/, this._efx ? shx : _round(c0.x / _zoom) + shx)
                   .replace(/\$2/, this._efx ? shy : _round(c0.y / _zoom) + shy)
                   .replace(/\$3/, (so * 100).toFixed(2)));
          }
        }

        rv.push('<div', VML_BASE_STYLE, zindex);
        if (this._efx) {
          rv.push(tfrag, '">');
        } else { // 1:1 scale
          rv.push(';top:', _round(c0.y / _zoom),
                  'px;left:', _round(c0.x / _zoom), 'px">')
        }
        rv.push(frag[1], frag[2], frag[3], frag[5], frag[6]);
      }
      vml = rv.join("");
    } else {
      c0 = this._map(dx, dy);
      switch (info.az) {
      case 3: // 1:1 scale
              rv.push('<div', VML_BASE_STYLE, zindex,
                      ';left:', _round(c0.x / _zoom),
                      'px;top:', _round(c0.y / _zoom), 'px">')
              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div>');
              break;
      case 5:
              m = _matrix.multiply(_matrix.scale(dw / iw, dh / ih), this._mtx);
              rv.push('<div', VML_BASE_STYLE, zindex,
                      trans(m, dx, dy, dw, dh),
                      '"><div style="width:',  _round(iw * dw / sw),
                                 'px;height:', _round(ih * dh / sh), 'px">');
              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div></div>');
              break;
      case 9: // buggy(not impl)
              m = _matrix.multiply(_matrix.scale(dw / sw, dh / sh), this._mtx);
  //          m = _matrix.multiply(_matrix.translate(dx, dy), m);
              rv.push('<div', VML_BASE_STYLE, zindex,
                      ';overflow:hidden',
                      trans(m, dx, dy, dw, dh), '">');

              iz = image._ctx2d._history.length;

              for (; i < iz; ++i) {
                rv.push(image._ctx2d._history[i]);
              }
              rv.push('</div>');
              break;
      }
      vml = rv.join("");
/*
      // effect CSS::opacity and filter::opacity
      vml = vml.replace(/opacity=\"([\d\.]+)\"/g, function(m, opa) {
        return 'opacity="' + (opa * me[GLOBAL_ALPHA]).toFixed(3) + '"';
      }).replace(/opacity=([\d\.]+)/g, function(m, opa) {
        return 'opacity=' + (opa * me[GLOBAL_ALPHA]).toFixed(3);
      });
 */
    }
    !this.xFlyweight &&
      this._history.push(this._clipPath ? (vml = this._clippy(vml)) : vml);
    this._elm.insertAdjacentHTML("BeforeEnd", vml);
  },

  // === Pixel manipulation ================================
  // === Gradient ==========================================
  createLinearGradient: function(x0, y0, x1, y1) {
    return new Grad(1, // 1:gradient
                    { x0: x0, y0: y0, x1: x1, y1: y1 }, 1);
  },

  createRadialGradient: function(x0, y0, r0, x1, y1, r1) {
    return new Grad(2, // 2:gradientradial
                    { x0: x0, y0: y0, r0: r0, x1: x1, y1: y1, r1: r1 }, 1);
  },

  createPattern: function(image, repetition) {
    return new Patt(image, repetition);
  },

  // build Gradation Color
  _gcolor: function(ary) {
    var rv = [], i = 0, iz = ary.length;
    for (; i < iz; ++i) {
      rv.push(ary[i].offset + " " + ary[i].color[0]);
    }
    return rv.join(",");
  }
});

// === Extend Shadow API ===================================
// - setShadow() - deprecated(uuCanvas1.0.2)
// - getShadow() - deprecated(uuCanvas1.0.2)
function _setShadow(me,      // @param this:
                    color,   // @param ColorString: shadow color
                    offsetX, // @param Number: offset X
                    offsetY, // @param Number: offset Y
                    blur) {  // @param Number: blur
  me[SHADOWS[0]] = color;
  me[SHADOWS[1]] = offsetX;
  me[SHADOWS[2]] = offsetY;
  me[SHADOWS[3]] = blur;

  // Gecko don't maintain the shadow parameters(in Firefox2 and Firefox3)
  //    ctx.shadowColor = "gray";
  //    ctx.shadowBlur = 4;
  //    alert(ctx.shadowColor)  ->  "null"
  //    alert(ctx.shadowBlur)   ->  0
  me._shadow = [color, offsetX, offsetY, blur]
}

function _getShadow(me) { // @return Hash: { shadowColor, shadowOffsetX,
                          //                 shadowOffsetY, shadowBlur }
  return {
    shadowColor:   me[SHADOWS[0]] || me._shadow[0],
    shadowOffsetX: me[SHADOWS[1]] || me._shadow[1],
    shadowOffsetY: me[SHADOWS[2]] || me._shadow[2],
    shadowBlur:    me[SHADOWS[3]] || me._shadow[3]
  };
}

if (_ie) {
  _mix(SL2D.prototype, {
    setShadow: function(color, offsetX, offsetY, blur) {
      _setShadow(this, color, offsetX, offsetY, blur);
    },
    getShadow: function() {
      return _getShadow();
    }
  });
  _mix(VML2D.prototype, {
    setShadow: function(color, offsetX, offsetY, blur) {
      _setShadow(this, color, offsetX, offsetY, blur);
    },
    getShadow: function() {
      return _getShadow();
    }
  });
} else {
  // wrapper
  _crc2d._save = _crc2d.save;
  _crc2d._restore = _crc2d.restore;

  _mix(_crc2d, {
    _shadow: [TRANSPARENT, 0, 0, 0],
    _stack: [],
    save: function() {
      this._stack.push([this[FONT], this[TEXT_ALIGN], this[TEXT_BASELINE],
                        _mix([], this._shadow)]);
      this._save();
    },
    restore: function() {
      this._restore();
      if (this._stack.length) { // for Opera9.5+, Firefox2, Firefox3
        var last = this._stack.pop();
        this[FONT] = last[0];
        this[TEXT_ALIGN] = last[1];
        this[TEXT_BASELINE] = last[2];
        this._shadow = last[3];
      }
    },
    setShadow: function(color, offsetX, offsetY, blur) {
      _setShadow(this, color, offsetX, offsetY, blur);
    },
    getShadow: function() {
      return _getShadow();
    }
  });

  if (_gecko && _egver <= 1.9) {
    _crc2d.__defineSetter__(SHADOWS[0], function(c) { this._shadow[0] = c; });
    _crc2d.__defineSetter__(SHADOWS[1], function(x) { this._shadow[1] = x; });
    _crc2d.__defineSetter__(SHADOWS[2], function(y) { this._shadow[2] = y; });
    _crc2d.__defineSetter__(SHADOWS[3], function(b) { this._shadow[3] = b; });
    _crc2d.__defineGetter__(SHADOWS[0], function() { return this._shadow[0]; });
    _crc2d.__defineGetter__(SHADOWS[1], function() { return this._shadow[1]; });
    _crc2d.__defineGetter__(SHADOWS[2], function() { return this._shadow[2]; });
    _crc2d.__defineGetter__(SHADOWS[3], function() { return this._shadow[3]; });
  }
}

// === Extend Text API =====================================
// for Opera9.2+, Opera10.0, Firefox2(Gecko1.8.1), Firefox3(Gecko1.9), Chrome1+
// - fillText()
// - strokeText()
// - measureText()
// - unsupported shadow
// - unsupported pattern
// - unsupported gradation
// - unsupported matrix transform
if ((_gecko && _egver <= 1.9) ||
    (_opera && _uaver <= 10)) {

  // wrapper
  _crc2d._clearRect = _crc2d.clearRect;
  _crc2d.xAutoTextRender = 1; // 1 = auto;

  _mix(_crc2d, {
    font:         "10px sans-serif", // for Firefox3
    textAlign:    "start",
    textBaseline: "top",    // spec: "alphabetic"
    xMissColor:   "#000",
    xTextMarginTop: 1.3,
    clearRect: function(x, y, w, h) {
      var fn = clearRectDOM;
      if (this.xAutoTextRender) {
        if (_gecko && _egver === 1.9) {
          fn = clearRectMoz;
        } else if (_opera && _uaver >= 9.5 && _uaver <= 10) {
          fn = clearRectSVG;
        }
      }
      fn(this, x, y, w, h);
    },
    fillText: function(text, x, y, maxWidth, wire) {
      var fn = fillTextDOM;
      if (this.xAutoTextRender) {
        if (_gecko && _egver === 1.9) {
          fn = fillTextMoz;
        } else if (_opera && _uaver >= 9.5 && _uaver <= 10) {
          fn = fillTextSVG;
        }
      }
      fn(this, text, x, y, maxWidth, wire);
    },
    strokeText: function(text, x, y, maxWidth) {
      this.fillText(text, x, y, maxWidth, 1);
    },
    measureText: function(text) {
      var metric = getTextMetric(text, this[FONT]);
      return new TextMetrics(metric.w, metric.h);
    }
  });
} else if (_chrome && _uaver <= 2) { // Chrome3 strokeText() implemented
  _crc2d.strokeText = function(text, x, y, maxWidth) {
    this.save();
    this[FILL_STYLE] = this[STROKE_STYLE];
    this.fillText(text, x, y, maxWidth);
    this.restore();
  }
}

function clearTextView(me) {
  var i = 1, iz = me.canvas._canvasTextView.length;
  for (; i < iz; ++i) {
    me.canvas._canvasTextView[i].textContent = "";
  }
}

function clearRectMoz(me, x, y, w, h) {
  me._clearRect(x, y, w, h);
}

function clearRectDOM(me, x, y, w, h) {
  if (me.canvas._canvasTextView &&
      !x && !y && w == me.canvas.width && h == me.canvas.height) {
    clearTextView(me);
  }
  me._clearRect(x, y, w, h);
}

function clearRectSVG(me, x, y, w, h) {
  me._clearRect(x, y, w, h);
}

function fillTextMoz(me, text, x, y, maxWidth, wire) {
  var align = me[TEXT_ALIGN], dir = "ltr",
      metric = getTextMetric(text, me[FONT]),
      offX = 0, offY = 0,
      // for shadow
      si = 0, so = 0, sd = 0,
      sc = _colorCache[me._shadow[0]] ||
           _parseColor(me._shadow[0]);

  switch (align) {
  case "end": dir = "rtl"; // break;
  case "start":
    align = _runstyle(me.canvas, "").direction === dir ? "left" : "right"
  }
  if (align === "center") {
    offX = metric.w / 2;
  } else if (align === "right") {
    offX = metric.w;
  }
  offY = (metric.h + metric.h / 2) / 2; // emulate textBaseline="top"

  me.save();
  me[GLOBAL_COMPO] = "source-over";
  me.mozTextStyle = me.font;
  me.translate(x - offX, y + offY);
  if (wire) {
    me[FILL_STYLE] = me[STROKE_STYLE];
  }

  if (sc[1]) {
    so = _max(this.xShadowOpacityFrom + 0.9, 1);
    sd = this.xShadowOpacityDelta;

    me.save();
    me.translate(_shadowWidth / 2 + me._shadow[1],
                 _shadowWidth / 2 + me._shadow[2]);
    for (; si < _shadowWidth; so += sd, ++si) {
      me.translate(-1, -1);
      me[GLOBAL_ALPHA] = so.toFixed(2);
      me[FILL_STYLE] = sc[0];
      me.mozDrawText(text);
    }
    me.restore();
  }

  me.mozDrawText(text);
  // http://d.hatena.ne.jp/uupaa/20090506/1241572019
  me.fillRect(0,0,0,0); // force redraw(Firefox3.0 bug)
  me.restore();
}

function fillTextDOM(me, text, x, y, maxWidth, wire) {
  var canvas = me.canvas, // HTMLCanvasElement
      view, layer, sc, name,
      offX = 0, metric, align = me[TEXT_ALIGN], dir = "ltr";

  if (canvas._canvasLayerView) {
    view = canvas._canvasLayerView._view;
    canvas._canvasTextView = [view];
  } else if (!canvas._canvasTextView) {
//  view = canvas.parentNode.appendChild(_doc.createElement("div"));
    view = _doc.body.appendChild(_doc.createElement("div"));
    view.style.position = "absolute";
    view.style.overflow = "hidden";
    canvas._canvasTextView = [view];

    // reposition
    function repos(attr) {
      function getPos(elm) {
        var x = 0, y = 0, r;
        if (elm.getBoundingClientRect) {
          r = elm.getBoundingClientRect();
          x = r.left + pageXOffset;
          y = r.top  + pageYOffset;
        } else {
          while (elm) {
            x += elm.offsetLeft || 0;
            y += elm.offsetTop  || 0;
            elm = elm.offsetParent;
          }
        }
        return { x: x, y: y };
      }

      try {
        var rect, style = _runstyle(me.canvas, "");
        if (attr & 1) {
          rect = getPos(me.canvas);
        } else {
          rect = { x: _int(style.left), y: _int(style.top) };
        }
        _mix(me.canvas._canvasTextView[0].style, {
//        zIndex: (_int(style.zIndex) || 0) + 1, // Fx2"auto" -> 1
          height: _int(canvas.height) + "px",
          width: _int(canvas.width) + "px",
          left: rect.x + "px",
          top: rect.y + "px",
          visibility: style.visibility,
          display: style.display,
          opacity: _float(style.opacity)
        });
        if (!_gecko) {
          _mix(me.canvas._canvasTextView[0].style, {
            zIndex: (_int(style.zIndex) || 0) + 1
          });
        }
      } catch (err) {}
    }
    function onAttr(evt) {
      var attr = HIT_PROPS2[evt.attrName] || 0;
      if (attr) {
        (attr & 1) && clearTextView(me); // clear
        repos(attr);
      }
    }
    repos(3);
    canvas.addEventListener("DOMAttrModified", onAttr, false);
    setInterval(function() { repos(3); }, 1000); // delay 1sec
  } else {
    view = canvas._canvasTextView[0];
  }
  // Firefox2: shadowColor is always null
  if (_gecko) {
    sc = _colorCache[me._shadow[0]] ||
         _parseColor(me._shadow[0]);
  } else {
    sc = _colorCache[me[SHADOW_COLOR]] ||
         _parseColor(me[SHADOW_COLOR]);
  }

  metric = getTextMetric(text, me[FONT]);
  switch (align) {
  case "end": dir = "rtl"; // break;
  case "start":
    align = _runstyle(me.canvas, "").direction === dir ? "left" : "right"
  }
  if (align === "center") {
    offX = metric.w / 2;
  } else if (align === "right") {
    offX = metric.w;
  }

  layer = view.appendChild(_doc.createElement("div"));
  _mix(layer.style, {
    font: me[FONT],
    position: "absolute",
    opacity: me[GLOBAL_ALPHA],
    height: _int(metric.h * 1.2) + "px",
    width: _int(metric.w * 1.2) + "px", // avoid word wrap
    left: (x - offX) + "px",
    top: y + "px"
  });

  if (sc[1]) {
    layer.style.textShadow = [me[SHADOWS[1]] + "px",
                              me[SHADOWS[2]] + "px",
                              me[SHADOWS[3]] + "px",
                              me[SHADOWS[0]]].join(" ");
  }
  name = wire ? STROKE_STYLE : FILL_STYLE;
  if (typeof me[name] === "string") {
    layer.style.color = me[name];
  }
  layer.textContent = text;
  canvas._canvasTextView.push(layer);
}

function fillTextSVG(me, text, x, y, maxWidth, wire) {
  text = text.replace(/(\t|\v|\f|\r\n|\r|\n)/g, " ");

  function svge(name) {
    return _doc.createElementNS("http://www.w3.org/2000/svg", name);
  }
  function attr(elm, hash) {
    for (var i in hash) {
      elm.setAttribute(i, hash[i]);
    }
  }
  function filter(svg, sx, sy, sb, sc) {
    var e = [];
    svg.appendChild(e[0] = svge("defs"));
      e[0].appendChild(e[1] = svge("filter"));
        e[1].appendChild(e[2] = svge("feGaussianBlur"));
        e[1].appendChild(e[3] = svge("feOffset"));
        e[1].appendChild(e[4] = svge("feFlood"));
        e[1].appendChild(e[5] = svge("feComposite"));
        e[1].appendChild(e[6] = svge("feMerge"));
          e[6].appendChild(e[7] = svge("feMergeNode"));
          e[6].appendChild(e[8] = svge("feMergeNode"));
    attr(e[1], {
      id:             "dropshadow",
      filterUnits:    "userSpaceOnUse"
    });
    attr(e[2], {
      "in":           "SourceAlpha",
      stdDeviation:   (sb < 8) ? sb / 2 : _math.sqrt(sb * 2)
    });
    attr(e[3], {
      dx:             sx,
      dy:             sy,
      result:         "offsetblur"
    });
    attr(e[4], {
      "flood-color":   sc[0],
      "flood-opacity": sc[1]
    });
    attr(e[5], {
      in2:            "offsetblur",
      operator:       "in"
    });
    attr(e[8], {
      "in":           "SourceGraphic"
    });
  }

  var style = wire ? me[STROKE_STYLE] : me[FILL_STYLE],
      types = (typeof style === "string") ? 0 : style._type,
      align = me[TEXT_ALIGN],
      dir = _runstyle(me.canvas, "").direction === "ltr",
      font = parseFont(me[FONT], me.canvas),
      metric = getTextMetric(text, me[FONT]),
      svg = svge("svg"),
      txt = svge("text"),
      sc = _colorCache[me[SHADOW_COLOR]] ||
           _parseColor(me[SHADOW_COLOR]),
      offset = { x: 0, y: 0 },
      margin = 100,
      validFontFamily;

  switch (align) {
  case "left":   align = "start"; break;
  case "center": align = "middle"; break;
  case "right":  align = "end"; break;
  case "start":  align = dir ? "start" : "end"; break;
  case "end":    align = dir ? "end"   : "start";
  }
  switch (align) {
  case "middle": offset.x = metric.w / 2; break;
  case "end":    offset.x = metric.w;
  }
  if (me[TEXT_BASELINE] === "top") {
    // text margin-top fine tuning
    offset.y = font.size /
        (FONT_SCALES[font.rawfamily.split(",")[0].toUpperCase()] ||
         me.xTextMarginTop);
  }
  attr(svg, {
    width:  metric.w + margin,
    height: metric.h + margin
  });
  if (sc[1]) {
    filter(svg, me[SHADOW_OFFSET_X], me[SHADOW_OFFSET_Y],
           me[SHADOW_BLUR], sc);
    attr(txt, {
      filter: "url(#dropshadow)"
    });
  }
  attr(txt, {
    x:              0 + margin / 2 + offset.x,
    y:              offset.y + offset.y / 2.4 + margin / 2,
    fill:           types ? me.xMissColor : style,
    "text-anchor":  align,
    "font-style":   font.style,
    "font-variant": font.variant,
    "font-size":    font.size + "px",
    "font-weight":  font.weight,
    "font-family":  font.family
  });
  validFontFamily = txt.getAttribute("font-family");
  if (!validFontFamily.replace(/[\"\']/g, "")) {
    return; // Opera9.5, Opera9.6 buggy
  }
  svg.appendChild(txt);
  txt.appendChild(_doc.createTextNode(text));

  _doc.body.appendChild(svg);
  me.save();
  me[GLOBAL_COMPO] = "source-over";
  try {
    me.drawImage(svg, x - margin / 2 - offset.x, y - margin / 2);
  } catch(err) {} // Opera9.2x
  me.restore();
  _doc.body.removeChild(svg);
}

// --- initialize ---
function initCanvas() {
  var lc = /loaded|complete/, rs = "readyState", fn,
      fnready ="onreadystatechange";

  if (!_ie) {
    if (_doc.getElementsByTagName("canvas").length) { // window.loaded state
      ++_canvasReady;
    } else if (_opera) {
      addEventListener("load", function() {
        ++_canvasReady;
      }, false);
    } else if (_webkit && _doc[rs]) {
      fn = function() {
        lc.test(_doc[rs]) ? ++_canvasReady : setTimeout(fn, 0);
      };
      fn();
    } else if (_gecko) {
      _doc.addEventListener("DOMContentLoaded", function() {
        ++_canvasReady;
      }, false);
    } else {
      ++_canvasReady;
    }
    return;
  }
  // --- IE part ---
  function initIE() {
    var v, node = _doc.getElementsByTagName("canvas"), i = node.length;
    while (i--) {
      v = node[i];
      _canvas.init(node[i],
          (!_slver || (" " + v.className + " ").indexOf(" vml ") >= 0));
    }
    ++_canvasReady;
  }

  if (lc.test(_doc[rs])) { // DOM already
    initIE();
  } else {
    fn = function() {
      lc.test(_doc[rs]) && (initIE(), _doc.detachEvent(fnready, fn));
    };
    _doc.attachEvent(fnready, fn);
  }
};

// --- export ---
_win.uuCanvas = _canvas; // window.uuCanvas
_canvas.SL2D = SL2D;
_canvas.VML2D = VML2D;
if (_ie) {
  _doc.createElement("canvas"); // dummy
  _win.CanvasRenderingContext2D = function() {};
  _win.CanvasGradient = Grad;
  _win.CanvasPattern = Patt;
}
initCanvas();

})(); // uuCanvas scope

// === uuLayer ===
// depend: uuMeta, uuCanvas, uuStyle, uuImage
/*

--- layer functions ---
new uuLayer(view, width = "auto", height = "auto")
uuLayer.getLayerInstance(elm) - return LayerObject or null

--- view operations ---
uuLayer.view
uuLayer.resizeView(width, height)
uuLayer.getViewInfo() - return { clid, cctx, front, rear,
                                 zmax, zmin, zorder, length }

--- layer operations ---
uuLayer.createLayer(id, type, hide = 0, back = 0,
                    width = void 0, height = void 0) - return new layer element
uuLayer.appendLayer(id, node, hide = 0) - return node
uuLayer.removeLayer(id) - return this
uuLayer.resizeLayer(id, width, height) - return this
uuLayer.refLayer(id) - return layer element
uuLayer.bringLayer(id, tgt) - return this
uuLayer.moveLayer(id = "", x, y, diff = false) - return this
uuLayer.showLayer(id = "") - return this
uuLayer.hideLayer(id = "") - return this
uuLayer.getLayerOpacity(id) - return 0.0 ~ 1.0
uuLayer.setLayerOpacity(id = "", opacity = 1.0, diff = false) - return this

  --- canvas 2D context operations ---
  uuLayer.getContext(id = "") - return canvas context or undefined
  uuLayer.push(id) - return this
  uuLayer.pop() - return this

    --- canvas 2D context style operations ---
    uuLayer.alphas(globalAlpha) - return this
    uuLayer.fills(fillStyle) - return this
    uuLayer.wires(strokeStyle, lineWidth = undef) - return this
    uuLayer.fonts(font) - return this
    uuLayer.lines(lineWidth) - return this
    uuLayer.shadows(color = undef,
                    x = undef, y = undef, blur = undef) - return this
    uuLayer.sets(propHash) - return this
    uuLayer.gets(propHash) - return { prop: value, ... }

    --- canvas 2D context drawing operations ---
    uuLayer.clear(x = 0, y = 0,
                  w = canvas.width, h = canvas.height) - return this
    uuLayer.save() - return this
    uuLayer.restore() - return this
    uuLayer.scale(x, y) - return this
    uuLayer.translate(x, y) - return this
    uuLayer.rotate(90 or "90deg" or "1.2rad") - return this
    uuLayer.transform(m11, m12, m21, m22, dx, dy) - return this
    uuLayer.begin(x = undef, y = undef) - return this
    uuLayer.move(x, y) - return this
    uuLayer.line(x, y) - return this
    uuLayer.curve(a0, a1, a2, a3,
                  a4 = undef, a5 = undef) - return this
    uuLayer.clip() - return this
    uuLayer.arc(x, y, r, a0 = "0deg",
                         a1 = "360deg", clock = 1) - return this
    uuLayer.draw(wire = 0) - return this
    uuLayer.close() - return this
    uuLayer.text(text, x = 0, y = 0,
                 wire = 0, maxWidth = undef) - return this
    uuLayer.measureText(text) - return { width, height }
    uuLayer.poly([point, ...], wire = 0) - return this
    uuLayer.box(x, y, w, h, r = 0, wire = 0) - return this
    uuLayer.boxpath(x, y, w, h, r = 0) - return this
    uuLayer.metabo(x, y, w, h, r = 0,
                   bulge = 10, wire = 0) - return this
    uuLayer.circle(x, y, w, h, r, wire = 0) - return this
    uuLayer.dots(x, y, w, h, {palette},
                 [data, ...], index = 0) - return this
    uuLayer.linearGrad(x1, y1,
                       x2, y2, [offset, ...],
                               [color, ...]) - return CanvasGradient
    uuLayer.radialGrad(x1, y1, r1,
                       x2, y2, r2, [offset, ...],
                                   [color, ...]) - return CanvasGradient
    uuLayer.pattern(image, pattern = "repeat") - return CanvasPattern
    uuLayer.image(image, arg1, arg2, arg3, arg4,
                         arg5, arg6, arg7, arg8) - return this

    --- canvas 2D context convenient operations ---

    uuLayer.fitImage(image) - return this
    uuLayer.grid(size = 10, unit = 5,
                 color = "skyblue", color2 = "steelblue") - return this
    uuLayer.angleGlossy(x, y, preset, extend) - return this
    uuLayer.metaboGlossy(x, y, preset, extend) - return this
    uuLayer.jellyBean(x, y, preset, extend) - return this
 */
(function() {
var _layer, // inner namespace
    _mm = uuMeta,
    _style = uuStyle,
    _image = uuImage,
    _doc = document,
    _ie = _mm.ie,
    _opera = _mm.opera,
    _int = parseInt,
    _float = parseFloat,
    _math = Math,
    _rad = _math.PI / 180, // Math.toRadians
//  _deg = 180 / _math.PI, // Math.toDegrees
    _runstyle = _mm.runstyle,
    _mix = _mm.mix,
    _instance = {}, // { uid: layer }
    SHADOWS = { shadowColor:    1,
                shadowOffsetX:  2,
                shadowOffsetY:  3,
                shadowBlur:     4 };

_layer =
    function(view,     // @param Node: layer container
             width,    // @param Number: view width
                       //        /CSSString(= "auto"): 300 "300px" "auto"
             height) { // @param Number: view height
                       //        /CSSString(= "auto"): 150 "150px" "auto"
  var uid = _mm.uid(view),
      vs = _ie ? view[_runstyle]
               : _runstyle(view, ""),
      w = (width  === void 0 || width  === "auto") ? "auto"
                                                   : _int(width) + "px",
      h = (height === void 0 || height === "auto") ? "auto"
                                                   : _int(height) + "px";

  if (uid in _instance) {
    return _instance[uid]; // already
  }

  // uuLayer.view - public property
  this.view = view;

  this._layer = {}; // Hash( { id: elm, ctx, chain } )
  // for canvas context
  this._stack = [];    // context stack
  this._cctx = void 0; // current canvas context
  this._clid = void 0; // current canvas context layer id

  _mix(view.style, {
    width: w,
    height: h,
    zIndex: _int(vs.zIndex) || 0,
    overflow: "hidden"
  });
  if (vs.position === "static") {
    view.style.position = "relative";
  }

  _instance[uid] = this;
};

// --- layer functions ---

// uuLayer.getLayerInstance
_layer.getLayerInstance = function(elm) { // @param Node:
                                          // @return LayerObject/null:
  var uid = _mm.uid(elm);
  if (uid in _instance) {
    return _instance[uid];
  }
  return null;
};


_layer.prototype = {

// --- view operations ---

  // uuLayer.resizeView - resize view
  resizeView: function(width,    // @param CSSPixelUnitString: "300px"
                       height) { // @param CSSPixelUnitString: "150px"
    this.view.style.width  = width;
    this.view.style.height = height;
  },

  // uuLayer.getViewInfo - get view state
  getViewInfo: function() { // @return Hash: { clid, cctx, front, rear,
                            //                 zmax, zmin, zorder, length }
    var hash = this._layer, v, i, j = 0,
        front, rear, max = 0, min = 0, order = [];

    for (i in hash) {
      ++j;
      v = _int(hash[i].elm.style.zIndex);
      (!front || max <= v) && (max = v, front = i);
      (!rear  || min >= v) && (min = v, rear = i);
      order[v] = i;
    }
    return { clid:  this._clid,   // current layer id
             cctx:  this._cctx,   // current canvas context
             front: front || "",  // front layer id
             rear:  rear  || "",  // rear layer id
             zmax:  max || 0,     // front layer z-index
             zmin:  min || 0,     // rear layer z-index
             zorder: order,       // order["a", "b", "c"]
             length: j };         // layer.length
  },

// --- layer operations ---

  // uuLayer.createLayer - create child layer
  createLayer:
      function(id,      // @param String: layer id
               type,    // @param String: "canvas", "vmlcanvas",
                        //                "div", "img", etc...
               hide,    // @param Boolean(= false): true = hidden layer
               back,    // @param Boolean(= false): back insertion
               width,   // @param Number(= undefined): canvas,image width
               height) {// @param Number(= undefined): canvas,image height
                        // @return Node: new layer element
    type = type.toLowerCase();

    var elm, es, ctx, v = this.view, viewInfo = this.getViewInfo();

    if (/canvas$/.test(type)) {
      elm = _doc.createElement("canvas");
      elm.width  = (width !== void 0) ? width :
                   (v.style.width === "auto") ? v.offsetWidth :
                   _int(v.style.width);
      elm.height = (height !== void 0) ? height :
                   (v.style.height === "auto") ? v.offsetHeight :
                   _int(v.style.height);
      elm = uuCanvas.init(elm, type === "vmlcanvas");
      es = elm.style;

      back ? v.insertBefore(elm, v.firstChild)
           : v.appendChild(elm);

      ctx = elm.getContext("2d");
      ctx.textBaseline = "top"; // force
      // set current context
      this._clid = id;
      this._cctx = ctx;
    } else {
      elm = _doc.createElement(type);
      es = elm.style;

      back ? v.insertBefore(elm, v.firstChild)
           : v.appendChild(elm);

      if (type !== "img") {
        es.width  = v.style.width;
        es.height = v.style.height;
      } else {
        (width  !== void 0) && (elm.width  = width);
        (height !== void 0) && (elm.height = height);
      }
    }
    es.zIndex = back ? (viewInfo.zmin - 1) : (viewInfo.zmax + 1);
    es.display = hide ? "none": "";
    es.position = "absolute";
    es.top = "0";
    es.left = "0";

    this._layer[id] = { elm: elm, ctx: ctx, chain: [] };
    return elm;
  },

  // uuLayer.appendLayer - add node
  appendLayer:
      function(id,      // @param String: layer id
               node,    // @param Node:
               hide) {  // @param Boolean(= false): true = hidden layer
                        // @return Node:
    var elm = node, type = elm.tagName.toLowerCase(), ctx;

    if (type === "canvas") {
      ctx = elm.getContext("2d");
      ctx.textBaseline = "top"; // force
      // set current context
      this._clid = id;
      this._cctx = ctx;
    }
    elm.style.zIndex = this.getViewInfo().length;

    this._layer[id] = { elm: elm, ctx: ctx, chain: [] };
    return elm;
  },

  // uuLayer.removeLayer - remove child layer
  removeLayer: function(id) { // @param String: layer id
                              // @return this:
    if (id in this._layer) {
      var v = this._layer[id], i = 0, iz = v.chain.length;
      for (; i < iz; ++i) {
        v.chain[i].elm.parentNode.removeChild(v.chain[i].elm);
        delete this._layer[v.chain[i]];
      }
      v.elm.parentNode.removeChild(v.elm);
      v.elm = void 0;
      delete this._layer[id];

      // reset context stack
      this._stack = [];
      this._clid = void 0;
      this._cctx = void 0;
    }
    return this;
  },

  // uuLayer.resizeLayer - resize child layer
  resizeLayer: function(id,       // @param String: layer id
                        width,    // @param Number: pixel width
                        height) { // @param Number: pixel height
                                  // @return this:
    var node = this._layer[id].elm;

    switch (node.tagName.toLowerCase()) {
    case "canvas":
    case "img":
      node.width  = width;
      node.height = height;
      break;
    default:
      node.style.width  = width  + "px";
      node.style.height = height + "px";
    }
    return this;
  },

  // uuLayer.refLayer - refer child layer
  refLayer: function(id) { // @param String: layer id
                           // @return Node: layer element
    return this._layer[id].elm;
  },

  // uuLayer.bringLayer - lift child layer
  //    bringLayer("a") is bring to front
  //    bringLayer("a", "b") is bring to layer("b")
  bringLayer: function(id,    // @param String: move layer id
                       tgt) { // @param String(= ""): target layer id
                              // @return this:
    var ly = this._layer, z, i,
        p1 = _int(ly[id].elm.style.zIndex),
        p2 = _int(ly[tgt || this.getViewInfo().front].elm.style.zIndex);

    if (p1 < p2) {
      for (i in ly) {
        z = _int(ly[i].elm.style.zIndex);
        if (z > p1 && z <= p2) {
          ly[i].elm.style.zIndex = z - 1;
        }
      }
      ly[id].elm.style.zIndex = p2;
    } else if (p1 === p2) {
      ly[id].elm.style.zIndex = p1 + 1;
    }
    return this;
  },

  _chains: function(id) {
    var rv = {}, v, i = 0, iz;

    rv[id] = v = this._layer[id];
    for (iz = v.chain.length; i < iz; ++i) {
      rv[v.chain[i]] = this._layer[v.chain[i]];
    }
    return rv;
  },

  // uuLayer.moveLayer - set absolute/relative child layer position
  //    moveLayer("", 100, 100) is move all layers to pos(100px, 100px)
  //    moveLayer("a", 100, 100, 1) is move layer("a") to pos(+100px, +100px)
  moveLayer:
      function(id,     // @param String(= ""): layer id
               x,      // @param Number: style.left value(unit px)
               y,      // @param Number: style.top value(unit px)
               diff) { // @param Boolean(= false): difference,
                       //           false = x and y is absolute value
                       //           true = x and y is relative value
                       // @return this:
    x = _int(x), y = _int(y), diff = diff || 0;

    var hash = id ? this._chains(id) : this._layer, v, i,
        doPixel = _ie || _opera;

    for (i in hash) {
      v = hash[i].elm.style;
      if (doPixel) {
        v.pixelLeft = (diff ? v.pixelLeft : 0) + x;
        v.pixelTop  = (diff ? v.pixelTop  : 0) + y;
      } else {
        v.left = (diff ? _int(v.left) : 0) + x + "px";
        v.top  = (diff ? _int(v.top)  : 0) + y + "px";
      }
    }
    return this;
  },

  // uuLayer.showLayer - show child layer
  //    showLayer() is show all layers
  //    showLayer("a") is show layer("a")
  showLayer: function(id) { // @param String(= ""): layer id
                            // @return this:
    return this._showLayer(id, 0);
  },

  // uuLayer.hideLayer - hide child layer
  //    hideLayer() is hide all layers
  //    hideLayer("a") is hide layer("a")
  hideLayer: function(id) { // @param String(= ""): layer id
                            // @return this:
    return this._showLayer(id, 1);
  },

  _showLayer: function(id, hide) {
    var hash = id ? this._chains(id) : this._layer, i;

    for (i in hash) {
      hash[i].elm.style.display = hide ? "none" : "block";
    }
    return this;
  },

  // uuLayer.getLayerOpacity - get child layer opacity value(from 0.0 to 1.0)
  getLayerOpacity:
      function(id) { // @param String: layer id
                     // @return Number: float value(min: 0.0, max: 1.0)
    return _style.getOpacity(this._layer[id].elm);
  },

  // uuLayer.setLayerOpacity - set child layer opacity value(from 0.0 to 1.0)
  //    setLayerOpacity("", 0.1, 1) is set all layers opacity(+0.1)
  //    setLayerOpacity("a", 0.5) is set layer("a") opacity(0.5)
  setLayerOpacity:
      function(id,      // @param String(= ""): layer id
               opacity, // @param Number(= 1.0): float value(0.0 to 1.0)
               diff) {  // @param Boolean(= false):
                        // @return this:
    opacity = _float(opacity === void 0 ? 1.0 : opacity);
    diff = diff || 0;

    var hash = id ? this._chains(id) : this._layer, i;

    for (i in hash) {
      _style.setOpacity(hash[i].elm, opacity, diff);
    }
    return this;
  },

// --- canvas 2D context operations ---

  // uuLayer.getContext - get canvas context
  getContext: function(id) { // @param String(= ""): layer id
                             //                      "" is current context
                             // @return CanvasRenderingContext2D/undefined:
    if (id) {
      return (id in this._layer) ? this._layer[id].ctx
                                 : void 0;
    }
    return this._cctx;
  },

  // uuLayer.push - push current context
  push: function(id) { // @param String: layer id
                       // @return this:
    this._clid && this._stack.push(this._clid);
    this._clid = id;
    this._cctx = this._layer[id].ctx;
    return this;
  },

  // uuLayer.pop - pop current context
  pop: function() { // @param String: layer id
                    // @return this:
    if (this._stack.length) {
      this._clid = this._stack.pop();
      this._cctx = this._layer[this._clid].ctx;
    }
    return this;
  },

// --- canvas 2D context style operations ---

  // uuLayer.alphas - set globalAlpha
  //    globalAlpha: from 0.0 to 1.0
  alphas: function(globalAlpha) { // @param Number: globalAlpha
                                  // @return this:
    this._cctx.globalAlpha = globalAlpha;
    return this;
  },

  // uuLayer.fills - set fillStyle
  fills: function(fillStyle) { // @param String/Object: fillStyle
                               // @return this:
    this._cctx.fillStyle = fillStyle;
    return this;
  },

  // uuLayer.wires - set strokeStyle
  wires:
      function(strokeStyle, // @param String/Object: strokeStyle
               lineWidth) { // @param Number(= undefined): lineWidth(from 1.0)
                            // @return this:
    this._cctx.strokeStyle = strokeStyle;
    if (lineWidth !== void 0) {
      this._cctx.lineWidth = lineWidth;
    }
    return this;
  },

  // uuLayer.fonts - set font style
  //    font: CSS font style value. (eg: "10px sans-serif")
  fonts: function(font) { // @param CSSFontString: font
                          // @return this:
    this._cctx.font = font;
    return this;
  },

  // uuLayer.lines - set lineWidth
  lines: function(lineWidth) { // @param Number: lineWidth(from 1.0)
                               // @return this:
    this._cctx.lineWidth = lineWidth;
    return this;
  },

  // uuLayer.setShadow - set shadow styles
  shadows: function(color,  // @param String(= undefined): shadowColor
                    x,      // @param Number(= undefined): shadowOffsetX
                    y,      // @param Number(= undefined): shadowOffsetY
                    blur) { // @param Number(= undefined): shadowBlur
                            // @return this:
    var ctx = this._cctx;
    (color !== void 0) && (ctx.shadowColor   = ctx._shadow[0] = color);
    (x     !== void 0) && (ctx.shadowOffsetX = ctx._shadow[1] = x);
    (y     !== void 0) && (ctx.shadowOffsetY = ctx._shadow[2] = y);
    (blur  !== void 0) && (ctx.shadowBlur    = ctx._shadow[3] = blur);
    return this;
  },

  // uuLayer.sets - set styles
  sets: function(propHash) { // @param Hash: { prop: value, ... }
                             // @return this:
    var ctx = this._cctx, i, v = 0;
    for (i in propHash) {
      if ( (v = SHADOWS[i]) ) {
        ctx._shadow[v - 1] = propHash[i];
      }
      ctx[i] = propHash[i];
    }
    return this;
  },

  // uuLayer.gets - get styles
  gets: function(propHash) { // @param Hash: { font: "", ... }
                             // @return Hash: { font: "32pt Arial", ... }
    var ctx = this._cctx, i, rv = {}, v = 0;
    for (i in propHash) {
      if ( (v = SHADOWS[i]) ) {
        rv[i] = ctx[i] || ctx._shadow[v - 1];
      } else {
        rv[i] = ctx[i];
      }
    }
    return rv;
  },

// --- canvas 2D context drawing operations ---

  // uuLayer.clear - clear rect
  clear: function(x,   // @param Number(= 0): position x
                  y,   // @param Number(= 0): position y
                  w,   // @param Number(= canvas.width):  width
                  h) { // @param Number(= canvas.height): height
                       // @return this:
    var ctx = this._cctx;
    ctx.clearRect(x || 0, y || 0,
                  w || ctx.canvas.width, h || ctx.canvas.height);
    return this;
  },

  // uuLayer.save
  save: function() { // @return this:
    this._cctx.save();
    return this;
  },

  // uuLayer.restore
  restore: function() { // @return this:
    this._cctx.restore();
    return this;
  },

  // uuLayer.scale - scale
  scale: function(w,   // @param Number: width scale
                  h) { // @param Number: height scale
                       // @return this:
    this._cctx.scale(w, h);
    return this;
  },

  // uuLayer.translate - offset origin
  translate: function(x,   // @param Number: offset x
                      y) { // @param Number: offset y
                           // @return this:
    this._cctx.translate(x, y);
    return this;
  },

  // uuLayer.rotate - rotate
  //    angle: 360 or "360deg" or "1.5rad"
  rotate: function(angle) { // @param Number/String: angle
                            // @return this:
    var ang = (/rad$/.test(angle + "") ? 1 : _rad) * _float(angle);
    this._cctx.rotate(ang);
    return this;
  },

  // uuLayer.transform
  transform: function(m11, m12, m21, m22, dx, dy) {
    this._cctx.transform(m11, m12, m21, m22, dx, dy);
    return this;
  },

  // uuLayer.begin - beginPath + moveTo
  begin: function(x,    // @param Number(= undefined): move x
                  y) {  // @param Number(= undefined): move y
                        // @return this:
    this._cctx.beginPath();
    (x !== void 0 && y !== void 0) && this._cctx.moveTo(x || 0, y || 0);
    return this;
  },

  // uuLayer.move - moveTo
  move: function(x,   // @param Number: move x
                 y) { // @param Number: move y
                      // @return this:
    this._cctx.moveTo(x, y);
    return this;
  },

  // uuLayer.line - lineTo
  line: function(x,   // @param Number: move x
                 y) { // @param Number: move y
                      // @return this:
    this._cctx.lineTo(x, y);
    return this;
  },

  // uuLayer.curve - quadraticCurveTo or bezierCurveTo
  curve: function(a0,   // @param Number:
                  a1,   // @param Number:
                  a2,   // @param Number:
                  a3,   // @param Number:
                  a4,   // @param Number(= undefined):
                  a5) { // @param Number(= undefined):
                        // @return this:
    if (a4 === void 0) {
      // cpx, cpy, x, y
      this._cctx.quadraticCurveTo(a0, a1, a2, a3);
    } else {
      // cp1x, cp1y, cp2x, cp2y, x, y
      this._cctx.bezierCurveTo(a0, a1, a2, a3, a4, a5);
    }
    return this;
  },

  // uuLayer.clip - clip
  clip: function() { // @return this:
    this._cctx.clip();
    return this;
  },

  // uuLayer.arc - arc
  //    a0 and a1: 360 or "360deg" or "0rad"
  arc: function(x,       // @param Number:
                y,       // @param Number:
                r,       // @param Number:
                a0,      // @param Number/String(= "0deg"): angle0
                a1,      // @param Number/String(= "360deg"): angle1
                clock) { // @param Boolean(= true):
                         // @return this:
    a0 = a0 || "0deg";
    a1 = a1 || "360deg";
    var ang0 = (/rad$/.test(a0 + "") ? 1 : _rad) * _float(a0),
        ang1 = (/rad$/.test(a1 + "") ? 1 : _rad) * _float(a1);
    this._cctx.arc(x, y, r, ang0, ang1,
                   (clock === void 0) ? 0 : !clock);
    return this;
  },

  // uuLayer.draw - fill or stroke
  draw: function(wire) { // @param Boolean(= false):
                         // @return this:
    wire ? this._cctx.stroke() : this._cctx.fill();
    return this;
  },

  // uuLayer.close - closePath
  close: function() { // @return this:
    this._cctx.closePath();
    return this;
  },

  // uuLayer.text
  text: function(text,       // @param String:
                 x,          // @param Number(= 0):
                 y,          // @param Number(= 0):
                 wire,       // @param Boolean(= false):
                 maxWidth) { // @param Number(= undefined):
                             // @return this:
    var fn = wire ? "strokeText" : "fillText";
    if (maxWidth === void 0) { // for Firefox3.5 bug
      this._cctx[fn](text, x || 0, y || 0);
    } else {
      this._cctx[fn](text, x || 0, y || 0, maxWidth);
    }
    return this;
  },

  // uuLayer.measureText - get text dimension
  measureText: function(text) { // @param String:
                                // @return TextMetrics: { width, height }
    this._cctx.measureText(text);
  },

  // uuLayer.poly - poly line + fill
  poly: function(point,  // @param PointArray: Array( [x0, y0, x1, y1, ... ] )
                 wire) { // @param Boolean(= false):
                         // @return this:
    var p = point || [0, 0], i, iz = point.length;
    this.close().begin(p[0], p[1]);
    for (i = 2; i < iz; i += 2) {
      this.line(p[i], p[i + 1]);
    }
    this.draw(wire).close();
  },

  // uuLayer.box - add box path, fill inside
  box: function(x,      // @param Number:
                y,      // @param Number:
                w,      // @param Number:
                h,      // @param Number:
                r,      // @param Number(= 0):
                wire) { // @param Boolean(= false):
                        // @return this:
    return this.boxpath(x, y, w, h, r).draw(wire);
  },

  // uuLayer.boxpath - add box path
  boxpath: function(x,   // @param Number:
                    y,   // @param Number:
                    w,   // @param Number:
                    h,   // @param Number:
                    r) { // @param Number(= 0):
                         // @return this:
    if (!r) {
      this._cctx.rect(x, y, w, h);
      return this;
    }
    if (r < 0) {
      r = 0;
    }
    // round corner
    return this.close().begin(x, y + r).line(x, y + h - r).
                curve(x, y + h, x + r, y + h).line(x + w - r, y + h).
                curve(x + w, y + h, x + w, y + h - r).line(x + w, y + r).
                curve(x + w, y, x + w - r, y).line(x + r, y).
                curve(x, y, x, y + r).
                close();
  },

  // uuLayer.metabolic - metabolic box
  metabo: function(x,      // @param Number:
                   y,      // @param Number:
                   w,      // @param Number:
                   h,      // @param Number:
                   r,      // @param Number(= 0):
                   bulge,  // @param Number(= 10):
                   wire) { // @param Boolean(= false):
                           // @return this:
    r = r || 0;
    bulge = (bulge === void 0) ? 10 : bulge;

    if (bulge) {
      return this.close().begin(x, y + r).line(x, y + h - r). // 1
                  curve(x + w * 0.5, y + h + bulge, x + w, y + h - r). // 2,3,4
                  line(x + w, y + r). // 5
                  curve(x + w, y, x + w - r, y).line(x + r, y). // 6,7
                  curve(x, y, x, y + r).draw(wire). // 8
                  close();
    }
    return this.close().begin(x, y + r).line(x, y + h). // 1
                line(x + w, y + h). // 2,3,4
                line(x + w, y + r). // 5
                curve(x + w, y, x + w - r, y).line(x + r, y). // 6,7
                curve(x, y, x, y + r).draw(wire). // 8
                close();
  },

  // uuLayer.circle - circle + fill
  circle: function(x,      // @param Number:
                   y,      // @param Number:
                   w,      // @param Number:
                   h,      // @param Number:
                   r,      // @param Number:
                   wire) { // @param Boolean(= false):
                           // @return this:
    if (w === h) { // circle
      return this.close().begin(x, y).arc(x, y, r).
                  draw(wire).close();
    }
    // ellipse(oval) not impl.
    return this;
  },

  // uuLayer.dots - draw dot with palette
  //    palette: Hash( { paletteNo: "#ffffff" or { r,g,b,a }, ...} )
  //    data: [paletteNo, paletteNo, ...]
  dots: function(x,       // @param Number:
                 y,       // @param Number:
                 w,       // @param Number:
                 h,       // @param Number:
                 palette, // @param Hash: color palette,
                 data,    // @param Array: dot data
                 index) { // @param Number(= 0): start data index
                          // @return this:
    var ctx = this._cctx, i = 0, j = 0, p, v, idx = index || 0;
    for (; j < h; ++j) {
      for (i = 0; i < w; ++i) {
        v = data[idx + i + j * w];
        if (!(v in palette)) {
          continue;
        }
        p = palette[v];
        if (typeof p === "string") {
          ctx.fillStyle = p;
        } else if (p.a) { // skip alpha = 0
          ctx.fillStyle = "rgba(" + [p.r, p.g, p.b, p.a].join(",") + ")";
        }
        ctx.fillRect(x + i, y + j, 1, 1);
      }
    }
    return this;
  },

  // uuLayer.linearGrad - create linear gradient
  linearGrad:
      function(x1,      // @param Number:
               y1,      // @param Number:
               x2,      // @param Number:
               y2,      // @param Number:
               offset,  // @param Array: [offset, ...], offset from 0.0 to 1.0
               color) { // @param Number: [color, ...]
                        // @return CanvasGradient:
    var rv = this._cctx.createLinearGradient(x1, y1, x2, y2),
        i = 0, iz = offset.length;

    for (; i < iz; ++i) {
      rv.addColorStop(offset[i], color[i]);
    }
    return rv; // CanvasGradient object
  },

  // uuLayer.radialGrad - create radial gradient
  radialGrad:
      function(x1,      // @param Number:
               y1,      // @param Number:
               r1,      // @param Number:
               x2,      // @param Number:
               y2,      // @param Number:
               r2,      // @param Number:
               offset,  // @param Array: [offset, ...], offset from 0.0 to 1.0
               color) { // @param Number: [color, ...]
                        // @return CanvasGradient:
    var rv = this._cctx.createRadialGradient(x1, y1, r1, x2, y2, r2),
        i = 0, iz = offset.length;

    for (; i < iz; ++i) {
      rv.addColorStop(offset[i], color[i]);
    }
    return rv; // CanvasGradient object
  },

  // uuLayer.pattern - create pattern
  pattern: function(image,     // @param HTMLImageElement
                               //        /HTMLCanvasElement:
                    pattern) { // @param String(= "repeat"):
                               // @return CanvasPattern:
    return this._cctx.createPattern(image, pattern || "repeat");
  },

  // uuLayer.image - image
  image: function(image,  // @param HTMLImageElement
                          //        /HTMLCanvasElement:
                  arg1,   // @param Number(= undefined):
                  arg2,   // @param Number(= undefined):
                  arg3,   // @param Number(= undefined):
                  arg4,   // @param Number(= undefined):
                  arg5,   // @param Number(= undefined):
                  arg6,   // @param Number(= undefined):
                  arg7,   // @param Number(= undefined):
                  arg8) { // @param Number(= undefined):
                          // @return this:
    switch (arguments.length) {
    case 1: this._cctx.drawImage(image, 0, 0); break;
    case 3: this._cctx.drawImage(image, arg1, arg2); break;
    case 5: this._cctx.drawImage(image, arg1, arg2, arg3, arg4); break;
    case 9: this._cctx.drawImage(image, arg1, arg2, arg3, arg4,
                                        arg5, arg6, arg7, arg8); break;
    default: throw "";
    }
    return this;
  },

// --- canvas 2D context convenient operations ---

  // uuLayer.fitImage - image fitting(auto-scaling)
  fitImage: function(image) { // @param HTMLImageElement: image element
                              // @return this:
    var ctx = this._cctx,
        dim = _image.getActualDimension(image),
        cw = _int(ctx.canvas.width),
        ch = _int(ctx.canvas.height),
        sw = dim.w,
        sh = dim.h,
        dx = (sw <= cw) ? _math.floor((cw - sw) / 2) : 0,
        dy = (sh <= ch) ? _math.floor((ch - sh) / 2) : 0,
        dw = (sw <= cw) ? sw : cw,
        dh = (sh <= ch) ? sh : ch;
    ctx.drawImage(image, 0, 0, sw, sh, dx, dy, dw, dh);
    return this;
  },

  // uuLayer.grid - draw hatch
  grid: function(size,     // @param Number(= 10):
                 unit,     // @param Number(= 5):
                 color,    // @param String(= "skyblue"):
                 color2) { // @param String(= "steelblue"):
                           // @return this:
    size = size || 10, unit = unit || 5;
    color = color || "skyblue", color2 = color2 || "steelblue";
    var x = size, y = size, i = 1, j = 1,
        w = _int(this._cctx.canvas.width),
        h = _int(this._cctx.canvas.height);

    for (; x < w; ++i, x += size) {
      this.wires((i % unit) ? color : color2).
           begin(x, 0).line(x, h).draw(1).close();
    }
    for (; y < h; ++j, y += size) {
      this.wires((j % unit) ? color : color2).
           begin(0, y).line(w, y).draw(1).close();
    }
    return this;
  },

  // uuLayer.angleGlossy
  //    override: Hash ( { gcolor1, gcolor2, overlayAlpha, w, h, r, angle } )
  angleGlossy: function(x,        // @param Number: move x
                        y,        // @param Number: move y
                        preset,   // @param String(= "GBLACK"): preset name
                        extend) { // @param Hash(= undefined): extend style
                                  // @return this:
    preset = (preset || "GBLACK").toUpperCase();
    preset = (preset in this.preset) ? this.preset[preset] : {};
    extend = _mix({ w: 100, h: 100, r: 12, angle: 0 }, preset, extend || {});

    var w = extend.w, h = extend.h, r = extend.r, angle = extend.angle,
        oa = extend.overlayAlpha, b = 3, dist = 0; // bevel size

    if (angle < -45) { angle = -45; }
    if (angle >  45) { angle =  45; }

    this.fills(this.linearGrad(x, y, x, y + h,
                               [0.0, 1.0], [extend.gcolor1, extend.gcolor2])).
         begin().box(x, y, w, h, r).close().
         fills("rgba(255,255,255," + oa + ")");

    switch (angle) {
    case 45:  this.begin(x + b, y + b + r).line(x + b, y + h - b * 2).
                    line(x + w - b * 2, y + b).line(x + b + r, y + b).
                    curve(x, y, x + b, y + b + r).draw().close(); break;
    case -45: this.begin(x - b + w, y + b + r).line(x - b + w, y + h - b * 2).
                    line(x + b * 2, y + b).line(x - b - r + w, y + b).
                    curve(x + w, y, x - b + w, y + b + r).draw().close(); break;
    default:  dist = ((h - b * 2) / 45 * angle) / 2;
              this.begin(x + b, y + b + r).
                    line(x + b, y + (h / 2) - b * 2 + dist).
                    line(x + w - b, y + (h / 2) - b * 2 - dist).
                    line(x + w - b, y + b + r).
                    curve(x + w, y, x + w - r, y + b).line(x + b + r, y + b).
                    curve(x, y, x + b, y + b + r).draw().close();
    }
    return this;
  },

  // uuLayer.metaboGlossy
  metaboGlossy: function(x,        // @param Number: move x
                         y,        // @param Number: move y
                         preset,   // @param String(= "GBLACK"): preset name
                         extend) { // @param Hash(= undefined): extend style
                                   // @return this:
    preset = (preset || "GBLACK").toUpperCase();
    preset = (preset in this.preset) ? this.preset[preset] : {};
    extend = _mix({ w: 100, h: 50, r: 12, bulge: 6 }, preset, extend || {});

    var w = extend.w, h = extend.h, r = extend.r, bulge = extend.bulge,
        oa = extend.overlayAlpha, r2 = r > 4 ? r - 4 : 0, b = 3; // bevel size

    this.fills(this.linearGrad(x, y, x, y + h,
                               [0.0, 1.0], [extend.gcolor1, extend.gcolor2])).
          begin().box(x, y, w, h, r).close().
          fills("rgba(255,255,255," + oa + ")").
          begin().metabo(x + b, y + b, w - b * 2, h * 0.5, r2, bulge).close();
    return this;
  },

  // uuLayer.jellyBean
  jellyBean: function(x,        // @param Number: move x
                      y,        // @param Number: move y
                      preset,   // @param String(= "GBLACK"): preset name
                      extend) { // @param Hash(= undefined): extend style
                                // @return this:
    extend = _mix({ w: 100, h: 30, r: 16, bulge: 6 }, extend || {});
    this.metaboGlossy(x, y, preset, extend);
    return this;
  },

// --- ---
  createReflectionLayer:
      function(id,             // @param String: layer id
               image,          // @param Node: image element
               hide,           // @param Boolean(= false): true = hidden layer
               x,              // @param Number(= 0): x
               y,              // @param Number(= 0): y
               width,          // @param Number(= image width): width
               height,         // @param Number(= image height): height
               mirrorHeight,   // @param Number(= 0.625): mirror height(0 to 1)
               offsetX,        // @param Number(= 0): offset X
               offsetY) {      // @param Number(= 0): offset Y
                               // @return Node: new layer element
    var dim = _image.getActualDimension(image),
        w = width || dim.w,
        h = height || dim.h,
        ox = offsetX || 0,
        oy = offsetY || 0,
        mh = (mirrorHeight === void 0) ? 0.625 : mirrorHeight;

    if (!_ie) {
      return this._addReflection(id, image, hide,
                                 dim, x || 0, y || 0, w, h, mh, ox, oy);
    }
    return this._addReflectionIE(id, image, hide,
                                 dim, x || 0, y || 0, w, h, mh, ox, oy);
  },

  _addReflection: function(id, image, hide, dim, x, y, w, h, mh, ox, oy) {
    var elm = this.createLayer(id, "canvas", hide),
        grad,
        sx = w / dim.w, // scale x
        sy = h / dim.h; // scale y

    elm.width  = w;
    elm.height = h + h * mh;

    this.moveLayer(id, x, y);

    x = 0, y = 0;
    grad = this.linearGrad(x, y + h, x, y + h + h * mh,
                           [mh, 0],
                           ["rgba(255, 255, 255, 1.0)",
                            "rgba(255, 255, 255, 0.3)"]),
    this.clear().
      save().translate(x, y).scale(sx, sy).image(image).restore().
      save().translate(x + ox, y + h * 2 + oy).
             scale(sx, -sy).image(image).restore().
      save().sets({ globalCompositeOperation: "destination-out" }).
             fills(grad).box(x, y + h, w, h * mh).restore();
    return elm;
  },

  _addReflectionIE: function(id, image, hide, dim, x, y, w, h, mh, ox, oy) {
    var img1 = this.createLayer(id, "img", hide),
        img2 = this.createLayer(id + "_reflection", "img", hide); // mirror

    _mix(img1, { src: image.src, width: w, height: h });
    _mix(img2, { src: image.src, width: w, height: h });

    _mix(img1.style, { top: x + "px", left: y + "px" });
    _mix(img2.style, { top: (x + h + oy) + "px", left: (y + ox) + "px" });
    img2.style.filter =
        "flipv progid:DXImageTransform.Microsoft.Alpha" +
        "(opacity=70,style=1,finishOpacity=0," +
        "startx=0,starty=0,finishx=0,finishy=" + ((1 - mh) * 100) + ")";

    this._layer[id].chain.push(id + "_reflection");
    return img1;
  }
};

// --- initialize ---
(function() {
  function make(gcolo1, gcolor2, overlayAlpha) {
    return { gcolor1: gcolo1, gcolor2: gcolor2, overlayAlpha: overlayAlpha };
  }
  _layer.prototype.preset = {
    GBLACK:   make("#000",    "#333",    0.25),
    GGRAY:    make("black",   "silver",  0.38),
    GSLIVER:  make("gray",    "white",   0.38),
    GBLUE:    make("#0000a0", "#0097ff", 0.38),
    GGREEN:   make("#006400", "#00ff00", 0.38),
    GRED:     make("#400000", "#ff0000", 0.38),
    GLEMON:   make("#dfcc00", "#FFE900", 0.38),
    GGOLD:    make("#fffacd", "gold",    0.45), // lemonchiffon
    GPEACH:   make("violet",  "red",     0.38),
    GBLOODORANGE:
              make("orange",  "red",     0.38)
  };
})();

// --- export ---
window.uuLayer = _layer; // window.uuLayer

})(); // uuLayer scope

// === uuCSSValidator ===
// depend: uuColor, uuToken
/*
uuCSSValidator.width(value, rv = void 0) - return { width, valid }
uuCSSValidator.background(value, rv = void 0)
                              - return { image, repeat, position, attachment,
                                         origin, clip, rgba, valid }
uuCSSValidator["-webkit-box-shadow"](value, rv = void 0)
                              - return { color, ox, oy, blur, valid }

uuCSSValidator["-webkit-gradient"](value, rv = void 0)
                              - return { type, point, radius,
                                         offset, color, valid }
 */
(function() {
var _validator, // inner namespace
    _color = uuColor,
    _token = uuToken,
    _float = parseFloat,
    TRIM = /^\s+|\s+$/g,
    LENGTH = /^(?:[\d\.]+(%|px|em|pt|cm|mm|in|pc|px)|auto|0)$/;

_validator = {
  // uuCSSValidator.width - parse width: property
  width: function(value, // @param String: width property value
                  rv) {  // @param Hash(= undefined): result value
                         // @return Hash: {
                         //            width: "value",
                         //            valid: 1 }
    rv = (rv === void 0) ? {} : rv;
    rv.width = value;
    rv.valid = LENGTH.test(value) ? 1 : 0;
    return rv;
  },

  // uuCSSValidator.background - parse background: property
  //    background("url(...) top left, blue url(...) bottom center")
  background: function(value, // @param String: background property value
                       rv) {  // @param Hash(= undefined): result value
                              // @return Hash: {
                              //            image: ["url(...), ...],
                              //            repeat: ["repeat", ...],
                              //            position: ["0% 0%", ...],
                              //            attachment: ["scroll", ...],
                              //            origin: ["padding", ...],
                              //            clip: ["no-clip", ...],
                              //            rgba: { r,g,b,a },
                              //            valid: 1 }
    var BACKGROUND_POS = /^([\d\.]+(%|px|em|pt|cm|mm|in|pc|px)|left|center|right|top|bottom|0)$/,
        BACKGROUND_IDENT = /^(?:(-webkit-gradient\(.*?\))|(none|url\(.*?\))|(repeat|no-repeat|repeat-x|repeat-y|space|round)|(scroll|fixed|local)|(border-box|padding-box|content-box)|(no-clip))$/,
        multi = _token.splitCSSValue(value.replace(/\s+/g, " "), ","),
        i = 0, j, iz = multi.length, jz, m, v, w, ary,
        img, rpt, att, pox, poy, ori, clp, rgba,
        valid = 1;

    if (rv === void 0) {
      rv = {
        image: [], repeat: [], position: [], attachment: [], 
        origin: [], clip: []
      };
    }

    while ( (v = multi[i++]) ) {
      img = rpt = pox = poy = ori = clp = "";
      ary = _token.splitCSSValue(v.replace(TRIM, ""), " ");

      for (j = 0, jz = ary.length; valid && j < jz; ++j) {
        w = ary[j];
        if ( (m = BACKGROUND_IDENT.exec(w)) ) {
          if (m[1]) { img ? (valid = 0) : (img = m[1]); } // -webkit-gradient
          if (m[2]) { img ? (valid = 0) : (img = m[2]); } // url
          if (m[3]) { rpt ? (valid = 0) : (rpt = m[3]); } // repeat
          if (m[4]) { att ? (valid = 0) : (att = m[4]); } // attachment
          if (m[5]) { ori ? (valid = 0) : (ori = m[5]); } // origin
          if (m[6]) { clp ? (valid = 0) : (clp = m[6]); } // clip
          continue;
        } else if ( (m = BACKGROUND_POS.exec(w)) ) {
          poy ? (valid = 0)
              : pox ? (poy = m[1])
                    : (pox = m[1]);
          continue;
        } else if (i === iz &&  _color.parse(w, 0, 2)) {
          // color is permitted in <final-bg-layer>, but not in <bg-layer>
          // http://www.w3.org/TR/css3-background/
          rgba ? (valid = 0)
               : (rgba = _color.parse(w, 1)); // rgba
          continue;
        }
        // unknown token
        valid = 0;
        break;
      }
      rv.image.push(img || "none");
      rv.repeat.push(rpt || "repeat");
      rv.attachment.push(att || "scroll");
      rv.position.push((pox || "0%") + " " + (poy || "0%"));
      rv.origin.push(ori || "padding-box");
      rv.clip.push(clp || "no-clip");
    }
    rv.rgba = rgba || { r: 0, g: 0, b: 0, a: 0 }; // color
    rv.valid = valid; // valid
    return rv;
  },

  // box-shadow: <color> || <offsetX> <offsetY> <blur>
  "-webkit-box-shadow":
      function(value, // @param String: -webkit-box-shadow: value
               rv) {  // @param Hash(= undefined): result value
                      // @return Hash: {
                      //            color:  "#C0FFEE",
                      //            ox:     0,
                      //            oy:     0,
                      //            blur:   0,
                      //            valid:  1 }
    var ary = _token.splitCSSValue(value.replace(TRIM, ""), " "),
        color, ox, oy, blur, valid = 1;

    if (rv === void 0) {
      rv = {};
    }

    color = /^\d/.test(ary[0]) ? ary.pop() : ary.shift();
    ox    = ary.shift() || 0;
    oy    = ary.shift() || 0;
    blur  = ary.shift() || 0;

    rv.valid = valid;
    rv.color = color; // shadow color
    rv.ox    = ox;    // shadow offset x
    rv.oy    = oy;    // shadow offset y
    rv.blur  = blur;  // shadow blur
    return rv;
  },

  // -webkit-gradient(<type>,
  //                  <point> [, <radius>]?,
  //                  <point> [, <radius>]? [, <stop>]*)
  "-webkit-gradient":
      function(value, // @param String: -webkit-gradient() value
               rv) {  // @param Hash(= undefined): result value
                      // @return Hash: {
                      //            type:  "linear" or "radial",
                      //            point:  [],
                      //            radius: [],
                      //            offset: [],
                      //            color:  [],
                      //            valid:  1 }
    var GRAD_TYPE = /^\s*(linear|radial)$/,
        GRAD_POINT = /^\s*(?:(left)|(right)|([\d\.]+%?))\s+(?:(top)|(bottom)|([\d\.]+%?))$/,
        GRAD_RADIUS = /^\s*([\d\.]+)$/,
        GRAD_FROM = /^\s*from/,
        GRAD_TO = /^\s*to/,
        GRAD_STOP = /^\s*color-stop/,
        type = 0, point = [], radius = [], from = 0, to = 0,
        offset = [], color = [], valid = 0,
        m, mm, ary, tmpary, v, w, i = 0;

    if (rv === void 0) {
      rv = {
        type: "", point: [], radius: [], offset: [], color: []
      };
    }

    if ( (m = /^-webkit-gradient\((.*)\)$/.exec(value)) ) {
      valid = 1;
      ary = _token.splitCSSValue(m[1], ",");

      while ( valid && ( v = ary[i++]) ) {
        if (GRAD_TYPE.test(v)) {
          type ? (valid = 0)
               : (type = (v === "linear") ? 1 : 2); // 1: linear, 2: radial
        } else if ( ( mm = GRAD_POINT.exec(v)) ) {
          if (point.length >= 4) {
            valid = 0;
          } else {
            point.push(mm[1] ? "0" : mm[2] ? "100%" : mm[3]);
            point.push(mm[4] ? "0" : mm[5] ? "100%" : mm[6]);
          }
        } else if ( ( mm = GRAD_RADIUS.exec(v)) ) {
          if (type !== 2 || radius.length >= 2) {
            valid = 0;
          } else {
            radius.push(_float(mm[1]));
          }
        } else if (GRAD_FROM.test(v)) {
          if (from) {
            valid = 0;
          } else {
            v = v.replace(/\s*from\(\s*/, "").replace(/\s*\)$/, "");
            offset.push(0);
            color.push(v);
            from = 1;
          }
        } else if (GRAD_TO.test(v)) {
          if (to) {
            valid = 0;
          } else {
            v = v.replace(/\s*to\(\s*/, "").replace(/\s*\)$/, "");
            offset.push(1);
            color.push(v);
            to = 1;
          }
        } else if (GRAD_STOP.test(v)) {
          v = v.replace(/\s*color-stop\(\s*/, "").replace(/\s*\)$/, "");
          tmpary = v.split(/\s*,\s*/);
          w = tmpary.shift();
          offset.push(/%$/.test(w) ? _float(w) / 100
                                   : _float(w)); // "90%" -> 0.9
          color.push(tmpary.join(","));
        } else {
          // unknown token
          valid = 0;
        }
      }
    }
    if (point.length !== 4 ||
        (type === 2 && radius.length !== 2) ||
        !offset.length) {
      valid = 0;
    }

    rv.valid  = valid;
    rv.type   = (type === 1) ? "linear" :
                (type === 2) ? "radial" : "";
    rv.point  = point;    // ["0", "100%", "20", "20%"]
    rv.radius = radius;   // [radius1, radius2]
    rv.offset = offset;   // [0, ...]
    rv.color  = color;    // ["#C0FFEE", ... ]
    return rv;
  }
};

// --- initialize ---

// --- export ---
window.uuCSSValidator = _validator;

})();


// === uuAltCSS ===
// depend: uuMeta, uuToken, [uuCodec], uuStyleSheet, uuQuery,
//         uuCSSValidator, uuAltCSSPlus
/*
window.UUALTCSS_FORCE_MARKUP = undefined; // 1: force markup, 0: markup off
window.UUALTCSS_DISABLE_CONDITIONAL_SELECTOR = 0;
window.UUALTCSS_VALUE_VALIDATION = 0;
window.UUALTCSS_STRIP_EMBED_IMAGE = 0;
window.UUALTCSS_STRIP_INLINE_WIDTH = 0;

uuAltCSS(context = void 0, rebuild = 0, css = "")
uuAltCSS.getRuleset() - return ruleset array
uuAltCSS.getDeclaredValues(node) - return Hash/undefined
 */
(function() {
var _altcss, // inner namespace
    _mm = uuMeta,
    _mix = _mm.mix,
    _ss = uuStyleSheet,
    _query = uuQuery,
    _validator = uuCSSValidator,
    _win = window,
    _doc = document,
    _xhr, // lazy
    _uaver = _mm.uaver,
    _egver = _mm.enginever,
    _ie = _mm.ie,
    _ie6 = _ie && _uaver === 6,
    _ie7 = _ie && _uaver === 7,
    _ie67 = _ie6 || _ie7,
    _opera = _mm.opera,
    _gecko = _mm.gecko,
    _webkit = _mm.webkit,
    _safari = _mm.safari,
    _chrome = _mm.chrome,
    _plus, // lazy detection
    _codec, // lazy detection
    _imagedir = _win.UUALTCSS_IMAGE_DIR || ".",
    _gif = _imagedir.replace(/\/+$/, "") + "/1dot.gif",
    // Value Validation
    _enableValidation = _win.UUALTCSS_VALUE_VALIDATION || 0,
    // IE6, IE7: Size of an inline element is invalidated.
    //    <span style="width:100px; height:100px">inline with size</span>
    //          v
    //    <span style="width:auto; height:auto">inline without size</span>
    _stripWidth = _ie67 && (_win.UUALTCSS_STRIP_INLINE_WIDTH || 0),
    // IE6, IE7: Strip embed image
    //    <img src="data:image/*,...">
    //    <div style="background: url(data:image/*,...)">
    //          v
    //    <img src="1dot.gif">
    //    <div style="background: url(1dot.gif)">
    _stripEmbedImage = _ie67 && (_win.UUALTCSS_STRIP_EMBED_IMAGE || 0),
    _enableDocumentFragment = !(_gecko && _egver <= 1.9), // exclude Fx2, Fx3
    _ruleset = [],
    _ruleuid = 0,   // unique rule id
    _rule = {}, // unique rule
    _ssid = "uuAltCSS", // StyleSheet ID
    _root, // root node( <html> )
    _initialized = 0,
    _markup = 0,
    _boostup = 0, // opacity, position:fixed
    _cache = {}, // import cache { url: cssText }
    _specs = [], // raw data
    _data = {}, // raw data
    _lazyClearClass = [], // lazy clear className nodeList
    _lastNodeList = [],
    SPEC_E = /\w+/g,
    SPEC_ID = /#[\w\u00C0-\uFFEE\-]+/g, // (
    SPEC_NOT = /:not\(([^\)]+)\)/,
    SPEC_ATTR =
        /\[\s*(?:([^~\^$*|=\s]+)\s*([~\^$*|]?\=)\s*(["'])?(.*?)\3|([^\]\s]+))\s*\]/g,
    SPEC_CLASS = /\.[\w\u00C0-\uFFEE\-]+/g,
    SPEC_PCLASS = /:[\w\-]+(?:\(.*\))?/g,
    SPEC_PELEMENT = /::[\w\-]+/g,
    SPEC_CONTAINS = /:contains\((["'])?.*?\1\)/g,
    TRIM = /^\s+|\s+$/g,
    MEMENTO = "uuAltCSSMemento",
    COMMENT = /\/\*[^*]*\*+([^\/][^*]*\*+)*\//g,
    IMPORTS =
        /@import\s*(?:url)?[\("']+\s*([\w\.\/\+\-]+)\s*["'\)]+\s*([\w]+)?\s*;/g,
    DATA_SCHEME = /^data\:/,
    DETOXIFY_WIDTH = "width:auto;height:auto",
    // CSS Validate for Acid2
    _validatorProps = {
      width: 1,
      background: 1
    };

_altcss =
    function(context, // @param Node/IDString(= void 0): revalidation context
             rebuild, // @param Number(= 0): 0 = OFF(quick), 1 = ON(full)
             css) {   // @param CSSString(= ""): CSS text
  // lazy revalidate for :target
  (_markup || _boostup) && setTimeout(function() {
    var ctx = (typeof context === "string") ? _query.id(context) : context,
        tick = +new Date;

    ctx = (!ctx || !ctx.parentNode || ctx === _doc) ? _doc
                                                    : ctx.parentNode;
    // --- begin perf block ---
        _altcss._unbond(ctx);
        if (rebuild) {
          _specs = [], _data = {}; // clear raw data
          _altcss._init(css);
        }
        _altcss._validate(ctx);
        _initialized = 1;
    // --- end perf block ---
    _mm.debug &&
        (_win.status = (new Date - tick) + "ms");
  }, 0);
};

_mm.mix(_altcss, {
  // uuAltCSS.getRuleset
  getRuleset: function() { // @return Array: rule-set
    return _ruleset;
  },

  // uuAltCSS.getDeclaredValues - get declaration values (raw CSS String)
  getDeclaredValues:
      function(node) { // @param Node:
                       // @return Hash/undefined: { bits, order, prop }
                       //             Number: bits, 0 to 0xffffffff
                       //             String: order, comma jointed string
                       //                            has tail comma
                       //             Hash: prop { color: "red", ... }
    if (_lastNodeList.length) {

      var ary = _lastNodeList, i = 0, iz = ary.length;

      for (; i < iz; i += 2) {
        if (ary[i] === node) {
          return ary[i + 1]; // { bits, order, prop }
        }
      }
    }
    return void 0;
  },

  // unbond attrs
  _unbond: function(context) { // @param Node:
    var node, v, i, j;

    // remove class="uucss{n} ..."
    //   1. replace element.className
    //   2. remove element.uuCSSC attr
    _lazyClearClass = _query("[uuCSSC]", context);

    // remove "!important" style
    //   1. collect old style from element.uuCSSIHash
    //   2. remove element.uuCSSI attr
    if (!_ie) {
      node = _query("[uuCSSI]", context), i = 0;
      while ( (v = node[i++]) ) {
        for (j in v.uuCSSIHash) {
          v.style.removeProperty(j);
          v.style.setProperty(j, v.uuCSSIHash[j], "");
        }
        v.removeAttribute("uuCSSI"); // unmarkup
      }
    }
/*
    // remove uuCSSHover="uucsshover{n} ..."
    //   1. remove element.uuCSSHover attr
    node = _query("[uuCSSHover]", context), i = 0;
    while ( (v = node[i++]) ) {
      v.removeAttribute("uuCSSHover");
    }
 */
  },

  _init: function(css) {
    var node, v, i, hash, dstr;

    // memnto
    if (!_initialized && _ie) {
      node = _query.tag("style"), i = 0;
      while ( (v = node[i++]) ) {
        !(MEMENTO in v) && (v[MEMENTO] = v.innerHTML);
      }
    }

    // collect style sheets
    css = _altcss._cleanup(css || _altcss._imports());

    // http://d.hatena.ne.jp/uupaa/20090619/1245348504
    if (!_initialized && _ie6) {
      v = _win.name;
      _win.name = ""; // clear
      if (/UNKNOWN[^\{]+?\{|:unknown[^\{]+?\{/.test(css) && // }}}}
          "UNKNOWN" !== v) {
        _win.name = "UNKNOWN";
        _mm.blackout = 1; // stop boot process
        location.reload(false);
        return false;
      }
    }

    // decode script
    //    <script src="data:text/javascript,..">
    if (_codec && _ie67) {
      node = _query.tag("script"), i = 0;
      while ( (v = node[i++]) ) {
        if (DATA_SCHEME.test(v.src)) {
          hash = _codec.decode(v.src);
          if (hash.mime === "text/javascript") {
            dstr = String.fromCharCode.apply(null, hash.data);
            (new Function(dstr))();
          }
        }
      }
    }

    // strip embed background-image
    if (!_initialized && _stripEmbedImage) {
      // <div style="background: url(data...)"> // (
      css = css.replace(/url\(\"data\:[^\)]+\"\)/gi,
                        'url("' + _gif + '")');
    }

    // parse CSS
    _altcss._parse(css);

    // sort
    _specs.sort(function(a, b) {
      return a - b;
    });

    // create inner stylesheet
    _ss.create(_ssid);
    return true;
  },

  _validate: function(context) {
    // uuNode.cutdown - cut all nodes less than context
    function cutdown(context) { // @Node(= document.body): parent node
                                // @return DocumentFragment:
      var rv, ctx = context || _doc.body;
      if (_doc.createRange) {
        (rv = _doc.createRange()).selectNodeContents(ctx);
        return rv.extractContents(); // return DocumentFragment
      }
      rv = _doc.createDocumentFragment();
      while (ctx.firstChild) {
        rv.appendChild(ctx.removeChild(ctx.firstChild));
      }
      return rv;
    }

    var v, w, i = 0, j, k, l, iz = _specs.length, jz, kz, lz,
        spec, data, expr, ruleid, nodeuid, found, node, unique = {},
        IMP = " !important;",
/*
        skip = _ie6
             ? /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:unknown/
             : /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:unknown|:hover/,
        hover = /:hover/,
        pseudo,
 */
        skip = /^\*$|::?before$|::?after$|::?first-letter$|::?first-line$|:active|:focus|:hover|:unknown/,
        fragment, ruleset = [],
        stripEmbedImageNode, // strip embed image
        // document fragment context
        dfctx = (!context || context === _doc ||
                             context === _root) ? _doc.body : context;

    _lastNodeList = []; // reset

    for (; i < iz; ++i) {
      spec = _specs[i];
      data = _data[spec];

      for (j = 0, jz = data.length; j < jz; ++j) {
        expr = data[j].expr;

        if (skip.test(expr)) { // skip universal, pseudo-class/elements
          continue;
        }

        try {
          if (_boostup) {
            found = _plus.find(data[j].pair, expr);
          }
/*
          pseudo = 0;

          if (!_ie6 || !hover.test(expr)) {
            node = _query(expr, context);
          } else {
            node = _query(expr.replace(hover, function(m) {
              pseudo |= 0x2;
              return "";
            }), context);
          }
 */
          node = _query(expr, context);

          if (_ie || spec < 10000) {
            // make unique rule id from expr
            ruleid = _rule[expr] || (_rule[expr] = ++_ruleuid);

            // add new rule
            if (_markup) {
              // ".uucss[num] { color: red; font-size: 24pt; ... }"
              w = (spec < 10000) ? data[j].decl.join(";")
                                 : data[j].decl.join(IMP) + IMP;
              w += ";";
/*
              if (!pseudo) {
                ruleset.push(".uucss" + ruleid, w);
              } else if (pseudo & 0x2) { // 0x2: hover
                ruleset.push(".uucsshover" + ruleid, w);
              }
 */
              ruleset.push(".uucss" + ruleid, w);
            }
            for (k = 0, kz = node.length; k < kz; ++k) {
              v = node[k];
              nodeuid = _mm.uid(v); // node unique id

              // init container
              if (!(nodeuid in unique)) {
                unique[nodeuid] = {
                  node: v,
                  rules: {},
                  klass: [],
                  found: { bits: 0, prop: {}, order: "" }
                };
              }

              // regist rule if not exists
              if (!(ruleid in unique[nodeuid].rules)) {

                unique[nodeuid].rules[ruleid] = ruleid;

                if (_markup) {
/*
                  if (!pseudo) {
                    unique[nodeuid].klass.push("uucss" + ruleid);
                  } else if (pseudo & 0x2) { // 0x2: hover
                    if (!("uuCSSHover" in v)) {
                      v.uuCSSHover = ""; // bond
                    }
                    v.uuCSSHover += " uucsshover" + ruleid;
                  }
 */
                  // .uucss{n}
                  unique[nodeuid].klass.push("uucss" + ruleid);
                }

                // for Acid2 test(need UUALTCSS_STRIP_INLINE_WIDTH = 1)
                //    inline-element has neither width nor height(in IE6, IE7)
                if (_stripWidth) {
                  if (/display:inline;/i.test(w) ||
                      /^inline$/.test(v.currentStyle.display)) {
/*
                    !pseudo &&
                        _markup && unique[nodeuid].klass.push("uucssinline");
 */
                    _markup && unique[nodeuid].klass.push("uucssinline");
                  }
                }
              }
              // mixin boostup info
              if (_boostup) {
                // mixin bits
                unique[nodeuid].found.bits |= found.bits;

                // mixin declarations
                _mix(unique[nodeuid].found.prop, found.prop);

                // append declaration order
                unique[nodeuid].found.order += found.order; // "has,last-comma,"
              }
            }
          } else { // "!important" route
            for (k = 0, kz = node.length; k < kz; ++k) {
              v = node[k];
              nodeuid = _mm.uid(v); // node unique id

              if (_markup) {
                v.setAttribute("uuCSSI", 1); // bond + markup for revalidate

                // init container
                !("uuCSSIHash" in v) && (v.uuCSSIHash = {}); // bond

                for (l = 0, lz = data[j].pair.length; l < lz; ++l) {
                  w = data[j].pair[l];
                  // save
                  v.uuCSSIHash[w.prop] = v.style.getPropertyValue(w.prop);
                  // overwrite
                  v.style.setProperty(w.prop, w.val, "important");
                }
              }

              // init container
              if (!(nodeuid in unique)) {
                unique[nodeuid] = {
                  node: v,
                  rules: {},
                  klass: [],
                  found: { bits: 0, prop: {}, order: "" }
                };
              }
              // mixin boostup info
              if (_boostup) {
                // mixin bits
                unique[nodeuid].found.bits |= found.bits;

                // mixin declarations
                _mix(unique[nodeuid].found.prop, found.prop);

                // append declaration order
                unique[nodeuid].found.order += found.order; // "has,last-comma,"
              }
            }
          }
        } catch(err) {
          _mm.debug && 
              alert("validate fail: " + err);
        }
      }
    }

    // --- make boostup plan ---
    if (_boostup) {
      // create array from unique data
      for (nodeuid in unique) {
        _lastNodeList.push(unique[nodeuid].node,    // [0] = node
                           unique[nodeuid].found);  // [1] = decl info
      }
      _plus.plan(_lastNodeList, _initialized, context);
    }

    // --- collect effective nodeList ---

    // for Acid2
    //    query('<img src="data:...">') nodeList
    if (!_initialized && _stripEmbedImage) {
      stripEmbedImageNode = [], node = _query.tag("img"), i = 0;
      while ( (v = node[i++]) ) {
        DATA_SCHEME.test(v.src) && stripEmbedImageNode.push(v);
      }
    }

    _enableDocumentFragment && !_plus.deny() && (fragment = cutdown(dfctx));
    // --- begin code block ---
        // lazy - clear all rules
        _ss.removeAllRules(_ssid);
        _ruleset = [];

        // lazy - clear all className
        for (i = 0, iz = _lazyClearClass.length; i < iz; ++i) {
          v = _lazyClearClass[i];
          if (v) {
            if (_ie67 && v.getAttribute("uuCSSLock")) {
              ;
            } else {
              v.className = v.className.replace(/uucss[\w]+\s*/g, "");
              v.removeAttribute("uuCSSC");
            }
          }
        }

        // apply to className
        if (_markup) {
          for (nodeuid in unique) {
            v = unique[nodeuid].node;
            if (_ie67 && v.getAttribute("uuCSSLock")) {
              ;
            } else {
              w = v.className + " " + unique[nodeuid].klass.join(" ");
              v.className = w.replace(TRIM, "").replace(/\s+/g, " ");
              v.setAttribute("uuCSSC", "1"); // bond + markup for revalidate
            }
          }
        }
        // insert rule
        for (i = 0, iz = ruleset.length; i < iz; i += 2) {
          _ss.insertRule(_ssid, ruleset[i], ruleset[i + 1]);
          _ruleset.push(ruleset[i] + " { " + ruleset[i + 1] + " }");
        }
        // strip width
        if (_stripWidth) {
          _ss.insertRule(_ssid, ".uucssinline", DETOXIFY_WIDTH);
          _ruleset.push(".uucssinline" + " { " + DETOXIFY_WIDTH + " }");
        }
        // <img src="data:..."> -> <img src="1dot.gif">
        if (stripEmbedImageNode) {
          i = 0;
          while ( (v = stripEmbedImageNode[i++]) ) {
            v.src = _gif;
          }
        }
        // boost prevalidate
        if (_boostup) {
          _plus.prevalidate(_lastNodeList, _initialized, context);
        }
    // --- end code block ---
    _enableDocumentFragment && !_plus.deny() && dfctx.appendChild(fragment);

    // boost postvalidate
    if (_boostup) {
      _plus.postvalidate(_lastNodeList, _initialized, context);
    }

    // Opera9.5+ problem fix and Opera9.2 flicker fix
    if (_opera) {
      _enableDocumentFragment = 0;
    }
  },

  _parse: function(css) {
    // escape "{};,"
    function esc(m, q, str) {
      ++escape;
      return q + str.replace(/\{/g, "\\u007B").replace(/;/g, "\\u003B").
                     replace(/\}/g, "\\u007D").replace(/,/g, "\\u002C") + q;
    }
    // unescape "{};,"
    function unesc(str) {
      return str.replace(/\\u007B/g, "{").replace(/\\u003B/g, ";").
                 replace(/\\u007D/g, "}").replace(/\\u002C/g, ",");
    }
    // http://www.w3.org/TR/2005/WD-css3-selectors-20051215/#specificity
    function calcSpec(expr) {
      var a = 0, b = 0, c = 0;
      function B() { ++b; return ""; }

      expr.replace(SPEC_NOT, function(m, E) { return " " + E; }).
            replace(SPEC_ID, function() { ++a; return ""; }). // #id
            replace(SPEC_CLASS, B).     // .class
            replace(SPEC_CONTAINS, B).  // :contains("...")
            replace(SPEC_PELEMENT, ""). // ::pseudo-element
            replace(SPEC_PCLASS, B).    // :pseudo-class
            replace(SPEC_ATTR, B).      // [attr=value]
            replace(SPEC_E,  function() { ++c; return ""; }); // E
      return a * 100 + b * 10 + c;
    }

    var escape = 0, v, i, j, k, iz, jz, kz,
        gd1, gd2, gp1, gp2, ary, expr, decl, decls, exprs, spec,
        rex1 = /\s*\!important\s*/,
        rex2 = /\s*\!important\s*/g,
        ignore, prop, val, valid;

    v = css.replace(/(["'])(.*?)\1/g, esc);
    if (_ie) {
      v = v.replace(/^\s*\{/,   "*{").
            replace(/\}\s*\{/g, "}*{").
            replace(/\{\}/g,    "{ }"); // for IE Array.split bug
    }
    ary = v.split(/\s*\{|\}\s*/);
    !_ie && ary.pop(); // for IE Array.split bug

    if (ary.length % 2) {
      return; // parse error
    }

    for (i = 0, iz = ary.length; i < iz; i += 2) {
      expr = ary[i];
      decl = ary[i + 1].replace(TRIM, "");
      exprs = (expr + ",").split(/,+/); !_ie && exprs.pop(); // IE split bug
      decls = (decl + ";").split(/;+/); !_ie && decls.pop(); // IE split bug

      gd1 = [], gd2 = [], gp1 = [], gp2 = [];
      for (k = 0, kz = decls.length; k < kz; ++k) {
        ignore = 0;

        if (decls[k]) {
          v = decls[k].replace(/;+$/, "").replace(TRIM, "").split(/\s*:\s*/);
          prop = v.shift();
          val = v.join(":");
          val = escape ? unesc(val): val;

          if (/\\/.test(prop)) { // .parser { m\argin: 2em; };
            ++ignore;
          } else if (rex1.test(val)) { // !important
            val = val.replace(rex2, "");
            valid = (_enableValidation && _validatorProps[prop]) ?
                        _validator[prop](val).valid : 1;
            if (valid) {
              gd2.push(prop + ":" + val);
              gp2.push({ prop: prop, val: val });
            } else {
              ++ignore;
            }
          } else if (/!/.test(val)) {
            // fix #Acid2  .parser { border: 5em solid red ! error; }
            ++ignore;
          } else {
            valid = (_enableValidation && _validatorProps[prop]) ?
                        _validator[prop](val).valid : 1;
            if (valid) {
              gd1.push(prop + ":" + val);
              gp1.push({ prop: prop, val: val });
            } else {
              ++ignore;
            }
          }
          ignore && _mm.debug &&
              alert('"' + prop + ":" + val + '" ignore decl');
        }
      }
      for (j = 0, jz = exprs.length; j < jz; ++j) {
        v = (escape ? unesc(exprs[j]) : exprs[j]).replace(TRIM, "");

        // * html .parser {  background: gray; }  -> "gray"
        if (/^\s*\*\s+html/i.test(v)) {
          _mm.debug && alert(v + " ignore CSS Star hack");
          continue; // ignore rule set
        }
        spec = calcSpec(v);
        if (gd1.length) {
          !(spec in _data) && (_specs.push(spec), _data[spec] = []);
          _data[spec].push({ expr: v, decl: gd1, pair: gp1 });
        }
        if (gd2.length) { // !important
          spec += 10000;
          !(spec in _data) && (_specs.push(spec), _data[spec] = []);
          _data[spec].push({ expr: v, decl: gd2, pair: gp2 });
        }
      }
    }
  },

  _imports: function() { // @return String: minified CSS
    function imports(css, absdir) { // @import
      return css.replace(COMMENT, "").
                 replace(IMPORTS, function(m, url, media) {
        var v = toAbsURL(url, absdir);
        return imports(sync(v), toDir(v));
      });
    }

    var rv = [], absdir = toAbsURL("."), href, hash, dstr,
        node = _mm.toArray(_doc.styleSheets), v, w, i = 0,
        prop1 = _ie ? "owningElement" : "ownerNode",
        prop2 = _ie ? MEMENTO : "textContent";

    while ( (v = node[i++]) ) {
      if (!v.disabled) {
        href = v.href || "";
        if (!DATA_SCHEME.test(href)) { // execlude data:text/css,...
          if (/\.css$/.test(href)) {
            // <link>
            w = toAbsURL(v.href, absdir);
            !(w in _cache) && (_cache[w] = imports(sync(w), toDir(w)));
            rv.push(_cache[w]);
          } else {
            // <style>
            rv.push(imports(v[prop1][prop2], absdir));
          }
        }
      }
    }
    // decode datauri
    //    <link href="data:text/css,...">
    if (_codec) {
      node = _query.tag("link"), i = 0;
      while ( (v = node[i++]) ) {
        if (DATA_SCHEME.test(v.href)) {
          hash = _codec.decode(v.href);
          if (hash.mime === "text/css") {
            dstr = String.fromCharCode.apply(null, hash.data);
            w = "link" + i; // "link1"
            !(w in _cache) && (_cache[w] = imports(dstr, absdir));
            rv.push(_cache[w]);
          }
        }
      }
    }
    return rv.join("");
  },

  _cleanup: function(css) { // @param String: dirty css
                            // @return String: clean css
    return css.replace(/<!--|-->/g,  ""). // <!-- ... --> (
        replace(/url\(([^\)]*)\)/gi, function(m, data) {
          return 'url("' + data.replace(/^["']|["']$/g, "") + '")';
        }).
        replace(/\\([{};,])/g, function(m, c) { // \} -> \\u007d
          return (0x10000 + c.charCodeAt(0)).
                    toString(16).replace(/^1/, "\\\\u");
        }).
        replace(/@[^\{]+\{[^\}]*\}/g, "").  // @font-face @page
        replace(/@[^;]+\s*;/g, "").         // @charset
        replace(/\s*[\r\n]+\s*/g, " ").     // ...\r\n...
        replace(/[\u0000-\u001f]+/g, "").   // \u0009 -> "" (unicode)
        replace(/\\x[01]?[0-9a-f]/gi, "").  // "\x9"  -> "" (hex \x00~\1f)
        replace(/\\[0-3]?[0-7]/g, "").      // "\9"   -> "" (octet \0~\37)
        replace(TRIM, "");
  }
});

function toAbsURL(url, curtdir) {
  if (!/^(file|https|http)\:/.test(url)) {
    var div = _doc.createElement("div");

    div.innerHTML = '<a href="' + (curtdir || "") + url + '" />';
    url = div.firstChild ? div.firstChild.href
                         : /href\="([^"]+)"/.exec(div.innerHTML)[1];
  }
  return url;
}

function toDir(absurl) {
  var ary = absurl.split("/");
  ary.pop();
  return ary.join("/") + "/";
}

function sync(url) {
  try {
    if (!_xhr && _ie && "ActiveXObject" in _win) {
      _xhr = new ActiveXObject("Microsoft.XMLHTTP");
    }
    if (!_xhr && "XMLHttpRequest" in _win) {
      _xhr = new XMLHttpRequest();
    }
    if (_xhr) {
      _xhr.open("GET", url, false); // XDomain no check
      _xhr.send(null);
      return (_xhr.status === 200 || !_xhr.status) ? _xhr.responseText : "";
    }
  } catch (err) {}
  return "";
}

// --- initialize ---
function autoexec() {
  // --- conditional selector ---
  //  <style>
  //    div>ul { color: black }                /* for Generic browser */
  //    html.ifwebkit div>ul { color: blue }   /* for Safari, Chrome */
  //    html.ifchrome3 div>ul { color: green } /* for Google Chrome3 */
  //    html.ifopera92 div>ul { color: red }   /* for Opera9.27 */
  //    html.ifswlt800.ifshlt600 div>ul { font-size: large }
  //                                           /* screen width  < 800 and
  //                                              screen height < 600 */
  //  </style>
  //  +----------------+---------------+------------------------------
  //  | CONDITION      | IDENT         | NOTE
  //  +----------------+---------------+------------------------------
  //  | IE             | "ifie"        | version >= 6
  //  | IE6.0          | "ifie6"       |
  //  | IE7.0          | "ifie7"       |
  //  | IE8.0          | "ifie8"       |
  //  | Opera          | "ifopera"     | version >= 9.20
  //  | Opera9.27      | "ifopera92"   |
  //  | Opera9.63      | "ifopera96"   |
  //  | Opera10.00     | "ifopera10"   |
  //  | Opera10.10     | "ifopera101"  |
  //  | Gecko          | "ifgecko"     |
  //  | Gecko1.9.1     | "ifgecko191b" | Gecko engine version >= 191(Firefox3.5)
  //  | Firefox        | "iffx"        | version >= 2
  //  | Firefox2.0     | "iffx2"       |
  //  | Firefox3.0     | "iffx3"       |
  //  | Firefox3.5     | "iffx35"      |
  //  | WebKit         | "ifwebkit"    | has Safari, Chrome, iPhone
  //  | WebKit522      | "ifwebkit522b"| WebKit engine version >= 522(Safari3)
  //  | WebKit530      | "ifwebkit530b"| WebKit engine version >= 530(Safari4)
  //  | Safari         | "ifsafari"    | version >= 3
  //  | Safari3.2.3    | "ifsafari32"  |
  //  | Safari4.0      | "ifsafari4"   |
  //  | iPod           | "ifiphone"    |
  //  | iPhone         | "ifiphone"    |
  //  | Chrome         | "ifchrome"    | version >= 1
  //  | Chrome1.0      | "ifchrome1"   |
  //  | Chrome2.0      | "ifchrome2"   |
  //  | Chrome3.0      | "ifchrome3"   |
  //  | Silverlight    | "ifsl"        | version >= 1
  //  | Flash          | "ifflash"     | version >= 7
  //  | HTML5::Canvas  | "ifcanvas"    | enable HTML5::Canvas
  //  | JavaScript     | "ifjs"        | enable JavaScript
  //  | width  <  800  | "ifw800s"     | screen width  <  800px
  //  | width  >= 1200 | "ifw1200b"    | screen width  >= 1200px
  //  | height <  600  | "ifh600s"     | screen height <  600px
  //  | height >= 1000 | "ifh1000b"    | screen height >= 1000px
  //  +----------------+---------------+------------------------------
  _root = _query.tag("html")[0];

  var cn = [_root.className.replace(/ifnojs/, ""), "ifjs"],
      css = "", context, tick,
      sw = screen.width, sh = screen.height;

  _ie          && cn.push("ifie ifie" + _uaver);
  _opera       && cn.push("ifopera ifopera" + _uaver);
  _gecko       && cn.push("ifgecko");
  _gecko       && _egver >= 1.91 && cn.push("ifgecko191b");
  _mm.firefox  && cn.push("iffx iffx" + _uaver);
  _webkit      && cn.push("ifwebkit");
  _webkit      && _egver >= 522  && cn.push("ifwebkit522b");
  _webkit      && _egver >= 530  && cn.push("ifwebkit530b");
  _safari      && cn.push("ifsafari ifsafari" + _uaver);
  _chrome      && cn.push("ifchrome ifchrome" + _uaver);
  _mm.iphone   && cn.push("ifiphone");
  _mm.slver    && cn.push("ifsl");
  _mm.flashver && cn.push("ifflash");
  _mm.canvas   && cn.push("ifcanvas");
  (sw <   800) && cn.push("ifw800s");
  (sw >= 1200) && cn.push("ifw1200b");
  (sh <   600) && cn.push("ifh600s");
  (sh >= 1000) && cn.push("ifh1000b");

  if (!_win.UUALTCSS_DISABLE_CONDITIONAL_SELECTOR) {
    _root.className = cn.join(" ").replace(TRIM, "").replace(/\./g, "");
  }

  _plus = uuAltCSSPlus;
  _codec = _win.uuCodecDataURI ? uuCodecDataURI : 0;

  if (_markup || _boostup) {
    tick = +new Date;
    // --- begin perf block ---
        if (!_altcss._init(css)) {
          return; // reload from browser cache
        }
        _boostup && _plus.init(context);
        _altcss._validate(context);
        _initialized = 1;
    // --- end perf block ---
    _mm.debug &&
        (_win.status = (new Date - tick) + "ms");
  }
}

_ie     && (_uaver >= 6   && _uaver <  9  ) && (++_markup, ++_boostup);
_opera  && (_uaver >= 9.2 && _uaver <  9.5) && (++_markup);
_opera  && (_uaver >= 9.2 && _uaver <  11 ) && (++_boostup); // Opera9.2-10
_gecko  && (_egver >  1.8 && _egver <= 1.9) && (++_markup);  // Fx2-Fx3
_gecko  && (_egver >  1.8 && _egver <= 1.91)&& (++_boostup); // Fx2-Fx3.5
_safari && (_uaver >= 3   && _uaver <  3.1) && (++_markup);
_chrome && (_uaver <  2                   ) && (++_markup);

switch (_win.UUALTCSS_FORCE_MARKUP || 2) {
case 0: _markup = 0; break;
case 1: _markup = 1;
}
_mm.boot(autoexec);

// --- export ---
_win.uuAltCSS = _altcss;

})(); // uuAltCSS scope


// === uuAltCSSPlus ===
// depend: uuMeta, uuQuery, uuStyle, uuStyleSheet, uuAltCSS, uuColor,
//         uuToken, uuImage, uuCanvas, uuLayer, uuResize, uuCSSValidator
/*
window.UUALTCSS_ENABLE_MAXMIN = 0
uuAltCSSPlus.redraw()
 */
(function() {
var _altcssplus, // inner namespace
    _mm = uuMeta,
    _mix = _mm.mix,
    _ss = uuStyleSheet,
    _query = uuQuery,
    _style = uuStyle,
    _color = uuColor,
    _token = uuToken,
    _validator = uuCSSValidator,
    _win = window,
    _doc = document,
    _float = parseFloat,
    _uaver = _mm.uaver,
    _ie = _mm.ie,
    _ie6 = _ie && _uaver === 6,
    _ie7 = _ie && _uaver === 7,
    _ie67 = _ie6 || _ie7,
    _runstyle = _mm.runstyle,
    _ssid = "uuAltCSSPlus",
    _spacer = _mm.imagedir + "/1dot.gif",
    _canvas = _mm.canvas && !(_mm.opera && _uaver < 9.5),
    // enable functions
    _enable = {
/*
      hover:      _ie6  ? 0x8   : 0, // IE6 :hover
 */
      stdcare:    _ie6  ? 0x10  : 0, // IE6 position: absolute bug
      posfxd:     _ie6  ? 0x20  : 0, // IE6 position: fixed
      alphapng:   _ie6  ? 0x40  : 0, // IE6 <img src="some.alpha.png">
      maxmin:     _ie67 ? 0x80  : 0, // IE6-IE7 max-width (IE7 td,th support)
      opacity:    _ie   ? 0x100 : 0, // IE6-IE8 opacity:
      disptbl:    _ie67 ? 0x200 : 0, // IE6-IE7 display: table
      boxshadow:  (_canvas && !_mm.webkit522)
                        ? 0x400 : 0,    // -webkit-box-shadow:
      boxreflect: (_canvas && !_mm.webkit530)
                        ? 0x800 : 0,    // -webkit-box-reflect:
      bradius:    (_canvas && !_mm.webkit522)
                        ? 0x1000 : 0,   // -webkit-border-radius:
      bimage:     (_canvas && !_mm.webkit522)
                        ? 0x2000 : 0,   // -webkit-border-image:
      gradient:   (_canvas && !_mm.webkit530)
                        ? 0x4000 : 0    // -webkit-gradient:
    },
    _deny = 0, // 1: deny removeChild
    _plan = { delay: [], alphapng: [], disptbl: [], posfxd: [],
              maxmin: [], boxeffect: [] },
    _find = {
      prop: {
        display: 1,
        position: 2,
        background: 3,
        "background-image": 3,
        opacity:                  _enable.opacity,
        "-webkit-box-shadow":     _enable.boxshadow,
        "-webkit-box-reflect":    _enable.boxreflect,
        "-webkit-border-radius":  _enable.bradius,
        "-webkit-border-image":   _enable.bimage,
        "-webkit-border-top-left-radius":     _enable.bradius,
        "-webkit-border-top-right-radius":    _enable.bradius,
        "-webkit-border-bottom-right-radius": _enable.bradius,
        "-webkit-border-bottom-left-radius":  _enable.bradius
      },
      display:    { table: _enable.disptbl },
      position:   { fixed: _enable.posfxd }
    },
    TRANSPARENT = "transparent",
    TRIM = /^\s+|\s+$/g;

if (!_win.UUALTCSS_ENABLE_MAXMIN) {
  _enable.maxmin = 0;
}
if (_enable.boxshadow  |
    _enable.boxreflect |
    _enable.bradius    |
    _enable.bimage     |
    _enable.gradient) {
  _enable.boxeffect = 0x10000;
}

// find keyword
function find(pair, expr) {
  var bits = 0, prop = {}, order = [], v, w, i = 0;

  while ( (v = pair[i++]) ) {
    switch (w = _find.prop[v.prop] || 0) {
    case 1: bits |= _find.display[v.val]; break;  // display:
    case 2: bits |= _find.position[v.val]; break; // position:
    case 3:
      if (v.val.indexOf("-webkit-gradient") >= 0) { // background(-image)
        bits |= _enable.gradient;
      }
      break;
    default:
      bits |= w;
    }
    prop[v.prop] = v.val;
    order.push(v.prop);
  }
/*
  if (_ie6) {
    if (/:hover/.test(expr)) {
      bits |= _enable.hover;
    }
  }
 */
  return { bits: bits, prop: prop, order: order.join(",") + "," };
}

function redraw() {
  _enable.posfxd && posfxdRecalc();
  _enable.maxmin && maxminRecalc();
  _enable.boxeffect && boxeffectRecalc();
}

_altcssplus = {
  init: function(context) {
    _enable.stdcare && stdcare(context);

    if (_enable.posfxd) {
      _plan.delay.push(function() {
        _ss.create(_ssid);
        _ss.insertRule(_ssid, ".uuposfix", // not "uucssposfix"
          ("z-index:5000;" +
           "behavior:expression(" +
           "this.style.pixelTop=document.#.scrollTop+this.uuCSSPosFxd.vpx," +
           "this.style.pixelLeft=document.#.scrollLeft+this.uuCSSPosFxd.hpx)").
          replace(/#/g, _mm.quirks ? "body" : "documentElement")
        );
      });
    }
  },

  // make plan
  plan: function(nodeList, revalidate, context) {
    if (!revalidate) {
      _enable.alphapng && alphapng(context);
    }
  },

  // pre-validate plan
  prevalidate: function(nodeList, revalidate, context) {
    var i, iz;

    if (_plan.delay.length) {
      for (i = 0, iz = _plan.delay.length; i < iz; ++i) {
        _plan.delay[i]();
      }
      _plan.delay = []; // clear
      _enable.stdcare = 0;
    }
    if (!revalidate) {
      _enable.alphapng && alphapngRecalc(context);
    }
  },

  // post-validate plan
  postvalidate: function(nodeList, revalidate, context) {
    var node, bits, info, i = 0, iz = nodeList.length;

    for (; i < iz; i += 2) {
      node = nodeList[i];
      info = nodeList[i + 1];
      bits = info.bits;

      (bits & _enable.opacity) && opacity(node);
      (bits & _enable.posfxd)  && posfxd(node);

      if (!revalidate) {
        if (bits & _enable.disptbl) {
          _plan.disptbl.push(node); // stock
        }
/*
        if (bits & _enable.hover) {
          (function(node) {
            _mm.event.bind(node, "mouseenter", function(evt) {
              node.className += node.uuCSSHover;
            });
            _mm.event.bind(node, "mouseleave", function(evt) {
              node.className =
                  node.className.replace(/\s*uucsshover[\d]+/g, "");
            });
          })(node);
        }
 */
        if (bits & (_enable.boxshadow  |
                    _enable.boxreflect |
                    _enable.bradius    |
                    _enable.bimage     |
                    _enable.gradient)) {
          _plan.boxeffect.push(node, info.prop, info.order); // stock
        }
      }
    }
    if (!revalidate) {
      _plan.posfxd.length && posfxdRecalc();
      _plan.disptbl.length && disptbl();
      _plan.boxeffect.length && boxeffect(revalidate);
    }
    _enable.maxmin && (maxmin(context),
                       maxminRecalc());

    if (!revalidate) {
      if (_enable.posfxd | _enable.maxmin | _enable.boxeffect) {
        _mm.event.resize(redraw, 1); // use resize-agent
      }
    }
  }
};

// ---------------------------------------------------------
// care: position: absolute bug(cannot select text)
//       smooth scroll
//       background image cache
// for IE6
function stdcare(context) {
  function markup(elm) {
    _plan.delay.push(function() {
      var st = elm.style;
      // text selection bug
      if (!_mm.quirks) {
        st.height = "100%";
//      st.margin  = "0"; // ToDo
//      st.padding = "0"; // ToDo
      }
      st.backgroundAttachment = "fixed"; // smooth scroll
      !_style.getBGImg(elm) && (st.backgroundImage = "url(none)");
    });
  }
  markup(_query.tag("html")[0]); // <html>
  markup(_doc.body);             // <body>
  _plan.delay.push(function() {
    try {
      _doc.execCommand("BackgroundImageCache", false, true);
    } catch(err) {}
  });
}

// ---------------------------------------------------------
// care: opacity
// for IE6, IE7, IE8
function opacity(elm) {
  var v = elm.style.opacity || elm.currentStyle.opacity;

  _style.setOpacity(elm, _float(v) || 1.0);
}

// ---------------------------------------------------------
// care: "position: fixed"
// for IE6
function posfxd(elm) {
  if ("uuCSSPosFxd" in elm) { return; } // already fixed

  var vp = _style.getViewport(), rect = _style.getRect(elm),
      cs = elm.currentStyle,
      v = cs.top  !== "auto", // vertical
      h = cs.left !== "auto", // horizontal
      pxfn = _style.getPixel;

  _plan.posfxd.push(elm); // mark

  elm.uuCSSPosFxd = { // bond
    mode: (v ? 1 : 2) | (h ? 4 : 8), // 1:top, 2:bottom, 4:left, 8:right
    vcss: v ? cs.top : cs.bottom,
    hcss: h ? cs.left : cs.right,
    vpx: v ? (pxfn(elm, "paddingTop") + pxfn(elm, "top"))
           : (vp.h - rect.oh - pxfn(elm, "bottom")),
    hpx: h ? (pxfn(elm, "paddingLeft") + pxfn(elm, "left"))
           : (vp.w - rect.ow - pxfn(elm, "right"))
  };
  elm.className += " uuposfix";
  elm.style.position = "absolute"; // position:fixed -> position:absolute
}

function posfxdRecalc() {
  var ary = [], v, w, i = 0, iz = _plan.posfxd.length,
      vp = _style.getViewport(), cs, p;

  for (; i < iz; ++i) {
    v = _plan.posfxd[i];
    if (v && (p = v.uuCSSPosFxd)) {
      cs = v.currentStyle;
      w = _style.toPixel(v, p.vcss, 1);
      p.vpx = (p.mode & 0x1) ? (_style.getPixel(v, "paddingTop") + w)
                             : (vp.h - v.offsetHeight - w);
      w = _style.toPixel(v, p.hcss, 1);
      p.hpx = (p.mode & 0x4) ? (_style.getPixel(v, "paddingLeft") + w)
                             : (vp.w - v.offsetWidth - w);
      ary.push(v);
    }
  }
  // http://www.microsoft.com/japan/msdn/columns/dude/dude061198.aspx
  ary.length && _doc.recalc(); // update
  _plan.posfxd = ary;
}

// ---------------------------------------------------------
// care: <img src="some.alpha.png" />
// care: <img class="alpha" src="some.png" />
// for IE6
function alphapng(context) {
  function filter(elm, src, method) {
    return [" progid:DXImageTransform.Microsoft.AlphaImageLoader",
            "(src='", src, "',sizingMethod='", method, "')"].join("");
  }

  var rv = [], v, i = 0,
      node = _query('img[src$=".png"],input[type=image][src$=".png"]',
                    context);

  while ( (v = node[i++]) ) {
    if (/\.alpha\.png$/.test(v.src) ||
        (" " + v.className + " ").indexOf(" alpha ") >= 0) {
      rv.push({ elm: v, filter: filter(v, v.src, "image"),
                orgw: v.width, orgh: v.height });
    }
  }

  _plan.alphapng = rv;
}

function alphapngRecalc(context) {
  var hash, ary = _plan.alphapng, v, i = 0;

  while ( (hash = ary[i++]) ) {
    v = hash.elm;
    v.style.filter += hash.filter;
    v.src = _spacer;
    (v.tagName === "IMG") ? (v.width = hash.orgw, v.height = hash.orgh)
                          : (v.style.zoom = 1);
  }
  _plan.alphapng = []; // clear
}

// ---------------------------------------------------------
// care: max-width, min-width, max-height, min-height
// for IE6, IE7(td, th only)
function maxmin(context) {
  var rv = [], xw, nw, xh, nh,
      node = _ie6 ? _query.tag("*", context)
                  : _query("td,th", context), // IE7 td, th
      v, i = 0, cs, rex1 = /^(inherit|none|auto)$/,
      blockLevel = { block: 1, "inline-block": 1, "table-cell": 1 };

  while ( (v = node[i++]) ) {
    cs = v.currentStyle;
    if (_ie7 || blockLevel[cs.display]) {
      xw = cs["max-width"]  || cs.maxWidth || ""; // length | % | none
      nw = cs["min-width"]  || cs.minWidth || ""; // length | %
      xh = cs["max-height"] || cs.maxHeight|| ""; // length | % | none
      nh = cs["min-height"] || cs.minHeight|| ""; // length | %
                                                  // (ie6 default "auto")
      rex1.test(xw) && (xw = "");
      rex1.test(nw) && (nw = "");
      rex1.test(xh) && (xh = "");
      rex1.test(nh) && (nh = "");

      if (xw === "" && nw === "" &&
          xh === "" && nh === "") {
        if ("uuCSSBoostMaxMin" in v) {
          delete v["uuCSSBoostMaxMin"];
        }
        continue; // exclude
      }

      _mix(v, {
        uuCSSBoostMaxMin: {}
      }, 0, 0);
      _mix(v.uuCSSBoostMaxMin, {
        maxWidth: xw,
        minWidth: nw,
        maxHeight: xh,
        minHeight: nh
      });
      _mix(v.uuCSSBoostMaxMin, {
        orgWidth:  v.currentStyle.width,
        orgHeight: v.currentStyle.height
      }, 0, 0);
      rv.push(v);
    }
  }
  _plan.maxmin = rv;
}

function maxminRecalc() {
  var elm, i = 0, hash,
      calcMaxWidth, calcMinWidth, calcMaxHeight, calcMinHeight,
      run, width, height, rect;

  while ( (elm = _plan.maxmin[i++]) ) {
    hash = elm.uuCSSBoostMaxMin;

    calcMaxWidth  = maxminRecalcSize(elm, hash, "maxWidth", 1);
    calcMinWidth  = maxminRecalcSize(elm, hash, "minWidth", 1);
    calcMaxHeight = maxminRecalcSize(elm, hash, "maxHeight");
    calcMinHeight = maxminRecalcSize(elm, hash, "minHeight");

    // recalc
    if (calcMaxWidth || calcMinWidth) {

      // recalc max-width
      if (calcMinWidth > calcMaxWidth) {
        calcMaxWidth = calcMinWidth;
      }

      // recalc width
      // width: auto !important
      run = elm.runtimeStyle.width;  // keep runtimeStyle.width
      elm.runtimeStyle.width = hash.orgWidth;
      elm.style.width = "auto";
      rect = elm.getBoundingClientRect(); // re-validate
      width = rect.right - rect.left;

      elm.style.width = hash.orgWidth; // o
      elm.runtimeStyle.width = run; // restore style

      // recalc limits
      if (width > calcMaxWidth) {
        width = calcMaxWidth;
        elm.style.pixelWidth = width;
      } else if (width < calcMinWidth) {
        width = calcMinWidth;
        elm.style.pixelWidth = width;
      }
    }

    if (calcMaxHeight || calcMinHeight) {

      // recalc max-height
      if (calcMinHeight > calcMaxHeight) {
        calcMaxHeight = calcMinHeight;
      }

      // recalc height
      // height: auto !important
      run = elm.runtimeStyle.height;  // keep runtimeStyle.height
      elm.runtimeStyle.height = hash.orgHeight;
      elm.style.height = "auto";
      rect = elm.getBoundingClientRect(); // re-validate
      height = rect.bottom - rect.top;

      elm.style.height = hash.orgHeight; // o
      elm.runtimeStyle.height = run; // restore style

      // recalc limits
      if (height > calcMaxHeight) {
        height = calcMaxHeight;
        elm.style.pixelHeight = height;
      } else if (height < calcMinHeight) {
        height = calcMinHeight;
        elm.style.pixelHeight = height;
      }
    }
  }
}

function maxminRecalcSize(elm, hash, prop, horizontal) {
  var rv = 0, rect;

  if (hash[prop] !== "") {
    if (/[\d\.]+%$/.test(hash[prop])) {
      rect = elm.parentNode.getBoundingClientRect();
      rv = _float(hash[prop]);
      rv = (horizontal ? (rect.right - rect.left)
                       : (rect.bottom - rect.top)) * rv / 100;
    } else {
      rv = _style.toPixel(elm, hash[prop], 1);
    }
  }
  return rv;
}

// ---------------------------------------------------------
// care: display:table, display: table-cell
// for IE6, IE7
function disptbl() {
  var node = _plan.disptbl, v, i, j, iz,
      tbl, row, cell;

  for (i = 0, iz = node.length; i < iz; ++i) {
    v = node[i];
    tbl = _doc.createElement("table");
    // copy attrs
    tbl.id = v.id;
    tbl.title = v.title;
    tbl.className = v.className;
    // copy style
    tbl.style.cssText = v.style.cssText;
    tbl.style.borderCollapse = "collapse";
    tbl.setAttribute("uuCSSLock", 1); // bond

    row = tbl.insertRow(); // add last row
    row.setAttribute("uuCSSLock", 1); // bond

    for (j = v.firstChild; j; j = j.nextSibling) {
      cell = row.insertCell(); // add last cell
      // copy attrs
      cell.id = j.id;
      cell.title = j.title;
      cell.className = j.className;
      // copy style
      cell.style.cssText = j.style.cssText;
      cell.style.margin = "0"; // force margin: 0
      // copy event handler(click, dblclick only)
      cell.onclick = j.onclick;
      cell.ondblclick = j.ondblclick;
      cell.setAttribute("uuCSSLock", 1); // bond

      while (j.firstChild) {
        cell.appendChild(j.removeChild(j.firstChild));
      }
    }
    v.parentNode.replaceChild(tbl, v);
  }
  _plan.disptbl = []; // clear
}

// ---------------------------------------------------------
function boxeffect(revalidate) {
  var BTW = "borderTopWidth",
      BLW = "borderLeftWidth",
      BRW = "borderRightWidth",
      BBW = "borderBottomWidth",
      BTC = "borderTopColor",
      ary = _plan.boxeffect, i = 0, iz = ary.length,
      node, css, order, prop, view, ns, vs, hash, canvas,
      decl, declw, declh,
      topx = _style.toPixel;

  for (; i < iz; i += 3) { // [(node, prop@css, order), (,,), ... ]
    node  = ary[i];     // node
    css   = ary[i + 1]; // prop
    order = ary[i + 2]; // order
    view  = node.parentNode;
    ns = _ie ? node[_runstyle] : _runstyle(node, ""); // node currentStyle
    vs = _ie ? view[_runstyle] : _runstyle(view, ""); // view currentStyle

    if (ns.display === "none" || vs.display === "none") {
      continue;
    }

    // IE6,IE7 CSS layout bugfix
    if (_ie67) {
      if (!vs.hasLayout) {
        view.style.zoom = 1;
      }
      node.style.zoom = 1; // apply z-index(sink canvas)
      if (ns.position === "static") {
        node.style.position = "relative"; // set "position: relative"
      }
      // bugfix position:relative + margin:auto
      // see demo/viewbox_position/position_relative.htm
      if (node.style.position === "relative") {
        (ns.marginTop === "auto") && (node.style.marginTop = 0);
        (ns.marginLeft === "auto") && (node.style.marginLeft = 0);
        (ns.marginRight === "auto") && (node.style.marginRight = 0);
        (ns.marginBottom === "auto") && (node.style.marginBottom = 0);
      }
    }

    prop = {
      css:        css,
      order:      order,
      view:       view,
      node:       node,
      layer:      0,
      slmode:     0, // 1 = Silverlight mode
      nodeRect:   _style.getRect(node),
      viewRect:   _style.getRect(view),
      nodeOffset: 0, // lazy
      border:     { render: 0,
                    t: topx(node, ns[BTW]),
                    l: topx(node, ns[BLW]),
                    r: topx(node, ns[BRW]),
                    b: topx(node, ns[BBW]),
                    tc: _color.parse(ns[BTC]) }, // border-top-color
      margin:     { t: topx(node, ns.marginTop),
                    l: topx(node, ns.marginLeft),
                    r: topx(node, ns.marginRight),
                    b: topx(node, ns.marginBottom) },
      mbg:        { render: 0, type: [],
                               image: ["none"],
                               repeat: ["repeat"],
                               position: ["0% 0%"],
                               attachment: ["scroll"],
                               origin: ["padding"],
                               clip: ["no-clip"],
                               rgba: { r: 0, g: 0, b: 0, a: 0 },
                               altcolor: _style.getBGColor(node, 1, 1),
                               grad: [],
                               imgobj: [],
                               tid: -1 },
      bradius:    { render: 0, r: [0,0,0,0] },
      boxshadow:  { render: 0, rgba: 0, ox: 0, oy: 0, blur: 0 },
      boxreflect: { render: 0, dir: 0, offset: 0, url: 0, img: 0,
                    grad: { render: 0 } }
    };
    if (prop.border.tc[1]) { // has border
      if (prop.border.t || prop.border.l ||
          prop.border.r || prop.border.b) {
        prop.border.render = 1;
      }
    }

    parseBoxReflectParam(ns, prop);

    if (prop.boxreflect.render) {
      hash = prop.boxreflect;
      if (hash.dir === "below") {
        if (node.tagName === "IMG") {
          // delay loader
          uuImage.load(node.src, function(img, state, dim) {
            if (state === 1) {
              prop.layer = new uuLayer(view, dim.w, dim.h * 2 + hash.offset);
              prop.layer.createReflectionLayer(
                  "reflect", node, 0,
                  node.offsetLeft, node.offsetTop, 0, 0,
                  void 0, 0, hash.offset);
              node.style.visibility = "hidden";
            }
            img.clear();
          });
        }
      }
    } else {
      canvas = "vmlcanvas";
      if (_ie && _mm.slver > 0 &&
          prop.css["-uu-canvas-type"] === "sl") {
        prop.slmode = 1; // Silverlight mode
        canvas = "canvas";
      }

      // get declaration value("auto" or "200px"),
      // The computed value cannot be used.
      declw = declh = "auto";
      decl = uuAltCSS.getDeclaredValues(view);
      if (!decl) {
        decl = { order: "" };
      }

      if (view.style.width && view.style.width !== "auto") {
        declw = view.style.width;
      } else if (decl.order.indexOf("width,") >= 0) {
        declw = decl.prop.width;
      }
      if (view.style.height && view.style.height !== "auto") {
        declh = view.style.height;
      } else if (decl.order.indexOf("height,") >= 0) {
        declh = decl.prop.height;
      }

      prop.layer = new uuLayer(view, declw, declh);

      prop.nodebgLayer =
          prop.layer.createLayer("nodebg", canvas, 0, 1,
                                 prop.nodeRect.ow, prop.nodeRect.oh);
      prop.viewbgLayer =
          prop.layer.createLayer("viewbg", canvas, 0, 1,
                                 prop.viewRect.ow, prop.viewRect.oh);
      prop.layer.appendLayer("node", node);
    }

    // http://d.hatena.ne.jp/uupaa/20090719
    if (_ie67) {
      prop.ie6borderorg = {
        marginTop: ns.marginTop,
        marginLeft: ns.marginLeft,
        marginRight: ns.marginRight,
        marginBottom: ns.marginBottom,
        borderTopWidth: ns[BTW],
        borderLeftWidth: ns[BLW],
        borderRightWidth: ns[BRW],
        borderBottomWidth: ns[BBW],
        borderStyle: "solid"
      };
      prop.ie6borderfix = {
        marginTop: (prop.margin.t + prop.border.t) + "px",
        marginLeft: (prop.margin.l + prop.border.l) + "px",
        marginRight: (prop.margin.r + prop.border.r) + "px",
        marginBottom: (prop.margin.b + prop.border.b) + "px",
        border: "0px none"
      };
    }

    detectMultipleBackground(ns, prop);

    // most important
    prop.nodeOffset = getOffsetFromAncestor(prop.node, view);

    node.uuAltCSSBoxEffect = prop; // bond
    prop.slmode ? boxeffectDelay(prop, 0)
                : boxeffectDraw(prop, 0);
  }
}

function boxeffectDelay(prop) {
  uuCanvas.ready(function() {
    boxeffectDraw(prop, 0);
  });
}

function boxeffectRecalc() {
  var ary = _plan.boxeffect, node, view, prop, ns, vs,
      i = 0, iz = ary.length;

  for (; i < iz; i += 3) {
    node = ary[i]; // node
    view  = node.parentNode;

    ns = _ie ? node[_runstyle] : _runstyle(node, ""); // node currentStyle
    vs = _ie ? view[_runstyle] : _runstyle(view, ""); // view currentStyle

    if (ns.display === "none" || vs.display === "none") {
      continue;
    }

    prop = node.uuAltCSSBoxEffect;
    boxeffectRecalcRect(node, prop);
    // improvement of response time
    boxeffectDelayRecalc(prop);
  }
}

function boxeffectRecalcRect(node, prop) {
  // http://d.hatena.ne.jp/uupaa/20090719
  if (_ie67) { // restore border and margin state
    _mm.mix(node.style, prop.ie6borderorg);
  }
  // update rect
  prop.nodeRect = _style.getRect(node);
  prop.viewRect = _style.getRect(prop.view);

  // most important
  prop.nodeOffset = getOffsetFromAncestor(prop.node, prop.view);

  if (_ie67) {
    _mm.mix(node.style, prop.ie6borderfix);
  }
}

function boxeffectDelayRecalc(prop) {
  setTimeout(function() {
    boxeffectDraw(prop, 1);
  }, 0);
}

function boxeffectDraw(prop, redraw) {
  var node = prop.node,
//    view = prop.view,
      layer = prop.layer,
      nodebg = prop.nodebgLayer,
      viewbg = prop.viewbgLayer,
      nctx,
      vctx,
      hash, ary, ns;

  if (layer) {
    nctx = layer.getContext("nodebg");
    vctx = layer.getContext("viewbg");
  }

  if (0) { // debug
    layer.view.style.border = "2px solid pink";
    nodebg.style.border = "5px solid red";
    viewbg.style.border = "5px solid green";
  }

  if (redraw) {
    viewbg && layer.resizeLayer("viewbg", prop.viewRect.w, prop.viewRect.h);
    nodebg && layer.resizeLayer("nodebg", prop.nodeRect[_ie67 ? "ow" : "w"],
                                          prop.nodeRect[_ie67 ? "oh" : "h"]);
  }

  ns = _ie ? node[_runstyle]
           : _runstyle(node, "");
  parseBorderRadiusParam(ns, prop);
  parseBoxShadowParam(ns, prop);

  // CSS3 background-origin:
  if (nodebg) {
    nodebg.style.left =
        (prop.nodeOffset.x + (_ie67 ? 0 : prop.border.l)) + "px";
    nodebg.style.top =
        (prop.nodeOffset.y + (_ie67 ? 0 : prop.border.t)) + "px";
  }

  if (viewbg) {
    // ToDo: clipping path for background-color: rgba(,,,0.5) support
/* keep
    if (0) {
      vctx.save();
      vctx.rect(0, 0, prop.viewRect.w, prop.viewRect.h);
      boxpath(vctx,
        prop.nodeOffset.x + prop.border.l,
        prop.nodeOffset.y + prop.border.t,
                prop.nodeRect.ow - prop.border.l - prop.border.r,
                prop.nodeRect.oh - prop.border.t - prop.border.b,
                prop.bradius.r,
                1); // open path
      vctx.clip();
    }
 */
    // draw shadow
    if (prop.boxshadow.render) {
      hash = prop.boxshadow;
      vctx.save();
      drawFakeShadow(vctx,
                     prop.nodeOffset.x - hash.blur / 2 + hash.ox,
                     prop.nodeOffset.y - hash.blur / 2 + hash.oy,
                     prop.nodeRect.ow + hash.blur,
                     prop.nodeRect.oh + hash.blur,
                     hash.rgba,
                     Math.max(hash.blur, Math.abs(hash.ox * 2),
                                         Math.abs(hash.oy * 2)),
                     prop.bradius.r);
      vctx.restore();
    }

    // draw border
    if (prop.border.render) {
      ary = [];
      if (prop.boxshadow.render) {
        hash = prop.bradius.r;
        ary[0] = !hash[0] ? 1 : (hash[0] < 40) ? hash[0] + 4 : hash[0];
        ary[1] = !hash[1] ? 1 : (hash[1] < 40) ? hash[1] + 4 : hash[1];
        ary[2] = !hash[2] ? 1 : (hash[2] < 40) ? hash[2] + 4 : hash[2];
        ary[3] = !hash[3] ? 1 : (hash[3] < 40) ? hash[3] + 4 : hash[3];
      } else {
        ary = prop.bradius.r;
      }
      vctx.save();
      vctx.fillStyle = prop.border.tc[0];
      boxpath(vctx,
              prop.nodeOffset.x,
              prop.nodeOffset.y,
              prop.nodeRect.ow,
              prop.nodeRect.oh,
              ary);
      vctx.fill();
      vctx.restore();
    }
/* keep
    if (0) { // end clip
      vctx.restore();
    }
 */
  }

  if (nodebg) {
    layer.push("nodebg");

    // draw background-color
    if (prop.border.render ||
        prop.boxshadow.render ||
        prop.mbg.rgba.a) {
      nctx.save();
      if (!prop.mbg.rgba.r &&
          !prop.mbg.rgba.g &&
          !prop.mbg.rgba.b &&
          !prop.mbg.rgba.a) { // background-color: transparent
        nctx.globalAlpha = prop.mbg.altcolor.a;
        nctx.fillStyle = _color.hex(prop.mbg.altcolor);
      } else {
        nctx.globalAlpha = prop.mbg.rgba.a;
        nctx.fillStyle = _color.hex(prop.mbg.rgba);
      }
      boxpath(nctx,
              _ie67 ? prop.border.l : 0,
              _ie67 ? prop.border.t : 0,
              prop.nodeRect.ow - prop.border.l - prop.border.r,
              prop.nodeRect.oh - prop.border.t - prop.border.b,
              prop.bradius.r);
      nctx.fill();
      nctx.restore();
    }

    // draw multiple background image
    nctx.save();
    drawMultipleBackgroundImage(prop, nctx);
    nctx.restore();
    layer.pop();
  }

  if (!redraw) {
    // bg setting
    node.style.backgroundColor = TRANSPARENT;
    node.style.backgroundImage = "none";
    node.style.borderColor = TRANSPARENT;
  }
  // http://d.hatena.ne.jp/uupaa/20090719
  // IE6 'borderColor = "transparent";' unsupported
  if (_ie67) {
    _mm.mix(node.style, prop.ie6borderfix);
  }

  if (!redraw && prop.slmode) {
    _deny = 1;
  }
}

function parseBorderRadiusParam(ns, prop) {
  // -webkit-border-radius: <radius>{1,4} [/ <radius>{1,4}]
  // -webkit-border-radius: top-left, top-right, bottom-right, bottom-left
  // -webkit-border-radius: top-left, top-right, [top-left], [top-right]
  // -webkit-border-top-left-radius: <h_radius> [<v_radius>]
  // -webkit-border-top-right-radius: <h_radius> [<v_radius>]
  // -webkit-border-bottom-left-radius: <h_radius> [<v_radius>]
  // -webkit-border-bottom-right-radius: <h_radius> [<v_radius>]
  var REX = /-webkit-border((?:-top|-bottom)(?:-left|-right))?-radius/g,
      POS = { "-top-left": 0, "-top-right": 1,
              "-bottom-right": 2, "-bottom-left": 3 },
      r = [0, 0, 0, 0], v, m;

  while ( (m = REX.exec(prop.order)) ) {
    if (m[1] in POS) {
      r[POS[m[1]]] = prop.css[m[0]].replace(/\s.*$/, ""); // ignore v_radius
    } else {
      v = prop.css[m[0]].replace(/\s*\/.*$/, "").split(/\s/);
      switch (v.length) {
      case 1: r[0] = r[1] = r[2] = r[3] = v[0]; break;
      case 2: r = v; r[3] = r[1]; r[2] = r[0]; break;
      case 3: r = v; r[3] = r[1]; break; // bottom-left = top-right
      case 4: r = v;
      }
    }
  }
  if (r[0] || r[1] || r[2] || r[3]) {
    prop.bradius.render = 1;
    prop.bradius.r = [ _style.toPixel(prop.node, r[0]),
                       _style.toPixel(prop.node, r[1]),
                       _style.toPixel(prop.node, r[2]),
                       _style.toPixel(prop.node, r[3]) ];
  }
}

function parseBoxShadowParam(ns, prop) {
  var rv, hash = prop.boxshadow,
      elm = prop.node,
      key = prop.css["-webkit-box-shadow"],
      topx = _style.toPixel;

  if (key) {
    rv = _validator["-webkit-box-shadow"](key);
    hash.render = rv.valid ? 1 : 0;
    if (rv.valid) {
      hash.rgba = _color.parse(rv.color, 1); // { r, g, b, a }
      hash.ox   = topx(elm, rv.ox);
      hash.oy   = topx(elm, rv.oy);
      hash.blur = topx(elm, rv.blur);
    }
  }
}

function parseBoxReflectParam(ns, prop) {
  // -webkit-box-reflect: <direction> [<offset>] [<mask-box-image>]
  // <direction> ::= "above" / "below" / "left" / "right"
  // <offset> ::= length
  // <mask-box-image> ::= -webkit-gradient() or url()
  var ary, dir, off, mask, /* match, */ url, img, grad,
      hash = prop.boxreflect,
      key = prop.css["-webkit-box-reflect"];

  if (key) {
    ary = key.split(/\s+/);

    dir  = ary.shift();
    off  = ary.shift() || 0;
    mask = ary.length ? ary.join(" ").replace(TRIM, "") : void 0;

/*
    if (mask) {
      if ( (match = /^\s*url\((.*)\)$/.exec(mask)) ) {
        url = match[1].replace(/^\s*[\"\']?|[\"\']?\s*$/g, "");

        img = new Image();
        img.state = 0; // bond
        img.onload = function() {
          if (img.complete ||
              img.readyState === "complete") { // IE8
            img.state = 2;
            setTimeout(function() {
              boxeffectRecalcRect(prop.node, prop);
              boxeffectDraw(prop, 1)
            }, 0);
          }
        };
        img.setAttribute("src", url);
      } else {
        grad = _validator["-webkit-gradient"](mask);
        grad.render = (grad.valid &&
                       grad.type) ? 1 : 0;
      }
    }
 */
    hash.render = 1;
    hash.dir    = dir;
    hash.offset = _style.toPixel(prop.node, off);
    hash.url    = url;
    hash.img    = img;
    hash.grad   = grad;
  }
}

function drawFakeShadow(ctx, x, y, width, height,
                        rgba, blur, radius) {
  var i = 0, j = 0, k, step = 1, line = 5, r = radius,
      fg = "rgba(" + [rgba.r, rgba.g, rgba.b, ""].join(","); // fragment

  if (blur > 30 || (_ie6 && !_mm.slver)) { // IE6 + VML - cut short
    step *= 2, line *= 2;
  }
  ctx.globalAlpha = 1;
  ctx.lineWidth = line;
  for (; i < blur; i += step) {
    k = i / blur;
    j += 0.5;
    ctx.strokeStyle = fg + (k * k * k) + ")";
    boxpath(ctx, x + i, y + i, width - (i * 2), height - (i * 2),
            [r[0] - j, r[1] - j, r[2] - j, r[3] - j]);
    ctx.stroke();
  }
}

function drawMultipleBackgroundImage(prop, ctx) {
  var mbg = prop.mbg, i = 0, iz = prop.mbg.image.length,
      img, draw = 0, pos = [];

  for (; i < iz; ++i) {
    switch (mbg.type[i]) {
    case 0: // background-image: none
      break;
    case 1: // image
      img = mbg.imgobj[i];
      if (img.state === 2) {
        pos[i] = parseBackgroundPosition(prop, mbg.position[i], img);
        ++draw;
      }
      break;
    case 2: // gradient
      if (mbg.grad[i].render) {
        ++draw;
      }
    }
  }

  if (draw) {
    if (!_ie || prop.slmode) {
      boxpath(ctx,
              _ie67 ? prop.border.l : 0,
              _ie67 ? prop.border.t : 0,
              prop.nodeRect.ow - prop.border.l - prop.border.r,
              prop.nodeRect.oh - prop.border.t - prop.border.b,
              prop.bradius.r);
      ctx.clip();
    }
    while (i--) {
      switch (mbg.type[i]) {
      case 1:
        img = mbg.imgobj[i];
        if (img.state === 2) {
          switch (mbg.repeat[i]) {
          case "no-repeat":
            // http://twitter.com/uupaa/status/2763996863
            // Firefox2 bugfix
            ctx.drawImage(img, pos[i].x | 0, pos[i].y | 0);
            break;
          case "repeat-x":
          case "repeat-y":
            drawImageTile(prop, ctx, img,
                          (mbg.repeat[i] === "repeat-x" ? 1 : 0),
                          pos[i].x | 0, pos[i].y | 0,
                          _ie67 ? prop.border.l : 0,
                          _ie67 ? prop.border.t : 0,
                          prop.nodeRect.ow - prop.border.l - prop.border.r,
                          prop.nodeRect.oh - prop.border.t - prop.border.b);
            break;
          case "repeat":
          default:
            ctx.save();
            ctx.fillStyle = ctx.createPattern(img, "repeat");
            boxpath(ctx,
                    _ie67 ? prop.border.l : 0,
                    _ie67 ? prop.border.t : 0,
                    prop.nodeRect.ow - prop.border.l - prop.border.r,
                    prop.nodeRect.oh - prop.border.t - prop.border.b,
                    prop.bradius.r);
            ctx.fill();
            ctx.restore();
            break;
          }
        }
        break;
      case 2:
        if (mbg.grad[i].render) {
          drawGradient(prop, ctx, mbg.grad[i]);
        }
      }
    }
  }
}

function drawImageTile(prop, ctx, img, horizontal,
                       ix, iy, left, top, right, bottom) {
  var x = ix, y = iy, w = img.width, h = img.height,
      xmin = left - w, ymin = top - h;

  if (horizontal) {
    for (x = ix; x < right; x += w) {
      ctx.drawImage(img, x, y);
    }
    for (x = ix - w; x > xmin; x -= w) {
      ctx.drawImage(img, x, y);
    }
  } else {
    for (y = iy; y < bottom; y += h) {
      ctx.drawImage(img, x, y);
    }
    for (y = iy - h; y > ymin; y -= h) {
      ctx.drawImage(img, x, y);
    }
  }
}

function drawGradient(prop, ctx, hash) {
  function pos(str, size) {
    return /%$/.test(str) ? (size * _float(str) / 100)
                          : _float(str);
  }

  var p0 = pos(hash.point[0], prop.nodeRect.w),
      p1 = pos(hash.point[1], prop.nodeRect.h),
      p2 = pos(hash.point[2], prop.nodeRect.w),
      p3 = pos(hash.point[3], prop.nodeRect.h);

  ctx.save();
  ctx.fillStyle = (hash.type === "linear")
      ? prop.layer.linearGrad(p0, p1, p2, p3,
                              hash.offset, hash.color)
      : prop.layer.radialGrad(p0, p1, hash.radius[0],
                              p2, p3, hash.radius[1],
                              hash.offset, hash.color);
  boxpath(ctx,
          _ie67 ? prop.border.l : 0,
          _ie67 ? prop.border.t : 0,
          prop.nodeRect.ow - prop.border.l - prop.border.r,
          prop.nodeRect.oh - prop.border.t - prop.border.b,
          prop.bradius.r);
  ctx.fill();
  ctx.restore();
}

function detectMultipleBackground(ns, prop) {
  function split(key) {
    return (key.indexOf(",") < 0) ? [key]
                                  : _token.splitCSSValue(key, ",");
  }
  var REX = /background(-image|-repeat|-position|-color|-attachment|-clip|-origin|-size|-break)?/g,
      css = prop.css,
      mbg = prop.mbg, shorthand = 0,
      i = 0, iz, m, url, N;

  while ( (m = REX.exec(prop.order)) ) {
    switch (m[1] || "shorthand") {
    case "shorthand":
      if (!shorthand++) {
        _mm.mix(mbg, _validator.background(css["background"]));
      }
      break;
    case "-image":
      mbg.image = split(css["background-image"]);
      break;
    case "-repeat":
      mbg.repeat = css["background-repeat"].split(",");
      break;
    case "-position":
      mbg.position = css["background-position"].split(",");
      break;
    case "-color":
      mbg.rgba = _color.parse(css["background-color"], 1);
    }
  }

  // spec http://www.w3.org/TR/css3-background/#layering
  N = Math.max(mbg.image.length, mbg.repeat.length, mbg.position.length);

  if (N > mbg.image.length) {
    mbg.image = multipleArray(mbg.image, Math.ceil(N / mbg.image.length), N);
  }
  if (N > mbg.repeat.length) {
    mbg.repeat = multipleArray(mbg.repeat, Math.ceil(N / mbg.repeat.length), N);
  }
  if (N > mbg.position.length) {
    mbg.position = multipleArray(mbg.position, Math.ceil(N / mbg.position.length), N);
  }

  for (iz = mbg.image.length; i < iz; ++i) {
    mbg.image[i] = mbg.image[i].replace(TRIM, ""); // trim both
    mbg.repeat[i] = mbg.repeat[i].replace(TRIM, "");
    mbg.position[i] = mbg.position[i].replace(TRIM, "");
    mbg.type[i] = 0; // unknown

    if ( (m = /^\s*url\((.*)\)$/.exec(mbg.image[i])) ) {
      mbg.type[i] = 1; // image
      url = m[1].replace(/^\s*[\"\']?|[\"\']?\s*$/g, "");
    } else if (/^\s*-webkit-gradient/.test(mbg.image[i])) {
      mbg.type[i] = 2; // gradient
      mbg.grad[i] = _validator["-webkit-gradient"](mbg.image[i]);
      mbg.grad[i].render = (mbg.grad[i].valid &&
                            mbg.grad[i].type) ? 1 : 0;
      continue;
    } else {
      continue; // "none" too
    }
    mbg.imgobj[i] = new Image();
    mbg.imgobj[i].state = 0; // bond
    // lazy load
    (function(imgobj, url) {
      imgobj.onerror = function() {
        imgobj.state = 1;
      };
      imgobj.onload = function() {
        if (imgobj.complete ||
            imgobj.readyState === "complete") { // IE8
          imgobj.state = 2;
          if (prop.mbg.tid >= 0) {
            clearTimeout(prop.mbg.tid);
          }
          prop.mbg.tid = setTimeout(function() {
            boxeffectRecalcRect(prop.node, prop);
            boxeffectDraw(prop, 1)
            prop.mbg.tid = -1;
          }, 100);
        }
      };
      imgobj.setAttribute("src", url);
    })(mbg.imgobj[i], url);
  }
}

function parseBackgroundPosition(prop, pos, img) {
  var key1 = { left: "0%", center: "50%", right: "100%" },
      key2 = { top: "0%", center: "50%", bottom: "100%" },
      ary, px, py,
      nw = prop.nodeRect.w,
      nh = prop.nodeRect.h,
      iw = img.width,
      ih = img.height;

  ary = (pos.indexOf(" ") > 0) ? pos.split(" ")
                               : [pos, pos];

  if (ary[0] === "top" || ary[0] === "bottom" ||
      ary[1] === "left" || ary[1] === "right") {
    ary.reverse(); // "top left" -> "left top"
  }

  ary[0] = key1[ary[0]] || ary[0];
  ary[1] = key2[ary[1]] || ary[1];

  if (ary[0].lastIndexOf("%") > 0) {
    px = nw * _float(ary[0]) / 100
       - iw * _float(ary[0]) / 100;
  } else {
    px = _style.toPixel(prop.node, ary[0]);
  }

  if (ary[1].lastIndexOf("%") > 0) {
    py = nh * _float(ary[1]) / 100
       - ih * _float(ary[1]) / 100;
  } else {
    py = _style.toPixel(prop.node, ary[1]);
  }
  return { x: px, y: py };
}

function boxpath(ctx, x, y, w, h, rary, openPath) {
  var r0 = rary[0], r1 = rary[1], r2 = rary[2], r3 = rary[3],
      w2 = (w / 2) | 0, h2 = (h / 2) | 0;

  if (r0 < 0) { r0 = 0; }
  if (r1 < 0) { r1 = 0; }
  if (r2 < 0) { r2 = 0; }
  if (r3 < 0) { r3 = 0; }
  if (r0 >= w2 || r0 >= h2) { r0 = Math.min(w2, h2) - 2; }
  if (r1 >= w2 || r1 >= h2) { r1 = Math.min(w2, h2) - 2; }
  if (r2 >= w2 || r2 >= h2) { r2 = Math.min(w2, h2) - 2; }
  if (r3 >= w2 || r3 >= h2) { r3 = Math.min(w2, h2) - 2; }

  if (!openPath) {
    ctx.beginPath();
  }
  ctx.moveTo(x, y + h2);
  ctx.lineTo(x, y + h - r3);
  ctx.quadraticCurveTo(x, y + h, x + r3, y + h); // bottom-left
  ctx.lineTo(x + w - r2, y + h);
  ctx.quadraticCurveTo(x + w, y + h, x + w, y + h - r2); // bottom-right
  ctx.lineTo(x + w, y + r1);
  ctx.quadraticCurveTo(x + w, y, x + w - r1, y); // top-left
  ctx.lineTo(x + r0, y);
  ctx.quadraticCurveTo(x, y, x, y + r0); // top-right
  ctx.closePath();
}

function getOffsetFromAncestor(elm, ancestor) {
  var e = elm, x = 0, y = 0;

  while (e && e !== ancestor) {
    x += e.offsetLeft || 0;
    y += e.offsetTop  || 0;
    e  = e.offsetParent;
  }
  return { x: x, y: y };
}

function multipleArray(ary, times, maxLength) {
  var rv = [], i = 0, iz;

  for (; i < times; ++i) {
    rv = rv.concat(ary);
  }
  if (rv.length > maxLength) {
    for (i = 0, iz = rv.length - maxLength; i < iz; ++i) {
      rv.pop();
    }
  }
  return rv;
}

// --- initialize ---

// --- export ---
_win.uuAltCSSPlus = _altcssplus;
_altcssplus.find = find;
_altcssplus.deny = function() { return _deny; };
_altcssplus.redraw = redraw;

})(); // uuAltCSSPlus scope

