////////////////////////////////////////////////
// Browser Detect
////////////////////////////////////////////////
new function() {
  Browser = {
    version: (navigator.userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/i) || [])[1],
    safari: /webkit/i.test(navigator.userAgent),
    opera: /opera/i.test(navigator.userAgent),
    msie: /msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent),
    mozilla: /mozilla/i.test(navigator.userAgent) && !/(compatible|webkit)/.test(navigator.userAgent),
    BadSafari: !((new RegExp("[\u0150\u0170]","g")).test("\u0150"))
  };
}

////////////////////////////////////////////////
// event listener
////////////////////////////////////////////////
var OnEvent = new function() {
  this.__zunkBox = new Array();
};

OnEvent.clear = function() {
  if(this.__zunkBox && this.__zunkBox.length > 0) {
    var item = null;
    while(item = this.__zunkBox.pop()) {
      this.del(item[0], item[1], item[2]);
    }
  }
};

OnEvent.add = function(_e, _a, _fn) {
  var _m = ["addEventListener", "attachEvent"];
  this.__zunkBox.push([_e, _a, _fn]);
  return this.__bindEvent(_m, _e, _a, _fn);
};

OnEvent.del = function(_e, _a, _fn) {
  var _m = ["removeEventListener", "detachEvent"];
  return this.__bindEvent(_m, _e, _a, _fn);
};

OnEvent.__case_ie = function(_a) {
  var __map = {
    blur: "focusout"
  };

  return __map[_a] || _a;
}

OnEvent.__bindEvent = function(_m, _e, _a, _fn) {
  if (_e[_m[0]]) {
    _e[_m[0]](_a, _fn, false);
  } 
  else if (_e[_m[0]]) {
    _e[_m[0]]('on' + this.__case_ie(_a), _fn);
  } 
  else {
    _e['on' + _a] = _fn;
  }
};

////////////////////////////////////////////////
// Bind
////////////////////////////////////////////////
var BindEvent = function(_this, _fn, _args) {
  var fn = _fn;
  var args = __A(_args);
  return function(e) {
    return fn.apply(_this, args.concat([(e || window.event)]));
  };
}

var Bind = function(_this, _fn, _args) {
  var fn = _fn;
  var args = __A(_args);
  return function() {
    return fn.apply(_this, args.concat(__A(arguments)));
  };
}

////////////////////////////////////////////////
// utils.
////////////////////////////////////////////////
var __A = function(_v) {
  if (!_v) return [];
  if (_v.toArray && typeof(_v) != "string") return _v.toArray();
  var iterable = (typeof _v == "string") ? _v.split(/,/) : _v;
  var length =  iterable.length;
  var results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

var Log = function(message) {
  if(Browser.msie) {
    var str = message;
    var logger = document.getElementById("logger");
    if(!logger) {
      var logger = document.createElement("div");
      logger.id = "logger";
      logger.style.clear = "both";
      logger.style.position = "absolute";
      logger.style.bottom = "0px";
      logger.style.left = "0px";
      document.getElementsByTagName("body")[0].appendChild(logger);
    }

    if( typeof(message) == 'object' ){
      str = "";
      for(var i in message) {
        str += "<div style='border:1px solid black'>" + i + "<br />" + message[i] + "</div>";
      }
    }

    logger.innerHTML += "<br>" + str;
  } else {
    var str = message;
    if( !('firebug' in console) ){
      if( typeof(message) == 'object' ){
        str = '&lt;';
        str += message.nodeName.toLowerCase();
        for( var i = 0; i < message.attributes.length; i++ ){
          str += ' ' + message.attributes[i].nodeName.toLowerCase() + '="' + message.attributes[i].nodeValue + '"';
        }
        str += '&gt;';
      }
    }

    console.debug(str);
  }
}

var WordCaps = function(s) {
  s = s.toLowerCase();
  s = s.replace(/(?:^|_)([a-zA-Z])/g, function(_m, _1) { return _1.toUpperCase() });
  return s;
};

var MoveToMousePos = function(_element, _mouse_position) {
  var _x = _mouse_position.x;
  var _y = _mouse_position.y;
  var sT = Math.max(document.documentElement.scrollTop, document.body.scrollTop);

  _element.style.top = (_y + 10)+ "px";
  _element.style.left =( _x + 10) + "px";
};

//
// minified jax
//
var zAjax = function() {
  this._xhr = null;
};

zAjax.prototype.send = function(_o) {
  this._url = _o.url;
  this._data = _o.data;
  this._method = _o.method;
  this.scb = _o.success || function() {};
  this.fcb = _o.failure || function() {};

  if(_o.sync) return this.sync();
  else this.async();
};

zAjax.prototype.sync = function() {
  this.xhr().open(this._method, this._url, false);
  if(this._method == "POST") this.xhr().setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  if(this._data) this.xhr().send(this.toQueryString(this._data));
  else this.xhr().send(null)

  this.callback();
  return this.xhr();
};

zAjax.prototype.async = function() {
  this.xhr().open(this._method, this._url, true);
  if(this._method == "POST") this.xhr().setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  this.xhr().onreadystatechange = Bind(this, this.orsc);

  this.xhr().send(this.toQueryString(this._data));
};

zAjax.prototype.callback = function() {
  if(this.xhr().status == 200) this.scb(this.xhr());
  else this.fcb(this.xhr());
}

zAjax.prototype.toQueryString = function(data) {
  if(typeof data == "string") return data;
  var queries = new Array();
  for(var i in data) queries.push(i + "=" + encodeURIComponent(String(data[i])));
  return queries.join("&");
};

zAjax.prototype.orsc = function() {
  if(this.xhr().readyState == 4) this.callback();
};

zAjax.prototype.xhr = function() {
  if(!this._xhr) this._xhr = this._XMLHttpRequest();

  return this._xhr;
};

zAjax.prototype._XMLHttpRequest = function() {
  var try_these = [
    function () { return new XMLHttpRequest(); },
    function () { return new ActiveXObject('Msxml2.XMLHTTP'); },
    function () { return new ActiveXObject('Microsoft.XMLHTTP'); },
    function () { return new ActiveXObject('Msxml2.XMLHTTP.4.0'); },
    function () { throw "Browser does not support XMLHttpRequest"; }
  ];

  for (var i = 0; i < try_these.length; i++) {
    var func = try_these[i];
    try {
      return func();
    } catch (e) {
    }
  }
};

//
// Url Builder
//
var ZunkyUrlBuilder = function(sources) {
  this.src_url = new ZunkyUrlParser();
  this.units = this.break_url(sources);
  this.url = this.build_url();
};

ZunkyUrlBuilder.prototype.break_url = function(sources) {
  var ret = new Object();
  for(var idx in sources) {
    var units = this.unit_contexts[idx](sources[idx]);
    ret[units.priority] = units.uri;
  }

  return ret;
};

ZunkyUrlBuilder.prototype.build_url = function (){
  var _url = this.src_url.origin;
  var _limit = GrepMaxKey(this.units);
  for(var i=0; i<=_limit; i++) {
    if(this.units[i]) _url += "/" + this.units[i];
  }

  return _url;
};

ZunkyUrlBuilder.prototype.unit_contexts = {
  controller: function(uri) {
    var ret = {};
    ret["priority"] = 1;
    ret["uri"] = uri;
    return ret;
  },
  action: function(uri) {
    var ret = {};
    ret["priority"] = 2;
    ret["uri"] = uri;
    return ret;
  },
  title: function(uri) {
    var ret = {};
    ret["priority"] = 3;
    ret["uri"] = uri;
    return ret;
  },
  id: function(uri) {
    var ret = {};
    ret["priority"] = 3;
    ret["uri"] = uri;
    return ret;
  },
  format: function(uri) {
    var ret = {};
    ret["priority"] = 100;
    ret["uri"] = "?format=" + uri;
    return ret;
  }
}

//
// Url Parser
//
var ZunkyUrlParser = function() {
  this.parse();
}

ZunkyUrlParser.prototype = {
  parse: function() {
    this.url = location.href;
    this.protocol = location.protocol;
    this.hostname = location.hostname;
    this.port = (location.port) ? ":" + location.port : "" ;
    this.pathname = location.pathname;
    this.search = location.search;
    this.query = {};
    this.hash = location.hash;
    this.origin = this.protocol + "//" + this.hostname + this.port;

    // query
    if(this.search) {
      var _q = this.search.replace(/^\?/,"").split("&");
      for(var i=0; i<_q.length; i++) {
        var _kv = _q[i].split("=");
        this.query[_kv[0]] = decodeURI(_kv[1]);
      }
    }
  }
}

//
// Array prototype
//
Array.prototype.last = function() {
  return this[this.length - 1];
};

Array.prototype.is_empty = function() {
  return (!this || this.length <= 0)
};

if(!Array.indexOf) {
  Array.prototype.indexOf = function(_m) {
    for(var i=0; i<this.length; i++) if(this[i] == _m) return i;
    return -1;
  }
};

Array.prototype.include = function(_m) {
  return this.indexOf(_m) > -1;
};

Array.prototype.eject = function(_v) {
  return this.splice(this.indexOf(_v), 1);
}

Array.prototype.longest = function() {
  var length = 0;
  var longest = null;
  for(var i=0; i<this.length; i++) if(this[i].length > length) longest = i, length = this[i].length;

  return this[longest];
};

//
// String prototype
//
String.prototype.is_empty = function() {
  return (!this || this.replace(/\s/g, '').length <= 0);
}

String.prototype.strip_tags = function() {
  var s = this.replace(/<\/?[^>]+?>/gi, '');
  s = s.replace(/[\r\n]/g, '');

  return s;
}

String.prototype.strip_scripts = function() {
  // var pattern = /(<([a-z]+)[^>]*>)(.*?)(?=<\/\2>)/gi;
  var s = this.replace(/<script[^>]*>[\s\S]*?<\/script[^>]*>/gi, '');
  //s = s.replace(/<[\s\S]*?on[a-z]+?=[^>]*?>/, '');
  return s;
}

String.prototype.trim = function() {
  var s = this.replace(/^(&nbsp;|\s)+/, '');
  s = s.replace(/(&nbsp;|\s)+$/, '');

  return s;
}

String.prototype.__encode = function() {
  return encodeURIComponent(this);
}

String.prototype.__decode = function() {
  return decodeURIComponent(this);
}

String.prototype.__encode_entities = function() {
  return this.replace(/[<>&"]/g, function(_m) {
    return { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;'}[_m] || _m;
  });
}

String.prototype.__decode_entities = function() {
  return this.replace(/&([a-z]{2,4});/gi, function(_m, _w) {
    return {'amp': '&', 'quot': '"', 'lt': '<', 'gt': '>'}[_w] || _m;
  })
}

String.prototype.__escape = function() {
  var s = this.replace(/&nbsp;/g, ' ');
  s = s.replace(/[?]/g, '%3F');
  s = s.replace(/[#]/g, '%23');
  s = s.replace(/[\[]/g, '%5B');
  s = s.replace(/[\]]/g, '%5D');
  s = s.replace(/[\/]/g, '%2F');
  s = encodeURI(this);

  return s;
}

String.prototype.__regexEscape = function() {
  return this.replace(/[@$()\^[\],.\/?*\|+]/g, function(a,b) {
    return "\\" + a;
  });
}

String.prototype.__zhref = function() {
  if(/^http/i.test(this)) return this.replace(/^http(s)?:\/\/[^\/]+?\//, "");
  else return this
}

String.prototype.last = function() {
  return this; 
} 

String.prototype.jsonize = function() {
	return eval("(" + this + ")");
}

var GrepMaxKey = function(o) {
  var max = 0;
  for(var idx in o) {
    max = Math.max(idx, max);
  }

  return max;
}

var toPostQueryString = function(_n, _o) {
  var result = new Array()
  for(var i in _o) {
    var _key = _n + "[" + i + "]";
    var _value = encodeURIComponent(_o[i]);

    result.push(_key + "=" + _value);
  }

  return result.join("&");
};

var Colors = new Array(
    '#F0F8FF','#FAEBD7','#00FFFF','#7FFFD4','#F0FFFF','#F5F5DC','#FFE4C4','#000000','#FFEBCD','#0000FF',
    '#8A2BE2','#A52A2A','#DEB887','#5F9EA0','#7FFF00','#D2691E','#FF7F50','#6495ED','#FFF8DC','#DC143C',
    '#00FFFF','#00008B','#008B8B','#B8860B','#A9A9A9','#006400','#BDB76B','#8B008B','#556B2F','#FF8C00',
    '#9932CC','#8B0000','#E9967A','#8FBC8F','#483D8B','#2F4F4F','#00CED1','#9400D3','#FF1493','#00BFFF',
    '#696969','#1E90FF','#D19275','#B22222','#FFFAF0','#228B22','#FF00FF','#DCDCDC','#F8F8FF','#FFD700',
    '#DAA520','#808080','#008000','#ADFF2F','#F0FFF0','#FF69B4','#CD5C5C','#4B0082','#FFFFF0','#F0E68C',
    '#E6E6FA','#FFF0F5','#7CFC00','#FFFACD','#ADD8E6','#F08080','#E0FFFF','#FAFAD2','#D3D3D3','#90EE90',
    '#FFB6C1','#FFA07A','#20B2AA','#87CEFA','#8470FF','#778899','#B0C4DE','#FFFFE0','#00FF00','#32CD32',
    '#FAF0E6','#FF00FF','#800000','#66CDAA','#0000CD','#BA55D3','#9370D8','#3CB371','#7B68EE','#00FA9A',
    '#48D1CC','#C71585','#191970','#F5FFFA','#FFE4E1','#FFE4B5','#FFDEAD','#000080','#FDF5E6','#808000',
    '#6B8E23','#FFA500','#FF4500','#DA70D6','#EEE8AA','#98FB98','#AFEEEE','#D87093','#FFEFD5','#FFDAB9',
    '#CD853F','#FFC0CB','#DDA0DD','#B0E0E6','#800080','#FF0000','#BC8F8F','#4169E1','#8B4513','#FA8072',
    '#F4A460','#2E8B57','#FFF5EE','#A0522D','#C0C0C0','#87CEEB','#6A5ACD','#708090','#FFFAFA','#00FF7F',
    '#4682B4','#D2B48C','#008080','#D8BFD8','#FF6347','#40E0D0','#EE82EE','#D02090','#F5DEB3','#FFFFFF',
    '#F5F5F5','#FFFF00','#9ACD32');  

var ColorNames = new Array(
    'AliceBlue','AntiqueWhite','Aqua','Aquamarine','Azure','Beige','Bisque','Black','BlanchedAlmond','Blue',
    'BlueViolet','Brown','BurlyWood','CadetBlue','Chartreuse','Chocolate','Coral','CornflowerBlue','Cornsilk',
    'Crimson','Cyan','DarkBlue','DarkCyan','DarkGoldenRod','DarkGray','DarkGreen','DarkKhaki','DarkMagenta',
    'DarkOliveGreen','Darkorange','DarkOrchid','DarkRed','DarkSalmon','DarkSeaGreen','DarkSlateBlue','DarkSlateGray',
    'DarkTurquoise','DarkViolet','DeepPink','DeepSkyBlue','DimGray','DodgerBlue','Feldspar','FireBrick','FloralWhite',
    'ForestGreen','Fuchsia','Gainsboro','GhostWhite','Gold','GoldenRod','Gray','Green','GreenYellow','HoneyDew',
    'HotPink','IndianRed','Indigo','Ivory','Khaki','Lavender','LavenderBlush','LawnGreen','LemonChiffon',
    'LightBlue','LightCoral','LightCyan','LightGoldenRodYellow','LightGrey','LightGreen','LightPink','LightSalmon',
    'LightSeaGreen','LightSkyBlue','LightSlateBlue','LightSlateGray','LightSteelBlue','LightYellow','Lime',
    'LimeGreen','Linen','Magenta','Maroon','MediumAquaMarine','MediumBlue','MediumOrchid','MediumPurple',
    'MediumSeaGreen','MediumSlateBlue','MediumSpringGreen','MediumTurquoise','MediumVioletRed','MidnightBlue',
    'MintCream','MistyRose','Moccasin','NavajoWhite','Navy','OldLace','Olive','OliveDrab','Orange','OrangeRed',
    'Orchid','PaleGoldenRod','PaleGreen','PaleTurquoise','PaleVioletRed','PapayaWhip','PeachPuff','Peru','Pink',
    'Plum','PowderBlue','Purple','Red','RosyBrown','RoyalBlue','SaddleBrown','Salmon','SandyBrown','SeaGreen',
    'SeaShell','Sienna','Silver','SkyBlue','SlateBlue','SlateGray','Snow','SpringGreen','SteelBlue','Tan','Teal',
    'Thistle','Tomato','Turquoise','Violet','VioletRed','Wheat','White','WhiteSmoke','Yellow','YellowGreen');

var randomColor = function() {
  return Colors[Math.floor( Math.random() * Colors.length )];
};

var zTag = function(tag_name, text, options) {
  var tag = new String();
  tag += "<";
  tag += tag_name;
  
  for(var i in options) {
    if(typeof options[i] != "string") continue;
    tag += ' ' + i.replace(/^_/, "") + '="' + options[i] + '"';
  }

  tag += ">";

  if(!zConf.SINGLE_TAG.include(tag_name.toLowerCase())) {
    tag += text;
    tag += "</";
    tag += tag_name;
    tag += ">";
  }

  return tag;
};

var zPageLink = function(_title) {
  return zTag("a", _title.__decode().__encode_entities(), {
    //_title: _title.__decode().__encode_entities(),
    _class: "zedit_class page_link",
    _href: _title
  })
};

var zImgShow = function(_path) {
  return zTag("p", 
    zTag("img", "", {
      _src: _path,
      _class: "zedit_class",
      _alt: _path.split("/").last()
    }), { 
    _class: "zedit_class"
  });
};

ExtractIdFromUrl = function(_name, _url) {
	var url = _url || location.href;
	var name = _name + "s";
	var patt = new RegExp(name + "/([0-9]+)")

	var _match = patt.exec(url);

	if(_match && _match.length >= 2) return _match[1];
	else return false;
};






var Normalize = function() {
};

Normalize.prototype.cleanup = function(s) {
  
  this.extract_tag(s);
};

Normalize.prototype.extract_tag = function(s) {
  var pattern = /(<([a-z]+)[^>]*>)(.*?)(?=<\/\2>)/gi;
  var result = new Array();
  while(match = pattern.exec(s)) {
    result.push({ _open: match[1], _text: match[3], _close: "</" + match[2] + ">" });
  }

  return result;
};

// by discussion. THH
function setCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function getCookie(name) {
	var nameKey = name + "=";
	var cookies = document.cookie.split(';');
	for(var i=0;i < cookies.length;i++) {
		var cookie = cookies[i];
		while (cookie.charAt(0)==' ') cookie = cookie.substring(1,cookie.length);
		if (cookie.indexOf(nameKey) == 0) return cookie.substring(nameKey.length,cookie.length);
	}
	return null;
}

function delCookie(name) {
	setCookie(name,"",-1);
}

var __serialize = function(cap, data) {
	var _ret = new Object();
	for(var key in data) if(data[key] && !String(data[key]).is_empty()) _ret[cap + "[" + key + "]"] = data[key];

	return _ret;
};

//
// eventor
//
var Eventor = function(_e) {
	this.junkies = {};
	this.junkies["_event"] = _e;
};

Eventor.prototype = {

	_event: function(_e) {
		//if(!this.junkies["_event"]) this.junkies["_event"] = _e || window.event;

		return this.junkies["_event"];
	},

	_node: function() {
		if(!this.junkies["_node"]) {
			this.junkies["_node"] = this._event().target || this._event().srcElement;
		}

		return this.junkies["_node"];
	},

	_type: function() {
		if(!this.junkies["_type"]) this.junkies["_type"] = this._event().type

		return this.junkies["_type"];
	},

	_keycode: function() {
		if(!this.junkies["_keycode"]) this.junkies["_keycode"] = this._event().keycode || this._event().keyCode;

		return this.junkies["_keycode"];
	},

	_page: function() {
		if(!this.junkies["_page"]) {

			var sl = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
			var st = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;

			this.junkies["_page"] = { 
				x: this._event().pageX || this._event().clientX + sl,
				y: this._event().pageY || this._event().clientY + st
			}

		}

		return this.junkies["_page"];
	},

	_mouse: function() {
		if(!this.junkies["_mouse"]) {
			this.junkies["_mouse"] = {};
			this.junkies["_mouse"]["_x"] = this._event().clientX;
			this.junkies["_mouse"]["_y"] = this._event().clientY;
		}

		return this.junkies["_mouse"];
	},

	break_event: function() {
		if (this._event().stopPropagation) {
			this._event().stopPropagation();
		} else {
			this._event().returnValue = false;
		}

		if (this._event().preventDefault) {
			this._event().preventDefault();
		} else {
			this._event().cancelBubble = true;
		}
	}
}



//
// selection
// 
var Selection = function(_win) {
	this.win = _win;
	this.junkies = {};
}

Selection.prototype = {

	_node: function() {
		if(!this.junkies["_node"]) this.junkies["_node"] = this._current_node();

		return this.junkies["_node"];
	},

	_current_node: function() {
		var _node = null;
		if(Browser.msie) {
			_node = this._range().parentElement();
		} else {
			_node = this._range().anchorNode
			if(!_node) _node = this.win.document.firstChild;
			if(_node.nodeType == 3) _node = _node.parentNode
		}

		return _node;
	},

	_range: function() {
		if(!this.junkies["_range"]) {
			if(Browser.msie) {
				this.junkies["_range"] = this.win.document.selection.createRange();
			} else {
				this.junkies["_range"] = this.win.getSelection();
			}
		}

		return this.junkies["_range"];
	},

	_html: function() {
		if(!this.junkies["_html"]) {
			if(Browser.mozilla) {
				var _r = this._range();
				this.junkies["_range"] = this._range().getRangeAt(0);
				this.junkies["_html"] = new XMLSerializer().serializeToString(this._range().cloneContents());
				this.junkies["_range"] = _r;
			} else {
				this.junkies["_html"] = this._range().htmlText;
			}
		}

		return this.junkies["_html"];
	},

// utils
	_node_focus: function(_node) {
		if(!_node) return;

		if(Browser.msie) {
			this._range().moveToElementText(_node);
			this._range().collapse(false);
			//this._range().move('character',-1);
			this._range().select();
			_node.focus();
		} else if(Browser.safari) {
			this._range().collapse(_node, (_node.nodeType == 1) ? _node.childNodes.length : _node.length);
		} else {
			var low_range = document.createRange();
			low_range.selectNode(_node);
			// var selected = this.iframe.contentWindow.getSelection();
			this._range().addRange(low_range);
			this._range().collapse(_node, _node.childNodes.length);
			this.win.focus();
		}
	},

	_focus_to_end: function() {
		var _node = this.win.document.getElementsByTagName("body")[0].lastChild;
		this._node_focus(_node);
	},

	_focus_to_marker: function() {
		var _marker = this.win.document.getElementById("_marker");
		if(_marker) {
			this._node_focus(_marker);
			_marker.parentNode.removeChild(_marker);
		}
	}
} // Selection prototype

// Global Utils
var CumulativeOffset = function(element) {
	var lt_y = 0;
	var lt_x = 0;
	do {
		lt_y += element.offsetTop  || 0;
		lt_x += element.offsetLeft || 0;
		element = element.offsetParent;
	} while (element);

	return [lt_x, lt_y];
};

var PositionedOffset = function(element) {
	var lt_y = 0;
	var lt_x = 0;
	do {
		lt_y += element.offsetTop  || 0;
		lt_x += element.offsetLeft || 0;
		element = element.offsetParent;
		if(element) {
			if(/^body$/i.test(element.tagName)) break;
			if(/(relative|absolute)/i.test(String(element.style.position))) break;
		}
	} while (element);

	return [lt_x, lt_y];
}
