$.iwilab = $.iwilab || {};

String.prototype.trim = function(){
  return this.replace(/^\s+|\s+$/g, "");
};

String.prototype.isEmpty = function(){
  if (this == null || this == undefined || this.trim() == "") 
    return true;
  return false;
};

String.prototype.escapeHTML = function(){
  return (this.replace(/&/g, '&amp;').replace(/>/g, '&gt;').replace(/</g, '&lt;').replace(/"/g, '&quot;'));
};

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

String.prototype.wordwrap = function(options){
  if (this == null || this == "") 
    return "";
  
  var text = this;
  if (options['byte_length'] != null) 
    text = text.multibyteTruncate(options['byte_length']);
  text = text.split("").join("<wbr/>");
  return options['break_line'] ? text.replace(/\n/g, "<br/>") : text;
};

// TODO: 특수문자, 일본어반각 등에 대한 처리
// truncateString이 1자리보다 긴 문자열인 경우에 대한 처리
String.prototype.multibyteTruncate = function(byteLength){
  var truncateString = "..";
  
  var len = 0;
  var charLength = 0;
  for (var i = 0; i < this.length; i++) {
    if (this.charCodeAt(i) > 127) 
      charLength = 2;
    else 
      charLength = 1;
    
    len += charLength;
    
    if (len < byteLength) 
      continue;
    if (i == this.length - 1) {
      if (len > byteLength) 
        return this.substr(0, i) + truncateString;
    }
    else {
      if (len > byteLength) 
        return this.substr(0, i) + truncateString;
      else 
        return this.substr(0, i + 1) + truncateString;
    }
  }
  
  return this.valueOf();
};

String.prototype.get_multibyte_length = function(){
  var len = 0;
  var charLength = 0;
  for (var i = 0; i < this.length; i++) {
    if (this.charCodeAt(i) > 127) 
      charLength = 2;
    else 
      charLength = 1;
    
    len += charLength;
  }
  
  return len;
};

String.prototype.josa = function(j1, j2){
  txt = this.trim();
  var lastChar = txt.charAt(txt.length - 1);
  if ('가' <= lastChar && '힣' >= lastChar) {
    return (((lastChar.charCodeAt(0) - '가'.charCodeAt(0)) % 28) != 0) ? j2 : j1;
  }
  else {
    return (!txt.match(/le$/i) && lastChar.match(/[2459aeiouy]/i)) ? j1 : j2;
  }
}

copyToClipboard = function(elementId){
  var $element = $('#' + elementId);
  if ($.browser.msie) {
    window.clipboardData.setData('text', $element.html());
  }
  else {
    var flashcopier = 'flashcopier';
    if (!document.getElementById(flashcopier)) {
      var divholder = document.createElement('div');
      divholder.id = flashcopier;
      document.body.appendChild(divholder);
    }
    document.getElementById(flashcopier).innerHTML = '';
    var divinfo = '<embed src="/flash/_clipboard.swf" FlashVars="clipboard=' + encodeURIComponent($element.html()) + '" width="0" height="0" type="application/x-shockwave-flash"></embed>';
    document.getElementById(flashcopier).innerHTML = divinfo;
  }
};

copyToClipboard2 = function(text){
  if ($.browser.msie) {
    window.clipboardData.setData('text', text);
  }
  else {
    var flashcopier = 'flashcopier';
    if (!document.getElementById(flashcopier)) {
      var divholder = document.createElement('div');
      divholder.id = flashcopier;
      document.body.appendChild(divholder);
    }
    document.getElementById(flashcopier).innerHTML = '';
    //var embeded = '<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/WdXDgaEgaJ8&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/WdXDgaEgaJ8&hl=en&fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object>';
    var embeded = '<embed src="/flash/_clipboard.swf" FlashVars="clipboard=' + encodeURIComponent(text) + '" width="0" height="0" type="application/x-shockwave-flash"></embed>';
    document.getElementById(flashcopier).innerHTML = embeded;
  }
};

parseBoolean = function(str){
  if (!str) 
    return false;
  str = str.toString().toLowerCase();
  if (str == "true" || str == "yes" || parseInt(str) > 0) 
    return true;
  return false;
};

function externalScriptLoader(url){
  var script = document.createElement("script");
  script.src = url;
  script.type = "text/javascript";
  document.getElementsByTagName("head")[0].appendChild(script);
}

function getFlashObject(movieName){
  return document[movieName] || window[movieName];
}

function navigateToURL(url){
  document.location.href = url;
}

//
// iwilab ajax
//
var MESSAGE_UNEXPECTED_ERROR = "예기치 못한 오류가 발생했습니다. \n잠시 후 다시 시도해 주십시오.";
var MESSAGE_JAVASCRIPT_ERROR = "웹페이지에 예기치 못한 오류가 발생했습니다. \n페이지 새로고침 후 다시 시도해 주십시오."
var MESSAGE_STATUS_500_ERROR = "서비스에 예기치 못한 오류가 발생했습니다. \n잠시 후 다시 시도해 주십시오."
var MESSAGE_CONNECTION_FAILURE_ERROR = "서비스에 접속할 수 없습니다. \n인터넷 접속 확인 후 다시 시도해 주십시오."

$.iwilab.Ajax = {
  _data: {},
  global: '_global_',
  settings: {
    showError: false,
    errorMessage: "처리중입니다 잠시만 기다려 주세요."
  },
  start: function(url, options){
    if (!url) {
      url = '_global_';
    }
    if (!options) {
      options = {}
    }
    settings = $.extend(this.settings, options);
    if (!this._data[url]) {
      return (this._data[url] = true);
    }
    else {
      if (settings.showError) {
        alert(settings.errorMessage);
      }
      return false;
    }
  },
  end: function(url){
    if (!url) {
      url = '_global_';
    }
    if (this._data[url]) {
      this._data[url] = false;
      return true;
    }
    else {
      return false;
    }
  }
};

$.iwilab.ajax = function(hash, options){
  var url = hash.url;
  var _complete;
  if (hash.complete) {
    _complete = hash.complete;
    hash.complete = function(data){
      _complete(data);
      $.iwilab.Ajax.end(url);
    };
  }
  else {
    hash.complete = function(data){
      $.iwilab.Ajax.end(url);
    }
  }
  if ($.iwilab.Ajax.start(url, options)) {
    $.ajax(hash);
  }
};

function xhr_request_handler(request, handler){
  if (handler == null) 
    return;
  try {
    var json = request;
    if (typeof(request) == "string") {
      json = eval('(' + request + ')');
    }
    
    var dataType = json['dataType'];
    switch (dataType) {
      case "json":
        data = eval('(' + json["data"] + ')');
        break;
      default:
        data = json["data"];
        break;
    }
    
    var status = json['status'];
    if (status == "200") {
      if (handler.success != null) {
        handler.success(data);
      }
    }
    else 
      if (status == "403") {
        $.venice.auth.signIn();
      }
      else 
        if (status == "503") { // maintenance
          document.location.href = data.redirect;
        }
        else {
          if (handler.failure != null) {
            handler.failure(data);
          }
          else {
            if (data != "") 
              alert(data);
          }
        }
  } 
  catch (e) {
    $.iwilab.error.showAlert();
    
    if ($.browser.safari) {
      $.iwilab.error.sendError(e);
    }
    else {
      throw e;
    }
  }
}

//
// error handler
//
var DEFAULT_AJAX_TIMEOUT = 10 * 1000;

$.iwilab.error = {
  sendError: function(message, url, line, stack){
    if ($.iwilab.error.lock) 
      return;
    
    $.iwilab.error.lock = true;
    
    stack = (stack || "") + "\n";
    stack += $.iwilab.error.getStackTrace().join("\n");
    
    $.ajaxSetup({
      timeout: 3 * 1000
    });
    
    $.ajax({
      type: "POST",
      url: "/log/script_error",
      data: {
        url: url || document.location.href,
        message: message || "",
        line: line || "unknown line number",
        stack: stack || ""
      },
      global: false,
      success: function(msg){
        $.iwilab.error.lock = false;
      }
    });
    
    $.ajaxSetup({
      timeout: DEFAULT_AJAX_TIMEOUT
    });
  },
  showAlert: function(status){
    var code = status == undefined ? -1 : parseInt(status);
    var message = "";
    
    if (code >= 12000 && code < 13000) 
      code = 12000; // IE
    switch (code) {
      case -1:
        message = MESSAGE_JAVASCRIPT_ERROR;
        break;
      case 0:
        message = MESSAGE_CONNECTION_FAILURE_ERROR;
        break;
      case 12000:
        message = MESSAGE_CONNECTION_FAILURE_ERROR;
        break;
      case 500:
        message = MESSAGE_STATUS_500_ERROR;
        break;
      default:
        message = MESSAGE_UNEXPECTED_ERROR;
    }
    alert(message);
  }
};

// prevent from unlimited loop
$.iwilab.error.lock = false;

function errorHandler(message, url, line, stack){
  $.iwilab.error.sendError(message, url, line, stack);
}

(function(){
  $.iwilab.error.getStackTrace = (function(){
  
    var mode;
    try {
      (0)()
    } 
    catch (e) {
      mode = e.stack ? 'Firefox' : window.opera ? 'Opera' : 'Other';
    }
    
    switch (mode) {
      case 'Firefox':
        return function(){
          try {
            (0)()
          } 
          catch (e) {
            return e.stack.replace(/^.*?\n/, '').replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split("\n");
          }
        };
        
      case 'Opera':
        return function(){
          try {
            (0)()
          } 
          catch (e) {
            var lines = e.message.split("\n"), ANON = '{anonymous}', lineRE = /Line\s+(\d+).*?in\s+(http\S+)(?:.*?in\s+function\s+(\S+))?/i, i, j, len;
            
            for (i = 4, j = 0, len = lines.length; i < len; i += 2) {
              if (lineRE.test(lines[i])) {
                lines[j++] = (RegExp.$3 ? RegExp.$3 + '()@' + RegExp.$2 + RegExp.$1 : ANON + RegExp.$2 + ':' + RegExp.$1) +
                ' -- ' +
                lines[i + 1].replace(/^\s+/, '');
              }
            }
            
            lines.splice(j, lines.length - j);
            return lines;
          }
        };
        
      default:
        return function(){
          var curr = arguments.callee.caller, FUNC = 'function', ANON = "{anonymous}", fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], j = 0, fn, args, i;
          while (curr) {
            fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
            args = stack.slice.call(curr.arguments);
            i = args.length;
            
            while (i--) {
              switch (typeof args[i]) {
                case 'string':
                  args[i] = '"' + args[i].replace(/"/g, '\\"') + '"';
                  break;
                case 'function':
                  args[i] = FUNC;
                  break;
                default:
                  args[i] = args[i] + "";
                  break;
              }
            }
            stack[j++] = fn + '(' + args.join() + ')';
            curr = curr.caller;
            if (j > 5) 
              break;
          }
          return stack;
        };
    }
  })();
})();

// Safari does not yet support window.onerror
window.onerror = errorHandler;






$.iwilab = $.iwilab || {};

$.Editable = {
	element_to_hide: null,
	hide_element: function(event) {
		var ops = $.Editable.element_to_hide;
		if (ops) {
			if (event.target != ops.el[0]) {
				$.Editable.save(ops.element, ops.el, ops.o);
			}
		}
	},
	show: function(element, el, o) {
		el.val(element.text());
		el.show();
		element.hide();
		el.focus();
		$.Editable.element_to_hide = {element:element, el:el, o:o};
		$(document.body).click($.Editable.hide_element);
	},
	hide: function(element, el, o) {
		el.hide();
		element.show();
		$.Editable.element_to_hide = null
		$(document.body).unbind('click', $.Editable.hide_element);
	},
	save: function(element, el, o) {
		element.text(el.val());
		$.Editable.hide(element, el, o);
		o.onUpdate(el.val());
	},
	cancel: function(element, el, o) {
		el.val(element.text());
		el.blur();
		$.Editable.hide(element, el, o);
		o.onCancel();
	},
	onUpdate: function(newText) {
		
	},
	onCancel: function() {
		
	},
	editable: function(_element, o) {
		var $editable = this;
		var element = $(_element);
		var _o = {
			inputClass: 'inputClass',
			trigger: '.trigger',
			inputElement: 'textInput',
			onUpdate: $editable.onUpdate,
			onCancel: $editable.onCancel
		};

		var options = $.extend(_o, o);

		var el;

		switch (options.inputElement) {
			case 'textInput':
			default:
				el = $('<input type="text" class="' + options.inputClass + '" style="display:none;" />');
				break;
		}

		element.before(el);

		$(options.trigger).click(function(){
			$editable.show(element, el, options);
			return false;
		});

		el.keydown(function(key){
			if (key.keyCode == 13) {
				$editable.save(element, el, options);
			} else if (key.keyCode == 27) {
				$editable.cancel(element, el, options);
			}
		});
		el.blur(function(){
			$editable.save(element, el, options);
		});
	}
};

$.fn.iwiEditable = function(o) {
	this.each(function() {
		$.Editable.editable(this, o);
	});
};


//
// DefaultTextInput: text input that has default text
//
$.iwilab.DefaultTextInput = function(element, defaultText, o){
  this.options = $.extend({
    defaultTextClass: false,
    defaultStyle: false
  }, o);
  
  this.element = element;
  this.defaultText = defaultText;
  
  switch (element.tagName.toLowerCase()) {
    case "input":
      this.textInputType = element.type.toLowerCase();
      if (this.textInputType != "text" && this.textInputType != "password") 
        return;
      break;
    case "textarea":
      this.textInputType = "textarea";
      break;
    default:
      return;  }
  
  this.createDefaultElement();
  this.reset();
  
  var self = this;
  $(this.element).parents('form').bind('reset', function(){
    self.reset();
  });
  
  $(this.element).blur(function(e){
    if (this.value.trim() == '') {
      self.reset();
    }
  });
  
  $(this.defaultElement).focus(function(e){
    self.changeFocus();
  });
};

$.extend($.iwilab.DefaultTextInput.prototype, {
  defaultElement: null,
  defaultText: '',
  
  createDefaultElement: function(){
    if (this.defaultElement != null) 
      return;
    
    // clone object (do not copy events)
    var $thisEl = $(this.element);
    var $el;
    if (this.textInputType == "textarea") {
      $el = $("<textarea>" + this.defaultText + "</textarea>");
    }
    else {
      $el = $("<input type='text' value='" + this.defaultText + "'>");
    }
    $el.attr("id", "_dti_" + $thisEl.attr("id"));
    $el.attr('disabled', $thisEl.attr('disabled'));
    
    // set attributes
    if (this.textInputType == "textarea") {
      $el.attr("cols", $thisEl.attr("cols"));
      $el.attr("rows", $thisEl.attr("rows"));
    }
    else {
      $el.attr("maxlength", $thisEl.attr("maxlength"));
      $el.attr("size", $thisEl.attr("size"));
    }
    
    $el.attr("class", $thisEl.attr("class"));
    if (this.options.defaultTextClass) {
      $el.addClass(this.options.defaultTextClass);
    }
    
    $el.attr("style", $thisEl.attr("style"));
    if (this.options.defaultStyle) {
      try {
        var styles = this.options.defaultStyle.split(';');
        for (var i = 0; i < styles.length; i++) {
          var style = styles[i].split(':');
          $el.css(style[0].trim(), style[1].trim());
        }
      } 
      catch (ignore) {
      }
    }
    
    this.defaultElement = $el;
    $(this.element).before(this.defaultElement);
  },
  reset: function(){
    if ($(this.element).val().length > 0) {
      $(this.defaultElement).hide();
      $(this.element).removeClass('hidden'); // Don't use show() method. IE Bug
    }
    else {
      $(this.element).addClass('hidden'); // Don't use hide() method. IE Bug
      $(this.defaultElement).show();
    }
  },
  changeFocus: function(){
    $(this.defaultElement).hide().blur();
    $(this.element).removeClass('hidden').focus(); // Don't use show() method. IE Bug
    if (this.options.afterFocus) 
      this.options.afterFocus(this.element);
  },
  changeText: function(text){
    $(this.defaultElement).val(text);
  }
});

$.fn.extend({
  defaultTextInput: function(defaultText, o){
    return this.each(function(){
      if (this.defaultTextInput == undefined) {
        this.defaultTextInput = new $.iwilab.DefaultTextInput(this, defaultText, o);
      }
    });
  },
  resetDefaultTextInput: function(){
    return this.each(function(){
      if (this.defaultTextInput != undefined) {
        this.defaultTextInput.reset();
      }
    });
  },
  changeDefaultText: function(text){
    return this.each(function(){
      if (this.defaultTextInput != undefined) {
        this.defaultTextInput.changeText(text);
      }
    });
  },
  changeFocus:function(text){
  	 return this.each(function(){
      if (this.defaultTextInput != undefined) {
        this.defaultTextInput.changeFocus();
      }
    });
  }
});

//
// ErrorField
//
$.fn.extend({
  errorField: function(o){
    return this.each(function(){
      $(this).data('_errorField', new $.iwilab.ErrorField(this, o));
    });
  },
  showError: function(key, message){
    var $error = $('#check_' + key + '_result');
    $error.append("<div>" + message + "</div>").show();
  },
  clearError: function(){
    this.find('span').empty().hide();
  }
});
$.iwilab.ErrorField = function(element, o){
  this.options = $.extend({}, o);
  $(element).append('<span id="check_' + $(element).attr('ref') + '_result"></span>');
};

//
// LimitInput
//
$.iwilab.LimitInput = function(element, o){
	var options = $.extend(this.defaults, o);
	$.extend(this, options);
  
  this.element = element;
  
	this.initialize();
};

$.extend($.iwilab.LimitInput.prototype, {
	defaults: {
		limit: 255,
		onTextLengthChange: function(length) {}
	},
	
	initialize: function() {
		var self = this;
		var $el = $(this.element);
		
	  $(this.element).keyup(function(event) {
			var currentText = $el.val();
			if (self.getBytesCount(currentText) > self.limit) {
				var caretPos = $el.caret().start;
				var len = currentText.length;
				currentText = self.cutTextByLimit(currentText, caretPos);
				caretPos -= (len - currentText.length);
				
				$el.blur();
				$el.val(currentText);
				$el.focus();
				
		  	$el.caret({
		  		start: caretPos,
		  		end: caretPos
		  	});
	  	}
			self.onTextLengthChange(self.getBytesCount(currentText));
		}).blur(function(event) {
			var currentText = $el.val();
			if (self.getBytesCount(currentText) > self.limit) {
				var caretPos = $el.caret().start;
				currentText = self.cutTextByLimit(currentText, caretPos);
				$el.val(currentText);
	  	}
			self.onTextLengthChange(self.getBytesCount(currentText));
		});
		
		this.onTextLengthChange(this.getBytesCount($el.val()));
	},
	getBytesCount: function(str) {
	  var count = 0;
	  for (var i = 0; i < str.length; i++) {
	    var onechar = str.charAt(i);
	    if (str.charCodeAt(i) >= 128) {
	      count += 2;
	    } else if (onechar != '\r') {
	      count++;
	    }
	  }
	  return count;
	},
	cutTextByLimit: function(str, caretPos) {
	  var tmpStr = str;
		var len = str.length;
	  while (this.getBytesCount(tmpStr) > this.limit) {
	    tmpStr = tmpStr.substring(0, caretPos-1) + tmpStr.substring(caretPos, len);
	    caretPos -= 1;
			len -= 1;
	  }
	  return tmpStr;
	}
});

$.fn.extend({
  limitInput: function(o){
    return this.each(function(){
      if (this.limitInput == undefined) {
        this.limitInput = new $.iwilab.LimitInput(this, o);
      }
    });
  }
});

//
// increment, decrement
//
$.fn.extend({
  increment: function(n, delimiter){
    return this.each(function(){
      var count = $(this).number();
      if (delimiter) 
        $(this).text((count + n).toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + ","));
      else 
        $(this).text(count + n);
    });
  },
  decrement: function(n, delimiter){
    return this.each(function(){
      var count = $(this).number();
      if (delimiter) 
        $(this).text((count - n).toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1" + ","));
      else 
        $(this).text(count - n);
    });
  },
  number: function(){
    return parseInt($(this).text().replace(/[^0-9]/g, "")) || 0;
  }
});

RegExp.escape = function(str){
  return str.replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&");
};

//
// highlight
//
$.fn.extend({
  highlight: function(terms){
    return this.each(function(){
      var t = null;
      if (terms instanceof Array) {
        var escapedTerms = new Array();
        for (var i = 0; i < terms.length; i++) {
          escapedTerms.push(RegExp.escape(terms[i]));
        }
        t = escapedTerms.join("|");
      }
      else {
        t = RegExp.escape(terms);
      }
      
      var regex = new RegExp('(' + t + ')', 'gi');
      var html = $(this).html();
      var newhtml = html.replace(regex, '<span class="highlight">$1</span>');
      $(this).html(newhtml);
    });
  }
});

function resizeThumbnail(el, maxWidth, maxHeight, bMagnify){
  if (el.width <= 0 || el.height <= 0 || maxWidth <= 0 || maxHeight <= 0) 
    return;
  if (bMagnify == null && el.width <= maxWidth && el.height <= maxHeight) 
    return;
  
  if (el.width / el.height > maxWidth / maxHeight) {
    el.width = maxWidth;
    el.height = (el.height * maxWidth) / el.width;
  }
  else {
    el.width = (el.width * maxHeight) / el.height;
    el.height = maxHeight;
  }
}

function isScrolledIntoView(elem){
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();
  
  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

  
  return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) &&
  (elemBottom <= docViewBottom) &&
  (elemTop >= docViewTop));
}

minScrollPos = function(elem){
  var docViewTop = $(window).scrollTop();
  var docViewBottom = docViewTop + $(window).height();
  
  
  var elemTop = $(elem).offset().top;
  var elemBottom = elemTop + $(elem).height();

  
  if (elemBottom > docViewBottom) {
    return docViewTop + (elemBottom - docViewBottom) + 10;
  }
  if (elemTop < docViewTop) {
    return elemTop - 10;
  }
  return null;
}

function setCaretTo(obj, pos) { 
    if(obj.createTextRange) { 
        /* Create a TextRange, set the internal pointer to
           a specified position and show the cursor at this
           position
        */ 
        var range = obj.createTextRange(); 
        range.move("character", pos); 
        range.select(); 
    } else if(obj.selectionStart) { 
        /* Gecko is a little bit shorter on that. Simply
           focus the element and set the selection to a
           specified position
        */ 
        obj.focus(); 
        obj.setSelectionRange(pos, pos); 
    } 
} 


$.venice = {};

$.venice.debug = false;
$.venice.hasFirebug = ("console" in window && "firebug" in window.console);

/* ajax event handler */
$(document).bind("ajaxError", function(event, request, settings){
  if (request.status == 0) 
    return; // exception for firefox page refresh while processing ajax calls
  if (request.status == 403) {
    var response = eval('(' + request.responseText + ')');
    $.venice.auth.signIn();
  }
  else {
    var stack = "";
    for (var i in settings) {
      stack += i + ' = ' + settings[i] + "\n";
    }
    
    if ($.iwilab.error.lock) {
      $.iwilab.error.lock = false;
      return;
    }
    
    $.iwilab.error.sendError(event.type + "[url : " + settings.url + " status : " + request.status + "]", null, null, stack);
    
    if (settings['error'] == null) {
      $.iwilab.error.showAlert(request.status);
    }
  }
});

$(function() {
	$(document).pngFix({
		blankgif: '/images/blank.gif'
	}); 
	
	if ($.venice.debug && $.venice.hasFirebug) {
		$.fn.bind = function(bind){
			return function(){
				console.count("jQuery bind count");
				console.log("jQuery bind %o", this);
				return bind.apply(this, arguments);
			};
		}($.fn.bind);
	}
});



$.venice.ui = {};

$.venice.ui.Utils = function(o){
  o = $.extend({}, o);
  
  $.extend(this, o);
};

$.extend($.venice.ui.Utils.prototype, {
  showPreviewPopup: function(options){
    options = options || {};
    var $popup = null;
    
    $('#attach_popup').remove();
    var menus = '';
    
    if (options.menus) {
      $.each(options.menus, function(){
        menus += '<div class="menu">' + this.toString() + '</div>';
      });
    }
    
    var width = options.width;
    var height = options.height;
    
    var html = '\
				<div class="attach_preview_popup" id="attach_popup" style="display:none;position:absolute;">\
	        <table class="skin"><tr><td class="tl"></td><td class="tc"></td><td class="tr"></td></tr>\
	          <tr>\
	            <td class="ml"></td>\
	            <td class="mc">\
	              <table class="table">\
				  <tr><td>\
				  <div class="menu_bar">\
									' + menus + '\
									<div class="spClose closelabel" onclick="$(this).parents(\'.simple_popup\').jqmHide();return false"></div>\
									<div class="clear"></div>\
								</div>\
				  </td></tr>\
	          <tr valign="middle">\
	            <td>\
	               <div class="content_area" style=""></div>\
	            </td>\
	          </tr>\
	        </table>\
	            </td>\
	            <td class="mr">\
	            </td>\
	          </tr>\
	          <tr><td class="bl"></td><td class="bc"></td><td class="br"></td></tr></table>\
      	</div>';
    var d = document.createElement('div')
    d.innerHTML = html;
    document.body.appendChild(d);
    $popup = $('#attach_popup');
    $popup.jqm({
      closeClass: 'spClose',
      trigger: '',
      modal: false,
      onShow: function(hash){
        hash.o.css({
          zIndex: 1000000
        });
        wd = width || (hash.w.find('.content_area .item').attr('width') > 0 ? hash.w.find('.content_area .item').attr('width') : hash.w.width());
        hash.w.css({
          left: Math.round(($(document).width() - wd) / 2),
          top: $(document).scrollTop() + 100,
          zIndex: 1000001
        }).show();
      },
      onHide: function(hash){
        $('#attach_popup .swfobject').each(function(){
            if (swfobject) {
                swfobject.removeSWF(this.id);
            }
        });
        
        $('#attach_popup embed').each(function(){
          this.parentNode.removeChild(this);
        });
        
        hash.w.hide();
        hash.o.remove();
      }
    });
    
    if (width) {
      $popup.find('.content_area').css('width', width + 'px');
    }
    
    if (height) {
      $popup.find('.content_area').css('height', width + 'px');
    }
    
    $popup.find('.menu_bar > *').each(function(){
      if ($(this).css('clear') != 'both') {
        $(this).css("float", "left");
        $(this).css("margin-left", "3px");
      }
    });
    
    var inner_html = options.html;
    
    if (options.url) {
      inner_html = $.get(options.url, function(data){
        $popup.find('.content_area').css({
          textAlign: 'center'
        }).html(data);
        $popup.jqmShow();
      });
    }
    else {
      $popup.find('.content_area').css({
        textAlign: 'center'
      }).html(inner_html);
      $popup.jqmShow();
    }
  },
  
  showAttachPreviewPopup: function(options){
    var link_to = options.link_to;
    
    if (!link_to) {
      return this.showPreviewPopup(options);
    }
    
    var menus = options.menus || [];
    var _menus = menus instanceof Array ? menus : Array(menus);
    _menus.push('<a href="' + link_to + '" class="_link_to"><div class="link_to"></div></a>');
    return this.showPreviewPopup($.extend(options, {
      menus: _menus
    }));
  },
	
	autoFocus: function(target_class) {
		var done = false;
		var $target = $();
		
		if (typeof(target_class) != 'undefined') {
			$target = (typeof(target_class) == 'string') ? $(target_class) : target_class;
		}
		
		$target.find(":text:visible,:password:visible").each(function(){
			if (!done && $(this).val() == '') {
				this.focus();
				done = true;
			}
		});
	}
});

$(function() {
	new $.venice.ui.Utils().autoFocus();
});

$.venice.auth = {
  signIn: function() {
	document.location.href = "/account/signin";
  }  
};



// Dependency: application.js#externalScriptLoader
var g_current_obj = null;

function search_callback_(context, json){
    g_current_obj.preview(context, json);
}

$.venice.OpenApi = function(o){
    o = $.extend({
        parentArea: null,
        progressAreaClass: "loadingdata",
        previewAreaClass: "loadingdata",
        onValidate: function(){
            return false;
        },
        getURL: function(){
            return "";
        },
        parse: function(json){
            return []
        },
        oneClick: false,
        
        scriptDOM: null,
        callback: 'search_callback_',
        timer: null,
        afterComplete: null,
        afterSelect: null,
        
        items: null,
        sid: null,
        
        noResultMessageTemplate: '<center style="margin:20px 0 20px 0"><strong>이야기가 검색되지 않았습니다. 다른 키워드로 이야기를 검색해보세요.</strong><center>',
        paginatorTemplate: '<div class="paginate"><div><% for(var i = 0; i< page_html.length; i++) { %><%= page_html[i] %><% } %></div></div>',
        pageTemplate: '<% if (page.current) { %><strong><%= page.label %></strong><% } else { %><a href="#" page="<%= page.start %>"><%= page.label %></a><% } %>',
        itemsTemplate: '<table class="openapi_search_results">' +
        '<% for(var i = 0; i< item_html.length; i++) { %>' +
        '<% if(i % 4 == 0) { %><tr><% } %>' +
        '<%= item_html[i] %>' +
        '<% if(i % 4 == 3) { %></tr><% } %>' +
        '<% } %>' +
        '</tr></table>',
        btn_template: '<div class="resource_add_btn" style="display:none;"><a href="#"><img src="/images/widget/openapi/btn_addimage.gif" /></a></div>'
    
    }, o);
    
    
    
    $.extend(this, o);
};

$.extend($.venice.OpenApi.prototype, {
    search: function(start, keywords){
        if (start >= 0) 
            this.start = start;
        if (keywords) 
            this.keywords = keywords;
        if (this.validate() == false) 
            return false;
        this.showProgressMessage("Loading data...");
        var url = this.getURL()
        if (url != '') 
            this.fetch(url);
    },
    
    validate: function(){
        if (!this.keywords) {
            alert("검색어를 입력하셔야 합니다.");
            return false;
        }
        if (this.keywords.trim() == '') {
            alert("검색어를 입력하셔야 합니다.");
            return false;
        }
        return true;
    },
    preview: function(context, json){
        if (context != 'google') 
            json = context;
        if (typeof json != "object") {
            this.setProgressMessage("Error!", "warn");
            return;
        }
        var self = this;
        if (this.scriptDOM && !this.timer) 
            return;
        // is too late...
        this.clearTimer();
        
        try {
            this.parse(json);
            var itemsHtml = this.toHTML(this.items, this.pages);
            
            var $preview = $(this.parentArea).find('.' + this.previewAreaClass);
            $preview.addClass('openapi_search_results');
            $preview.html(itemsHtml).show();
            
            $preview.find('.thumbnail').each(function(){
                self.bindClickEvents($(this));
            });
            
            $preview.find('span.zoom img').each(function(){
                self.bindZoomEvents($(this));
            });
            
            $preview.find('.paginate a').each(function(){
                self.bindPageEvents($(this));
            });
            
            this.resource_add_btn = $preview.find('.resource_add_btn');
            $preview.find('.resource_add_btn a').each(function(){
                self.bindSelectEvents($(this));
            })
            if (this.afterComplete) {
                this.afterComplete((!this.items) ? false : (this.items.length > 0));
            }
        } 
        catch (e) {
            alert('failed. ' + e.message);
            this.timeout_("fail to parse data");
        }
        
    },
    
    bindPageEvents: function($page){
        var self = this;
        $page.click(function(){
            self.search($page.attr('page'));
            return false;
        });
    },
    
    bindZoomEvents: function($div){
        var self = this;
        $div.click(function(){
            self.zoom($(this));
            return false;
        });
    },
    bindSelectEvents: function($btn){
        var self = this;
        
        $btn.click(function(){
            $(this).parent('.resource_add_btn').hide();
            self.dblselect($(this));
            return false;
        });
    },
    
    bindClickEvents: function($image){
        var self = this;
        $image.click(function(){
            self.select($(this));
        });
        $image.dblclick(function(){
            self.dblselect($(this));
            return false;
        })
    },
    fetch: function(url){
        g_current_obj = this;
        
        if (this.scriptDOM) {
            if (this.scriptDOM.parentNode) 
                this.scriptDOM.parentNode.removeChild(this.scriptDOM);
            this.scriptDOM = null;
        }
        this.scriptDOM = document.createElement('script');
        this.scriptDOM.setAttribute('type', 'text/javascript');
        $('HEAD')[0].appendChild(this.scriptDOM);
        
        // setting-timer first
        this.setTimer();
        this.scriptDOM.setAttribute('src', url);
    },
    
    clear: function(){
        $('#' + this.sid + ' form')[0].reset();
        this.hideProgressMessage();
        $('.' + this.previewAreaClass).hide();
    },
    
    showProgressMessage: function(message){
        var $progressArea = $(this.parentArea).find('.' + this.progressAreaClass);
        if ($progressArea.length == 0) 
            return;
        this.setProgressMessage(message, 'loading');
        
        if ($progressArea.is(':visible')) 
            return;
        $progressArea.show();
    },
    setProgressMessage: function(message, type){
        var imageTag = '';
        var msg = imageTag + (message || "");
        $(this.parentArea).find('.' + this.progressAreaClass).html(msg);
    },
    hideProgressMessage: function(){
        $(this.parentArea).find('.' + this.progressAreaClass).hide();
    },
    
    setTimer: function(sec){
        if (this.timer) 
            this.clearTimer();
        sec = sec || 10000;
        // default 10 sec.
        this.timer = setTimeout(this.timeout_, sec);
    },
    clearTimer: function(){
        if (this.timer) {
            clearTimeout(this.timer);
            this.timer = null;
        }
    },
    timeout_: function(message){
        var self = this;
        self.timer = null;
        self.setProgressMessage(message || "fail to get data", "warn");
    },
    
    toggle: function(el){
        $(el).hasClass('selected') ? this.unselect(el) : this.select(el);
    },
    select: function(el){
        if ($(this.parentArea).find('.openapi_search_results').find('.selected').length > 0) {
            this.unselect($(this.parentArea).find('.openapi_search_results').find('.selected')[0]);
        }
        $(el).addClass('selected');
        if (this.oneClick && this.afterSelect) {
            this.afterSelect(this.items[$(el).attr("_result_id")]);
        }
        else {
            if (this.resource_add_btn) {
                this.resource_add_btn.show();
                this.resource_add_btn.find('a').attr('_result_id', $(el).attr("_result_id"))
            }
        }
    },
    dblselect: function(el){
        if ($(this.parentArea).find('.openapi_search_results').find('.selected').length > 0) {
            this.unselect($(this.parentArea).find('.openapi_search_results').find('.selected')[0]);
        }
        $(el).addClass('selected');
        if (this.afterSelect) {
            this.resource_add_btn.hide();
            this.afterSelect(this.items[$(el).attr("_result_id")]);
        }
    },
    zoom: function(el){
        var item = this.items[$(el).attr("_result_id")];
        var html_to_show = this.compile_template(this.previewTemplate, {
            item: item,
            i: 0
        });
        this.onPopupPreview(html_to_show);
    },
    unselect: function(el){
        $(el).removeClass('selected');
        if (this.afterUnselect) {
            this.afterUnselect(this.items[$(el).attr("_result_id")]);
        }
    },
    onPopupPreview: function(inner_html, btn_html){
        var $popup = null;
        
        $popup = $('#search_image_popup');
        if ($popup.length < 1) {
            var html = '<div  class="simple_popup" id="search_image_popup" style="display:none;position:absolute;">\
        <table class="skin"><tr><td class="tl"></td><td class="tc"></td><td class="tr"></td></tr>\
          <tr>\
            <td class="ml"></td>\
            <td class="mc">\
              <table class="table">\
          <tr>\
                  <td><div class="sp_close_btn"><div class="down"></div><div class="spClose closelabel" onclick="$(this).parents(\'.simple_popup\').jqmHide();return false"></div></div></td>\
          </tr>\
          <tr valign="middle">\
            <td>\
                     <div class="image_area" style="">\
               </div>\
            </td>\
          </tr>\
        </table>\
            </td>\
            <td class="mr">\
            </td>\
          </tr>\
          <tr><td class="bl"></td><td class="bc"></td><td class="br"></td></tr></table>\
      </div>';
            var d = document.createElement('div')
            d.innerHTML = html;
            document.body.appendChild(d);
            $popup = $('#search_image_popup');
            $popup.jqm({
                closeClass: 'spClose',
                trigger: '',
                modal: false,
                onShow: function(hash){
                    hash.o.css({
                        zIndex: 1000000
                    });
                    wd = hash.w.find('.image_area img').attr('width') > 0 ? hash.w.find('.image_area img').attr('width') : hash.w.width();
                    
                    hash.w.css({
                        left: Math.round(($(document).width() - wd) / 2),
                        top: $(document).scrollTop() + 100,
                        zIndex: 1000001
                    }).show();
                },
                onHide: function(hash){
                    $('#search_image_popup .ytplayer').each(function(){
                        if (swfobject) {
                            swfobject.removeSWF(this.id);
                        }
                    });
                    hash.w.hide();
                    hash.o.remove();
                }
            });
        }
        if (btn_html) {
            $popup.find('.down').html(btn_html)
        }
        else {
            $popup.find('.down').html('');
        };
        $popup.find('.image_area').css({
            textAlign: 'center'
        }).html(inner_html);
        
        $popup.jqmShow();
    },
    toHTML: function(items, pages){
        try {
        
            if (!items) 
                return this.noResultMessageTemplate;
            
            if (items.length == 0) 
                return this.noResultMessageTemplate;
            var itemHTML = [];
            for (var i = 0; i < items.length; i++) {
                var item = items[i];
                itemHTML.push(this.compile_template(this.itemTemplate, {
                    item: item,
                    i: i
                }));
            }
            var itemsHTML = this.compile_template(this.itemsTemplate, {
                item_html: itemHTML
            })
            
            var pageHTML = [];
            if (pages && pages.length > 0) {
                for (var i = 0; i < pages.length; i++) {
                    var page = pages[i];
                    pageHTML.push(this.compile_template(this.pageTemplate, {
                        page: page
                    }));
                }
            }
            var paginator = this.compile_template(this.paginatorTemplate, {
                page_html: pageHTML
            })
            
            var add_resource_btn = this.btn_template
        } 
        catch (e) {
            return this.noResultMessageTemplate;
        }
        return itemsHTML + paginator + add_resource_btn;
    },
    
    compile_template: function(str, data){
        // Figure out if we're getting a template, or if we need to
        // load the template - and be sure to cache the result.
        var fn = !/\W/.test(str) ? this.compile_template(document.getElementById(str).innerHTML) :        // Generate a reusable function that will serve as a template
        // generator (and which will be cached).
        new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" +
        
        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +
        
        // Convert the template into pure JavaScript
        str.replace(/[\r\t\n]/g, " ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g, "$1\r").replace(/\t=(.*?)%>/g, "',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'") +
        "');}return p.join('');");
        
        // Provide some basic currying to the user
        return data ? fn(data) : fn;
    }
    
    
});

/** Youtube */

$.venice.OpenApi.Youtube = function(o){
    o = $.extend({
        clientId: "ytapi-IWILabCorp-Wisia-7op7ug0f-0",
        apiKey: "AI39si7_QNOrYoyNc-YfaNlDVnULoflnWRFC-hSrLctOAIwe-TeBUxLsnCzqQQ5uLSEVtxD73dOhPLsSD2mvENJeTK-Oa_woww",
        getURL: function(){
            var url = 'http://gdata.youtube.com/feeds/api/videos?alt=json-in-script&start-index=' + this.start + '&max-results=' + this.perPage + '&format=5&callback=' + this.callback + '&client=' + this.clientId;
            if (this.keywords && this.keywords != "") 
                url += "&vq=" + encodeURI(this.keywords);
            return url;
        },
        keywords: null,
        start: 1,
        perPage: 8,
        paginator: null,
        noResultMessageTemplate: '<center style="margin:20px 0 20px 0"><strong>동영상이 검색되지 않았습니다. 다른 키워드로 동영상을 검색해보세요.</strong><center>',
        previewTemplate: '<div id="ytapiplayer_<%= i %>" class="need_flash_player">' +
        '<div class="message_area">' +
        '<div>You need Flash player 8+ and JavaScript enabled to view this video.</div>' +
        '<div class="link"><a href="http://get.adobe.com/kr/flashplayer/?promoid=DJDZP" target="_blank">플래쉬 플레이어 설치하러 가기</a></div>' +
        '</div></div>' +
        '<script type="text/javascript">' +
        '  var params = { allowScriptAccess: "always", allowFullScreen: "true" };' +
        '  var atts = { id: "ytplayer_<%= i %>", "class": "ytplayer swfobject" };' +
        '  swfobject.embedSWF("<%= item.url %>&hl=ko&fs=1&hd=1&enablejsapi=1&playerapiid=ytplayer_<%= i %>","ytapiplayer_<%= i %>", "425", "356", "8", null, null, params, atts);' +
        '</script>',
        itemTemplate: '<td class="openapi_search_result" valign="top" align="center">' +
        '<div class="thumbnail video" _result_id="<%= i %>">' +
        '<table style="border-collapse:collapse;"><tr><td valign="bottom" align="center" style="width:120px;height:90px;">' +
        '<img src="<%= item.thumbnail %>" <% if (item.tbsize.width > 120) { %>width="120"<% } else if (item.tbsize.height > 90 ) { %>height="90"<% } %> />' +
        '</td></tr></table>' +
        '<span style="position:absolute;bottom:1px;right:1px;cursor:pointer;" class="zoom"><img src="/images/widget/openapi/btn_vodpreview.png" _result_id="<%= i %>"/></span>' +
        '</div>' +
        '<div class="api_desc"><%= item.title %></div>' +
        '</td>',
        btn_template: '<div class="resource_add_btn" style="display:none;"><a href="#"><img src="/images/widget/openapi/btn_vodupload.png" /></a></div>',
        parse: function(json){
            // http://code.google.com/apis/youtube/reference.html#youtube_data_api_tag_feed
            var items = [];
            var entry = (json) ? json.feed.entry : null;
            
            if (!entry) {
                this.items = [];
                return items;
            }
            
            for (var i = 0; i < entry.length; i++) {
                var item = {
                    type: 'video',
                    vendor: 'youtube'
                };
                var media = entry[i].media$group;
                $.each(entry[i].link, function(){
                    if (this.rel == 'alternate') 
                        item.from = decodeURI(this.href);
                });
                item.url = item.from.replace("watch?v=", "v/");
                item.title = media.media$title.$t;
                item.description = media.media$description.$t;
                item.tags = media.media$keywords.$t;
                item.thumbnail = media.media$thumbnail[0].url;
                item.tbsize = {
                    width: media.media$thumbnail[0].width,
                    height: media.media$thumbnail[0].height
                };
                item.date = entry[i].published.$t;
                items.push(item);
            }
            
            /* Paginator */
            var feed = json.feed;
            var maxPage = Math.ceil(feed.openSearch$totalResults.$t / feed.openSearch$itemsPerPage.$t);
            maxPage = (maxPage > 4) ? 4 : maxPage;
            var pages = [];
            for (var i = 0; i < maxPage; i++) {
                var page = {
                    label: i + 1,
                    start: feed.openSearch$itemsPerPage.$t * i + 1,
                    current: (feed.openSearch$itemsPerPage.$t * i + 1 == feed.openSearch$startIndex.$t)
                };
                pages.push(page);
            }
            this.pages = pages;
            this.items = items;
        }
    }, o);
    
    $.extend(this, new $.venice.OpenApi());
    $.extend(this, o);
};

/** Google */

$.venice.OpenApi.Google = function(o){
    /* Sign-up for an AJAX Search API Key : http://code.google.com/apis/ajaxsearch/signup.html */
    o = $.extend({
        apiKey: {
            "local.kakao.com:3000": "ABQIAAAAV3Qp42Npdh2w8yRX5CK37xQ8vx6TvAELDMjADyjzwOUD3AWNDRTEQZCqhozgkgqq07EXoAdIr202KA",
            "local.kakao.com": "ABQIAAAAV3Qp42Npdh2w8yRX5CK37xQ8vx6TvAELDMjADyjzwOUD3AWNDRTEQZCqhozgkgqq07EXoAdIr202KA",
            "alpha.kakao.com": "ABQIAAAAV3Qp42Npdh2w8yRX5CK37xScFCreYIXoj7rrrBLZ57wna-NqXhSn8uw-k7Wy-KBlw3Fj-DFrJGAV7g",
            "alpha-agit.kakao.com": "ABQIAAAAn_IKuzmlDg2spn2yWuVqZhQks9eWfzMHpqVF2Qt2Ed2FEVPHCBQFXjPciO94qh6XE9KroiWHbB8R7g",
            "alpha-admin.kakao.com": "ABQIAAAAAKFiNqk2h_cOO8BJX5IkYRTmH2QxrI9GRVjKgAZ3Pqy7vYiVihTvsHNjULGvy6780GBO_u7sNxedLA",
            "beta.kakao.com": "ABQIAAAAV3Qp42Npdh2w8yRX5CK37xTUlTuBWNZ0eR-f6tYWzB39b2irIxQfjko6htPm6_H3nUXIF036bDoncQ",
            "beta-agit.kakao.com": "ABQIAAAAn_IKuzmlDg2spn2yWuVqZhQlW_lAub1GBp2XvYrFZBgg0K1JTBSPjpFfcafHGW0GsM2wb5ORqCgwsA",
            "www.kakao.com": "ABQIAAAAV3Qp42Npdh2w8yRX5CK37xRiMhQx_dhTDrksyqKlWwWTRrdvUhQzNGHe4ikLbT8lb2eXmovOWDReTA",
            "agit.kakao.com": "ABQIAAAAn_IKuzmlDg2spn2yWuVqZhRsBFXYzow4vrx_ohBZwFesbdiUsBRWPPtFZqYItdrNQeXo-lvgjDb2eQ",
            "admin.kakao.com": "ABQIAAAAAKFiNqk2h_cOO8BJX5IkYRRPQ0ODciy1UxizfsxNmdqBZJMQIBR9kn7n-uDaQUl267qwY3x_Abbf_Q"
        },
        getURL: function(){
            var apiKeyByHost = this.apiKey[location.host];
            var url = "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=large&hl=ko&key=" + apiKeyByHost + "&start=" + this.start + "&callback=" + this.callback + "&context=google";
            // Image Search Specific Arguments
            url += "&safe=moderate";
            // http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_image
            url += "&q=" + encodeURIComponent(this.keywords);
            return url;
        },
        noResultMessageTemplate: '<center style="margin:20px 0 20px 0"><strong>이미지가 검색되지 않았습니다. 다른 키워드로 이미지를 검색해보세요.</strong><center>',
        itemTemplate: '<td class="openapi_search_result" valign="top" align="center" _result_id="<%= i %>">' +
        '<div class="thumbnail image" _result_id="<%= i %>">' +
        '<table style="border-collapse:collapse;"><tr><td valign="bottom" align="center" style="width:109px;height:109px;">' +
        '<img src="<%= item.thumbnail %>" <% if (item.tbsize.width > 109 && (item.tbsize.width / item.tbsize.height >= 1.0) ) { %>width="109"<% } else if (item.tbsize.height > 109 && (item.tbsize.height / item.tbsize.height >= 1.0)) { %>height="120"<% } %> />' +
        '</td></tr></table>' +
        '<span style="position:absolute;bottom:1px;right:1px;cursor:pointer;" class="zoom"><img src="/images/widget/openapi/btn_preview.gif" _result_id="<%= i %>"/></span>' +
        '</div>' +
        '<div class="api_desc"><%= item.title %></div>' +
        '</td>',
        itemsTemplate: '<table class="openapi_search_results openapi_search_image_results">' +
        '<% for(var i = 0; i< item_html.length; i++) { %>' +
        '<% if(i % 4 == 0) { %><tr><% } %>' +
        '<%= item_html[i] %>' +
        '<% if(i % 4 == 3) { %></tr><% } %>' +
        '<% } %>' +
        '</tr></table>',
        previewTemplate: '<div><img class="width500_image" width="<%= item.viewWidth %>" style="cursor:pointer;" src="<%= item.url %>"/></div>',
        btn_template: '<div class="resource_add_btn" style="display:none;"><a href="#"><img src="/images/widget/openapi/btn_imgupload.png" /></a></div>',
        parse: function(json){
            var items = [];
            var entry = (json) ? json.results : [];
            
            if (!entry) {
                this.items = [];
                return items;
            }
            
            var self = this;
            
            for (var i = 0; i < entry.length; i++) {
                var item = {
                    type: 'image',
                    vendor: 'google'
                };
                item.id = entry[i].imageId;
                item.url = entry[i].unescapedUrl;
                item.title = entry[i].contentNoFormatting;
                item.contextUrl = entry[i].originalContextUrl;
                item.thumbnail = entry[i].tbUrl;
                item.size = {
                    width: entry[i].width,
                    height: entry[i].height
                };
                item.viewWidth = item.size.width > 500 ? 500 : item.size.width;
                item.tbsize = {
                    width: entry[i].tbWidth,
                    height: entry[i].tbHeight
                };
                items.push(item);
            }
            
            var pages = [];
            if (json.cursor) {
                pages = json.cursor.pages || [];
                for (var x = 0; x < pages.length; x++) {
                    pages[x].current = (x == json.cursor.currentPageIndex);
                }
                
                // 마지막 페이지
                if (this.supportsNoImage && pages.length - 1 == json.cursor.currentPageIndex) {
                    var item = {
                        type: 'image',
                        vendor: 'wisia'
                    };
                    item.title = "";
                    item.url = "http://" + location.host + "/images/noimage_140140.gif"
                    item.thumbnail = item.url;
                    // 8개의 이미지가 있을 경우, 마지막 이미지를 빈 이미지로 대체한다.
                    if (entry.length == 8) {
                        items[7] = item;
                    }
                    else {
                        items.push(item);
                    }
                }
            }
            
            this.pages = pages;
            this.items = items;
        },
        start: 0,
        supportsNoImage: true
    }, o);
    $.extend(this, new $.venice.OpenApi());
    $.extend(this, o);
};


onYouTubePlayerReady = function(playerid){
    var id = setInterval(function(){
        var player = document.getElementById(playerid);
        if (player == null || player.getPlayerState() == 1) {
            clearInterval(id);
            return;
        }
        if (player.getPlayerState() == 5) {
            player.playVideo();
            clearInterval(id);
        }
    }, 300);
};


// dependency: jquery.js, swfobject.js
$.venice.webcam = function(o) {
  o = $.extend({
  }, o);
  $.extend(this, o);
}

$.extend($.venice.webcam.prototype, {
  flashVersion : "9.0.124",
  ie : !!navigator.userAgent.match(/MSIE/),
  protocol: location.protocol.match(/https/i) ? 'https' : 'http',
  swf_url : "/flash/webcam.swf?v=1.2",
  callback : null,
  upload_url : '/upload',
  show_upload_button: false,
  taken: false,
  fallback : function() {
    return "Adobe Flash Player 9.0 이상을 설치하셔야 합니다."
    + "<a href='http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash' target='_blank'>설치하기<a>";
  },
  
  get_html : function() {
    if (!swfobject.hasFlashPlayerVersion(this.flashVersion)) {
      return this.fallback();
    }

    this.ht_id = 'webcam_i_' + (new Date()).getTime();
    var _name = 'on_' + this.ht_id;
    var html = '';
    var flashvars = 'callback=' + (_name) + 
                    '&upload_url=' + this.upload_url +
                    '&show_upload_button=' + (this.show_upload_button ? '1' : '0');
		
    if (this.ie) {
      html += ['<object type="application/x-shockwave-flash" data="', this.swf_url, '" width="320" height="240" id="', this.ht_id, '" align="middle">',
               '<param name="allowScriptAccess" value="always" />',
               '<param name="allowFullScreen" value="false" />',
               '<param name="movie" value="', this.swf_url, '" />',
               '<param name="loop" value="false" />',
               '<param name="menu" value="false" />',
               '<param name="quality" value="best" />',
               '<param name="bgcolor" value="#ffffff" />',
               '<param name="wmode" value="transparent"/>',
               '<param name="flashvars" value="', flashvars, '"/>',
               '</object>'].join('');
    } else {
      html += ['<embed id="', this.ht_id, '" src="', this.swf_url, '" ',
               'width="320" height="240" ',
               'align="middle" ',
               'allowScriptAccess="always" ',
               'allowFullScreen="false" ',
               'loop="false" ',
               'menu="false" ',
               'quality="best" ',
               'bgcolor="#ffffff" ',
               'wmode="transparent" ',
               'name="webcam_movie" ',
               'type="application/x-shockwave-flash" ',
               'pluginspage="http://www.macromedia.com/go/getflashplayer" ',
               'flashvars="', flashvars, '" />'].join('');
    }

    var _self = this;

    window[_name] = function (code, message) {
      _self.hook(code, message);
    }
		
    return html;
  },
  
  remove: function() {
    if (swfobject) {
      swfobject.removeSWF(this.ht_id);
    }
  },
  
  upload: function() {
    try {
      this.getMovieElement().upload();
    } catch (e) { alert(e.description); }
  },
  
  getMovieElement: function() {
    return document.getElementById(this.ht_id);
  },
  
  fire_hook: function(code,message) {
		// fire hook callback, passing optional value to it
		if (this.callback) {
			if (typeof(this.callback) == 'function') {
				// callback is function reference, call directly
				this.callback(code,message);
			}
			else if (typeof(this.callback) == 'array') {
				// callback is PHP-style object instance method
				this.callback[0][this.callback[1]](code,message);
			}
			else if (window[this.callback]) {
				// callback is global function name
				window[ this.callback ](code,message);
			}
			return true;
		}
		return false; 
	},
  
  hook : function(code, message) {
    this.fire_hook(code, message)
    if (code == 'uploaded') {
    } else if (code == 'uploading' ) {
    } else if (code == 'fail' ) {
    }
  }
});


$.venice.AttachmentWidget = function(element, options){
  options = $.extend({
    groupId: null,
    attachmentTypes: []
  }, options)
  $.extend(this, options);
  
  if (this.groupId == null || this.attachmentTypes.length == 0) 
    return;
  
  this.element = element;
  this.initialize();
};

$.extend($.venice.AttachmentWidget.prototype, {
  initialize: function(){
    var self = this;
    
    this.element.innerHTML = '\
      <div class="attachment_widget">\
				<div class="attachment_menubar"></div>\
        <table class="attachment_form" style="display:none">\
          <tr>\
            <td class="tl"></td>\
            <td class="t"></td>\
            <td class="tr"></td>\
          </tr>\
          <tr>\
            <td class="l"></td>\
            <td class="c">\
              <div class="attachment_form_head"><img class="title"/><img src="/images/widget/attachment/btn_close.png?v=2" alt="닫기" class="close"><div class="clear"></div></div>\
            </td>\
            <td class="r"></td>\
          </tr>\
          <tr>\
            <td class="bl"></td>\
            <td class="b"></td>\
            <td class="br"></td>\
          </tr>\
        </table>\
			</div>';
    
    this.$menubar = $(this.element).find('.attachment_widget .attachment_menubar');
    this.$form = $(this.element).find('.attachment_widget .attachment_form');
    this.$bodyContainer = this.$form.find('.c');
    this.$pfForm = $(this.element).parents('.comment_form');
    
    $(this.attachmentTypes).each(function(i){
      self.addAttachmentMenu(this, i == self.attachmentTypes.length - 1);
    });
    
    this.$form.find('.close').click(function(){
			$(this).parents('.attachment_form').attr('id', null);
      self.reset();
    });
  },
  addAttachmentMenu: function(attachmentType, last){
    var namespace = attachmentType.namespace;
    var name = attachmentType.name;
    var self = this;
    var $menu = $('<div class="attachment_menu ' + (last ? 'last' : '') + '" type="' + namespace + '"><img title="' + name + '" src="/images/widget/attachment/' + namespace + '/icon.png" onmouseover="this.src=\'/images/widget/attachment/' + namespace + '/icon_hover.png\'" onmouseout="this.src=\'/images/widget/attachment/' + namespace + '/icon.png\'" /></div>');
    
    this.$menubar.append($menu);
    
    $menu.click(function(){
      self.$menubar.hide();
      
      var type = $(this).attr('type');
      self.type = type;
      
      var html = self.$pfForm.data(type);
      
      self.showAttachmentForm(type);
      
      // render form before render attached template
      // because of binding jquery in attached template
      self.$form.show();
      
      if (html) {
        var $formBody = self.$form.find('.attachment_form_body[type="' + type + '"]');
        if ($formBody.length == 0) {
          var $body = $('<div class="attachment_form_body" type="' + type + '">' + html + '</div>');
          self.$bodyContainer.append($body);
          $formBody = $body;
        }
        $formBody.html(html);
      }
      else {
        $.ajax({
          url: '/g/' + self.groupId + '/' + type + '/attach',
          async: false,
          dataType: 'html',
          success: function(html){
            var $body = $('<div class="attachment_form_body" type="' + type + '">' + html + '</div>');
            self.$bodyContainer.append($body);
            self.$pfForm.data(type, html);
            
            var $attachForm = $body.find("form");
            if ($attachForm.length > 0) {
              self.focusToFirstInput($attachForm[0]);
            }
          },
          error: function(request){
            // TODO: handle error
          }
        });
      }
      
    });
  },
  focusToFirstInput: function(form){
    // focus default input field and move caret to end
    var inputs = $(form).find('input[type=text]:last');
    if (inputs.length > 0) {
      var input = inputs[0];
      if (input.createTextRange) {
        var range = input.createTextRange();
        range.collapse(false);
        range.select();
      }
      else 
        if (input.setSelectionRange) {
          input.focus();
          var length = input.value.length;
          if (input.value.length > 0) {
            input.setSelectionRange(length, length);
          }
        }
    }
  },
  showAttachmentForm: function(type){
    this.$form.find(".attachment_form_body").hide();
    
    if (type) {
      $(this.element).addClass('expanded');
      this.$form.find('.attachment_form_head .title').attr('src', '/images/widget/attachment/' + type + '/txt.png');
      this.$form.find(".attachment_form_body[type='" + type + "']").show();
    }
  },
  hideAttachmentForm: function(){
    this.showAttachmentForm(false);
    this.$form.hide();
    $(this.element).removeClass('expanded');
  },
  serialize: function(){
    try {
      var data = this.$form.find('form').serialize();
      if (data == '') 
        return null;
      return '_attachment_type=' + this.type + '&_attachment_data=' + encodeURIComponent(data);
    } 
    catch (e) {
      return null;
    }
  },
  reset: function(){
    $(this.$form.find('.attachment_form_body:visible form')).each(function(){
      // IE6에서는 $.isFunction(this.reset) 이 false라 try~catch 처리함.
      try {
        this.reset();
      } catch (ignore) {}
    });
    this.hideAttachmentForm();
    this.$menubar.show();
  }
});

$.fn.extend({
  attachmentWidget: function(o){
    return this.each(function(){
      if (this.attachmentWidget == undefined) {
        this.attachmentWidget = new $.venice.AttachmentWidget(this, o);
      }
    });
  },
  resetAttachmentWidget: function(){
    return this.each(function(){
      if (this.attachmentWidget) 
        this.attachmentWidget.reset();
    });
  },
  serializeWidget: function(){
    if (this[0].attachmentWidget) 
      return this[0].attachmentWidget.serialize();
    return null;
  }
});


$.venice.commentsWidget = function(el, o) {
  var options = $.extend({
    attachmentTypes: [],
    viewMode: 'thread',
    showAlert: true
  }, o);
  
  $.extend(this, options);
  
  if (this.viewMode == 'time') {
    this.viewMode = 'mtime';
    $.cookie("LVO", 'mtime', {
      expires: 365,
      path: '/'
    });
  }
  
  if (!this.baseUrl) return;
  
  this.widgetId = "commentsWidgetID_" + $.venice.commentsWidget.widgetId++;
  $(el).find('.comments_widget').attr('id', this.widgetId);
  this.$widget = $(el).find("#" + this.widgetId);
  this.groupId = this.$widget.attr('group_id');
  this.contextApplicationId = this.$widget.attr('context_application_id');
  this.contextContentId = this.$widget.attr('context_content_id');
  this.theme = this.$widget.attr('theme');
  this.depthLimit = this.$widget.attr('depth_limit');
  if (this.depthLimit == '') this.depthLimit = 1; // default
  this.listUrl = this.baseUrl + '?group_id=' + this.groupId + '&context_application_id=' + this.contextApplicationId + '&context_content_id=' + this.contextContentId;
  this.createUrl = this.baseUrl;
  
  var self = this;
  
  this.$widget.find('.input_panel .attachments').attachmentWidget({
    attachmentTypes: this.attachmentTypes,
    groupId: this.groupId,
    form: this.$widget.find('.input_panel .attachments').parents('form')[0]
  });
  
  var $textarea = this.$widget.find('.input_panel textarea');
  var _lineHeight = parseInt($textarea.css('lineHeight')) || 16; // IE6에서는 값을 얻을 수 없다.
  var _minHeight = parseInt($textarea.css('minHeight'));
  // initialize comment form
	$textarea.val('');
  $textarea.autogrow({
    maxHeight: 320,
    lineHeight: _lineHeight,
    pad: _minHeight - _lineHeight
  }).defaultTextInput('새글을 입력합니다.', {
    defaultStyle: 'color:#808080;',
    afterFocus: function(el) {
      if (self.attachmentTypes.length > 0) {
        $(el).parents('.input_panel').removeClass('collapsed');
      }
    }
  }).keypress(function(evt) {
    var keyCode = evt.which || evt.keyCode;
    if (keyCode == 13) {
      if (evt.shiftKey) {
        $(this).parents('form').submit();
        $(this).blur();
        return false;
      } else { return true; }
    }
    return true;
  });
  
  this.$widget.find('.input_panel form').submit(function() {
    self.createComment(this);
    return false;
  });
  
  this.$widget.find('.input_panel .bn_helper .cancel_reply').live('click', function() {
    var $inputPanel = $(this).parents('.input_panel');
    self.resetForm($inputPanel.find('form')[0]);
    if ($inputPanel.hasClass('reply_input')) {
      $inputPanel.hide();
    }
  });
  
  this.$widget.find('.list .reply_button').live('click', function() {
    var commentId = $(this).attr('comment_id');
    // self.$widget.find('.pseudo_reply_panel[comment_id!=' + commentId + ']').show();
    self.$widget.find('.pseudo_reply_panel[comment_id=' + commentId + ']').hide();
    self.showReplyForm(commentId);
    return false;
  });
  
  // delete button
  this.$widget.find('.list .comment_panel .delete_button').live('click', function() {
    self.deleteComment($(this).attr('comment_id'));
  });
  
  // attachment 더보기
//  this.$widget.find('.list .comment_panel .wall_contents .more_loadable').live('click', function() {
//    if ($(this).data('loaded')) return false;
//    
//    $(this).data('loaded', true);
//    var $comment = $($(this).parents('.comment_panel')[0]);
//    var $self = $(this);
//    $.get('/comments/' + $comment.attr('comment_id') + '/more_contents?group_id=' + self.groupId + '&application_id=' + $comment.attr('application_id'), function(data) {
//      $($self.parents('.wall_contents')[0]).find('.contents').append(data);
//    });
//    
//    return false;
//  });  
  
  // initialize tab
  this.$widget.find('.tab .item[sort_by=' + this.viewMode + ']').addClass('selected');
  this.$widget.find('.tab .item').click(function() {
    if ($(this).attr('sort_by') == self.viewMode) return;
    self.changeViewMode($(this).attr('sort_by'));
  });
  
  // initialize more
  this.$widget.find('.more').click(function() {
    self.getMore(null, null);
  });
  
  // initialize scroll
  this.hasMore = this.$widget.find('.more').length > 0;
  this.isScrolling = false;
  this.more_panel = this.$widget.find('.more');
  this.container = window;
  
  this._scrollListener = function() {
    self.fill();
  }
  $(this.container).bind('scroll', this._scrollListener);
  
  if (this.showAlert) {
    $(document.body).bind('Juggernaut.updateWall', function(event) {
      self.updateArticle(event.userId, event.extra);
    });
  }
  
  $('.nwall').live('click', function() {
    var article_id = $(this).find('.strong').attr('article_id');
    var first_thread_id = $(this).find('.strong').attr('first_thread_id');
    if (self.viewMode == 'mtime') {
      self.forceViewFullThreads(first_thread_id, article_id);
    } else {
      self.forceGotoComment(article_id);
    }
  });
  
  this.redrawSeparator();
  
  if (this.jump_to_wall_id) {
    this.forceGotoComment(this.jump_to_wall_id);
  }
  
  this.$widget.find('.list .comment_panel .message').emoji();
};

$.extend($.venice.commentsWidget, {
  widgetId: 1
});

$.extend($.venice.commentsWidget.prototype, {
  validateForm: function(form) {
    if ($(form.message).val().trim() == '') {
      alert('내용을 입력해 주세요.');
      return false;
    }
    return true;
  },
  
  createComment: function(form) {
    form.sort_by.value = this.viewMode;
    
    var self = this;
		var $current_attachment = $(form).find('.attachments .attachment_form_body:visible'); 
    if ($current_attachment.length == 0) {
      if (!this.validateForm(form)) return false;
      this._createComment(form, null);
			return;
		}
		
		var $attachment_widget = $(form).find('.attachment_widget'); 
		
    $attachment_widget.unbind('venice.attachment.complete');
    $attachment_widget.bind('venice.attachment.complete', function(e) {
      var attachmentData = $(form).find('.attachments').serializeWidget();
      self._createComment(form, attachmentData);
    });
		
		try {
			var v = $current_attachment.find('form')[0].onsubmit();
  		if (!v) return false;
			
      $attachment_widget.trigger($.Event('venice.attachment.complete'));
			return;
	  } catch (e) {}
	},
  _createComment: function(form, attachmentData) {
    var parentId = form.parent_id.value;
    var formData = $(form).serialize();
    if (attachmentData != null) formData += '&' + attachmentData;
    
    if (this.locked) return false;
    this.locked = true;
    
    var self = this;
    $.ajax({
      type: "POST",
      url: this.createUrl,
      data: formData,
      dataType: "text",
      success: function(data) {
        self.updateList(data, parentId, false);
        self.resetForm(form);
        self.locked = false;
      },
      error: function(request) {
        alert(request.responseText);
        self.locked = false;
      },
      complete: function() {
        self.locked = false;
      }
    });
  },
  updateArticle: function(user_id, data) {
    if ($('#' + this.widgetId).length == 0) return;
    
    if (parseInt(this.userId) == parseInt(user_id)) return;
    var parent_id = parseInt(data.article.parent_id);
    this.updateList(data.thread, parent_id, true);
    this.showNotification(data);
  },
  showNotification: function(data) {
    var message = '<span class="strong" article_id="' + data.article.id + '" first_thread_id="' + data.article.first_thread_id + '">' + data.article.author_nickname + '</span>님의<br>' +
    (data.article.parent_id > 0 ? '덧글' : '새글') +
    '이 등록되었습니다.';
    $.jGrowl(message, {
      position: 'bottom-right',
      life: 15000,
      theme: 'nwall',
      closer: false,
      pool: 3
    });
  },
  updateList: function(data, parentId, is_notify) {
		var $data = $(data);
    if (this.depthLimit == 0) {
      $data.appendTo(this.$widget.children('.list')).filter('.comment_panel').animate({
        backgroundColor: '#ff6'
      }, 2000).animate({
        backgroundColor: '#fff'
      }, 2000);
      
      if (!is_notify) this.$widget.find('.reply_input').hide();
    } else if ((this.viewMode == 'thread' && parentId > 0) || (this.viewMode == 'mtime' && parentId > 0 && !is_notify)) {
      var $panel = this.$widget.children('.list').find('.comment_panel[comment_id=' + parentId + ']');
      if ($panel.length == 0) return;
      var depth = parseInt($panel.attr('depth'));
      while (true) {
        $panel = $panel.next();
        if ($panel.hasClass('input_panel')) continue;
        if (!$panel.hasClass('comment_panel') || depth >= parseInt($panel.attr('depth'))) break;
      }
      
      if ($panel.hasClass('pseudo_reply_panel')) {
        $data.insertBefore($panel).filter('.comment_panel').animate({
          backgroundColor: '#ff6'
        }, 2000).animate({
          backgroundColor: '#fff'
        }, 2000);
        $panel.show();
      } else if ($panel.hasClass('comment_panel')) {
        $data.insertBefore($panel).filter('.comment_panel').animate({
          backgroundColor: '#ff6'
        }, 2000).animate({
          backgroundColor: '#fff'
        }, 2000);
      } else {
        $data.insertAfter(this.$widget.children('.list').find('.comment_panel:last')).animate({
          backgroundColor: '#ff6'
        }, 2000).animate({
          backgroundColor: '#fff'
        }, 2000);
      }
      
      if (!is_notify) this.$widget.find('.reply_input').hide();
      
    } else if (this.viewMode == 'mtime' && parentId > 0 && is_notify) {
      var $panel = this.$widget.children('.list').find('.comment_panel[comment_id=' + parentId + ']');
      if ($panel.length == 0) return;
      var depth = parseInt($panel.attr('depth'));
      while (true) {
        $panel = $panel.next();
        if ($panel.hasClass('input_panel')) continue;
        if (!$panel.hasClass('comment_panel') || depth >= parseInt($panel.attr('depth'))) break;
      }
      
      if ($panel.hasClass('pseudo_reply_panel')) {
        $data.insertBefore($panel);
      } else if ($panel.hasClass('comment_panel')) {
        $data.insertBefore($panel);
      } else {
        $data.insertAfter(this.$widget.children('.list').find('.comment_panel:last'));
      }
      
      if (!is_notify) this.$widget.find('.reply_input').hide();
      
      var $threads = this.$widget.children('.list').children('[first_thread_id=' + parentId + ']');
      $threads.prependTo(this.$widget.children('.list'));
      var $last_thread = $threads.filter('.comment_panel:last');
      this.highlight($last_thread);
    } else {
      $data.prependTo(this.$widget.children('.list')).filter('.comment_panel').animate({
        backgroundColor: '#ff6'
      }, 2000).animate({
        backgroundColor: '#fff'
      }, 2000);
    }

    var $pseudoPanels = this.$widget.children('.list').children('.pseudo_reply_panel[first_thread_id=' + parentId + ']');
    if ($pseudoPanels.length > 1) $pseudoPanels.filter(':last').remove();  
    $data.pngFix({
			blankgif: '/images/blank.gif'
		});
    this.redrawSeparator();
    
    $data.filter('.comment_panel').find('.message').emoji();
  },
  showReplyForm: function(commentId) {
    var $commentPanel = this.$widget.children('.list').find('.comment_panel[comment_id=' + commentId + ']');
    if ($commentPanel.length == 0) {
      alert('덧글을 달 수 없습니다.');
      return;
    }
    var author = $commentPanel.find('.author').text().trim();
    
    if (parseInt($commentPanel.attr('depth')) > 0) {
      commentId = $commentPanel.attr('parent_id');
      $t_commentPanel = this.$widget.children('.list').find('.comment_panel[comment_id=' + commentId + ']');
      if ($t_commentPanel.length > 0) $commentPanel = $t_commentPanel;
    }
    if ($commentPanel.length == 0) {
      alert('덧글을 달 수 없습니다.');
      return;
    }
    
    $inputPanel = this.$widget.find('.reply_input');
    this.resetForm($inputPanel.find('form')[0]);
    
		var oldCommentId = $inputPanel.find('form.comment_form')[0].parent_id.value;
		if (oldCommentId.match(/^\d+$/) && commentId != oldCommentId) {
    	this.$widget.find('.pseudo_reply_panel[comment_id=' + oldCommentId + ']').show();
		}

    if (this.depthLimit == 0) {
      $inputPanel.show().appendTo(this.$widget.children('.list'));
    } else {
      var depth = parseInt($commentPanel.attr('depth'));
      while (true) {
        var $trav = $commentPanel.next();
        if ($trav.hasClass('input_panel')) $trav = $trav.next();
        if (!$trav.hasClass('comment_panel') || depth >= parseInt($trav.attr('depth'))) break;
        $commentPanel = $trav;
      }
      
      $inputPanel.show().insertAfter($commentPanel);
    }
    
    if (!isScrolledIntoView($inputPanel)) {
      var top = minScrollPos($inputPanel);
      if (top) window.scrollTo(0, top);
    }
    
    if (this.depthLimit > 0) {
      $inputPanel.find('form input[name=parent_id]').val(commentId);
    }
    $inputPanel.find('.bn_helper .author').html(author);
    $inputPanel.find('.bn_helper').show();
    $inputPanel.find('textarea').changeDefaultText('덧글을 입력합니다').changeFocus();
  },
	hideReplyForm: function(commentId) {
		var reply_input = this.$widget.find('.reply_input');
		if (reply_input.find('.comment_form')[0].parent_id.value == commentId) {
			reply_input.hide();
		}
		this.$widget.find('.pseudo_reply_panel[comment_id=' + commentId + ']').remove();
	},
  deleteComment: function(commentId) {
    if (!confirm("정말 삭제하시겠습니까?")) return;
    
    var self = this;
    $.ajax({
      type: "POST",
      url: this.baseUrl + "/" + commentId,
      data: {
        sort_by: this.viewMode,
        theme: this.theme,
        _method: 'delete'
      },
      dataType: "json",
      success: function(json) {
				var $comment = self.$widget.children('.list').find('.comment_panel[comment_id=' + commentId + ']');
				if ($comment.attr('parent_id') == 0) {
					self.hideReplyForm(commentId);
				}
        if (json.html != null) {
          self.$widget.children('.list').find('.comment_panel[comment_id=' + commentId + ']').replaceWith(json.html);
        } else if (json.parent_id != null) {
          self.removeChildren(json.parent_id);
          self.$widget.children('.list').find('.comment_panel[comment_id=' + json.parent_id + ']').remove();
        }
        self.redrawSeparator();
      }
    });
  },
  removeChildren: function(parentId) {
    var self = this;
    var $children = this.$widget.children('.list').find('.comment_panel[parent_id=' + parentId + ']');
    $children.each(function(i) {
      self.removeChildren($(this).attr('comment_id'));
    });
    $children.remove();
    
  },
  changeViewMode: function(viewMode) {
    var self = this;
    this.viewMode = viewMode;
    
    this.$widget.find('.tab .item').removeClass('selected');
    this.$widget.find('.tab .item[sort_by=' + this.viewMode + ']').addClass('selected');
    
    $.get(this.listUrl, {
      sort_by: this.viewMode,
      theme: this.theme
    }, function(data) {
      $.cookie("LVO", self.viewMode, {
        expires: 365,
        path: '/'
      });
      self.$widget.find('.list').html(data);
      if (self.$widget.find('.no_more_comments').length > 0) {
        self.$widget.find('.more').hide();
        self.hasMore = false;
      } else {
        self.$widget.find('.more').show();
        self.hasMore = true;
      }
      
      self.redrawSeparator();
    });
  },
  getMore: function(to_id, callback_func) {
    if (this.isScrolling) return false;
    
    this.isScrolling = true;
    this.$widget.find('.more .loading').show();
    var self = this;
    var lastId = this.$widget.find('.list .comment_panel:last').attr('comment_id');
    
    $.get(this.listUrl, {
      sort_by: this.viewMode,
      last_id: lastId,
      to_id: to_id,
      theme: this.theme
    }, function(data) {
      var $data = $(data);
      self.$widget.children('.list').append($data);
      if (self.$widget.find('.no_more_comments').length > 0) {
        self.$widget.find('.more').hide();
        self.hasMore = false;
      }
      self.$widget.find('.more .loading').hide();
      
      self.redrawSeparator();
      self.isScrolling = false;
      if (typeof callback_func === 'function') {
        callback_func();
      }
      
      $data.find('.message').emoji();
    });
  },
  resetForm: function(form) {
    var $inputPanel = $(form).parents('.input_panel');
    
    form.reset();
    $(form).find('textarea').resetDefaultTextInput();
    $(form).find('.uploaded_contents').html('<div class="clear"></div>').hide();
    $(form).find('.attachments').resetAttachmentWidget();
    
    if (!$inputPanel.hasClass('reply_input')) {
      //            $(form).find('input[name=parent_id]').val('');
      $(form).find('textarea').changeFocus();
    } else {
      var $parent = $inputPanel.prev('.comment_panel[parent_id="0"]');
      var parentId = $parent.attr('comment_id');
      var $children = $parent.nextAll('.comment_panel[parent_id="' + parentId + '"]');
      var $pseudoPanel = $parent.next('.pseudo_reply_panel'); 
      ($children.length > 0) ? $pseudoPanel.show() : $pseudoPanel.hide();
    }
    $inputPanel.find('.bn_helper').hide();
    $inputPanel.removeClass('no_bottom_border');
  },
  redrawSeparator: function() {
    this.$widget.children('.list').find('.comment_panel').addClass('tborder');
    this.$widget.children('.list').find('.comment_panel:first').removeClass('tborder');
  },
  fill: function() {
    this.hasMore = this.more_panel.is(':visible');
    if (this.hasMore && isScrolledIntoView(this.more_panel)) {
      if (!this.isScrolling) {
        this.getMore(null, null);
      }
    }
  },
  forceViewFullThreads: function(first_thread_id, last_thread_id) {
    if (this.viewMode != 'mtime') return;
    
    var self = this;
    
    if (self.$widget.children('.list').find('.comment_panel[comment_id=' + first_thread_id + ']').length > 0 &&
    self.$widget.children('.list').find('.comment_panel[comment_id=' + last_thread_id + ']').length > 0) {
      var $last_thread = self.$widget.children('.list').find('.comment_panel[comment_id=' + last_thread_id + ']');
      this.highlight($last_thread);
      return;
    }
    
    $.get(this.listUrl, {
      sort_by: 'mtime',
      from_id: first_thread_id,
      to_id: last_thread_id,
      theme: this.theme
    }, function(data) {
      self.$widget.children('.list').find('.comment_panel[first_thread_id=' + first_thread_id + ']').remove();
      self.$widget.children('.list').prepend(data);
      var $last_thread = self.$widget.children('.list').find('.comment_panel[comment_id=' + last_thread_id + ']');
      self.highlight($last_thread);
      
      self.redrawSeparator();
    });
  },
  forceGotoComment: function(to_id) {
    var self = this;
    var $a = $('#comment_panel_' + to_id);
    if ($a.length > 0) {
      this.highlight($a);
    } else {
      self.getMore(to_id, function() {
        self.highlight($('#comment_panel_' + to_id));
      });
    }
  },
  highlight: function(panel) {
    var $panel = $(panel);
    if ($panel.length == 0) return;
    
    $panel.animate({
      backgroundColor: '#ff6'
    }, 2000).animate({
      backgroundColor: '#fff'
    }, 2000);
    
    if (!isScrolledIntoView($panel)) {
      window.scrollTo(0, minScrollPos($panel));
    }
  }
});

$.fn.extend({
  commentsWidget: function(options) {
    return this.each(function() {
      this.commentsWidget = new $.venice.commentsWidget(this, options);
    });
  }
});


/*
 * Dependency: wisia-ui(increment/decrement)
 */

$.fn.extend({
  quickfilter: function(Data, options) {
    options = $.extend({
      data: Data,
      delay:20,
      winFirefoxFix: $.browser.mozilla && /win/i.test(navigator.userAgent) ? true : false
    }, options);
    
    return this.each(function() {
      new $.venice.quickFilter(this, options);
    });
  }
});

$.venice.quickFilter = function(element, options) {
  $.extend(this, options);

  $(element).keyup(function() {
    filterData($(element).val(), options.data);  
  }).change(function() {
    filterData($(element).val(), options.data);  
  }).focus(function(){
    if(options.winFirefoxFix) startCheckChange();
  }).blur(function() {
    if(options.winFirefoxFix) stopCheckChange();
  });
  
  var changeCheckTimeout = null;
  function startCheckChange() {
    if($(element).val().length >= 1) filterData($(element).val(), options.data);
    clearTimeout(changeCheckTimeout);
    changeCheckTimeout = setTimeout(startCheckChange, options.delay);
  }

  function stopCheckChange() {
    clearTimeout(changeCheckTimeout);
  }
  
  var self = this;
  
  function filterData(q, data) {
    var result_set = []
    if (data.length == 0 || q.length == 0) {
      options.changeList(null, q);
      return;
    }     
    
    var csub = [];
    for (var i=0;i < data.length;i++) {
      if (typeof data[i] === "object") {
        if (self.match(data[i].keywords, q)) result_set.push(data[i]);
      } else {
        if (self.match(data[i], q)) result_set.push(i);
      }
    }
    options.changeList(result_set, q);
    
  }
};

$.extend($.venice.quickFilter.prototype, {
  match: function(s, sub) {
    if (!s || !sub) return false;
    
    s = s.toLowerCase();
    sub = sub.toLowerCase();
    
    var last = sub.substr(sub.length - 1, 1);
    if ((last.charCodeAt(0) - '가'.charCodeAt(0)) % 28 == 0) { // kor
      var pre = sub.substr(0, sub.length - 1);
      var tail = pre + String.fromCharCode(last.charCodeAt(0) + 27);
      var idx = 0;
      while ((idx = s.indexOf(pre, idx)) >= 0) {
        var part = s.substr(idx++, sub.length);
        return (sub <= part && part <= tail);
      }
      return false;
    } else {
      return (s.indexOf(sub) >= 0);
    }
  }
});



$.fn.emoji = function() {
	return this.each(function(){
		$(this).html(
			$(this).html().replace(/([\ue001-\ue537])/g, $.fn.emoji.replacer)
		);
	});
};

$.fn.emoji.replacer = function (str, p1) {
	return p1.charCodeAt(0).toString(16).toUpperCase().replace(
		/^([\da-f]+)$/i,
		'<img src="/images/emoji/emoji-$1.png" alt="" style="vertical-align:bottom" />'
	);
};


/** 
 * flowplayer.js 3.2.0. The Flowplayer API
 * 
 * Copyright 2009 Flowplayer Oy
 * 
 * This file is part of Flowplayer.
 * 
 * Flowplayer is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Flowplayer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Flowplayer.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Date: 2010-05-03 20:23:59 +0000 (Mon, 03 May 2010)
 * Revision: 468 
 */
(function() {
 
/* 
  FEATURES 
  --------
  - $f() and flowplayer() functions 
  - handling multiple instances 
  - Flowplayer programming API 
  - Flowplayer event model  
  - player loading / unloading  
  - jQuery support
*/ 
 

/*jslint glovar: true, browser: true */
/*global flowplayer, $f */

// {{{ private utility methods
  
  function log(args) {
    console.log("$f.fireEvent", [].slice.call(args)); 
  }

    
  // thanks: http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
  function clone(obj) { 
    if (!obj || typeof obj != 'object') { return obj; }   
    var temp = new obj.constructor(); 
    for (var key in obj) {  
      if (obj.hasOwnProperty(key)) {
        temp[key] = clone(obj[key]);
      }
    }   
    return temp;
  }

  // stripped from jQuery, thanks John Resig 
  function each(obj, fn) {
    if (!obj) { return; }
    
    var name, i = 0, length = obj.length;
  
    // object
    if (length === undefined) {
      for (name in obj) {
        if (fn.call(obj[name], name, obj[name]) === false) { break; }
      }
      
    // array
    } else {
      for (var value = obj[0];
        i < length && fn.call( value, i, value ) !== false; value = obj[++i]) {       
      }
    }
  
    return obj;
  }

  
  // convenience
  function el(id) {
    return document.getElementById(id);   
  } 

  
  // used extensively. a very simple implementation. 
  function extend(to, from, skipFuncs) {
    if (typeof from != 'object') { return to; }
    
    if (to && from) {     
      each(from, function(name, value) {
        if (!skipFuncs || typeof value != 'function') {
          to[name] = value;   
        }
      });
    }
    
    return to;
  }
  
  // var arr = select("elem.className"); 
  function select(query) {
    var index = query.indexOf("."); 
    if (index != -1) {
      var tag = query.slice(0, index) || "*";
      var klass = query.slice(index + 1, query.length);
      var els = [];
      each(document.getElementsByTagName(tag), function() {
        if (this.className && this.className.indexOf(klass) != -1) {
          els.push(this);   
        }
      });
      return els;
    }
  }
  
  // fix event inconsistencies across browsers
  function stopEvent(e) {
    e = e || window.event;
    
    if (e.preventDefault) {
      e.stopPropagation();
      e.preventDefault();
      
    } else {
      e.returnValue = false;  
      e.cancelBubble = true;
    } 
    return false;
  }

  // push an event listener into existing array of listeners
  function bind(to, evt, fn) {
    to[evt] = to[evt] || [];
    to[evt].push(fn);   
  }
  
  
  // generates an unique id
   function makeId() {
      return "_" + ("" + Math.random()).slice(2, 10);   
   }
  
//}}} 
  

// {{{ Clip

  var Clip = function(json, index, player) {
    
    // private variables
    var self = this,
       cuepoints = {},
       listeners = {};
       
    self.index = index;
    
    // instance variables
    if (typeof json == 'string') {
      json = {url:json};  
    }
  
    extend(this, json, true); 
    
    // event handling 
    each(("Begin*,Start,Pause*,Resume*,Seek*,Stop*,Finish*,LastSecond,Update,BufferFull,BufferEmpty,BufferStop").split(","),
      function() {
      
      var evt = "on" + this;
        
      // before event
      if (evt.indexOf("*") != -1) {
        evt = evt.slice(0, evt.length -1); 
        var before = "onBefore" + evt.slice(2); 
        
        self[before] = function(fn) {
          bind(listeners, before, fn);
          return self;
        };        
      }  
      
      self[evt] = function(fn) {
        bind(listeners, evt, fn);
        return self;
      };
      
      
      // set common clip event listeners to player level
      if (index == -1) {
        if (self[before]) {
          player[before] = self[before];    
        }       
        if (self[evt])  {
          player[evt] = self[evt];    
        }
      }
      
    });       
    
    extend(this, { 
       
      onCuepoint: function(points, fn) {    
        
        // embedded cuepoints
        if (arguments.length == 1) {
          cuepoints.embedded = [null, points];
          return self;
        }
        
        if (typeof points == 'number') {
          points = [points];  
        }
        
        var fnId = makeId();  
        cuepoints[fnId] = [points, fn]; 
        
        if (player.isLoaded()) {
          player._api().fp_addCuepoints(points, index, fnId); 
        }  
        
        return self;
      },
      
      update: function(json) {
        extend(self, json);

        if (player.isLoaded()) {
          player._api().fp_updateClip(json, index); 
        }
        var conf = player.getConfig(); 
        var clip = (index == -1) ? conf.clip : conf.playlist[index];
        extend(clip, json, true);
      },
      
      
      // internal event for performing clip tasks. should be made private someday
      _fireEvent: function(evt, arg1, arg2, target) {         
        if (evt == 'onLoad') { 
          each(cuepoints, function(key, val) {
            if (val[0]) {
              player._api().fp_addCuepoints(val[0], index, key);    
            }
          }); 
          return false;
        }
        
        // target clip we are working against
        target = target || self;  
        
        if (evt == 'onCuepoint') {
          var fn = cuepoints[arg1];
          if (fn) {
            return fn[1].call(player, target, arg2);
          }
        }  

        // 1. clip properties, 2-3. metadata, 4. updates, 5. resumes from nested clip
        if (arg1 && "onBeforeBegin,onMetaData,onStart,onUpdate,onResume".indexOf(evt) != -1) {          
          // update clip properties
          extend(target, arg1);         
          
          if (arg1.metaData) {
            if (!target.duration) {
              target.duration = arg1.metaData.duration;   
            } else {
              target.fullDuration = arg1.metaData.duration; 
            }           
          }
        }         
        

        var ret = true;
        each(listeners[evt], function() {
          ret = this.call(player, target, arg1, arg2);    
        }); 
        return ret;       
      }     
      
    });
    
    
    // get cuepoints from config
    if (json.onCuepoint) {
      var arg = json.onCuepoint;
      self.onCuepoint.apply(self, typeof arg == 'function' ? [arg] : arg);
      delete json.onCuepoint;
    } 
    
    // get other events
    each(json, function(key, val) {
      
      if (typeof val == 'function') {
        bind(listeners, key, val);
        delete json[key];
      }
      
    });

    
    // setup common clip event callbacks for Player object too (shortcuts)
    if (index == -1) {
      player.onCuepoint = this.onCuepoint;  
    }

  };

//}}}


// {{{ Plugin
    
  var Plugin = function(name, json, player, fn) {
  
    var self = this,
       listeners = {},
       hasMethods = false;
  
    if (fn) {
      extend(listeners, fn);  
    }   
    
    // custom callback functions in configuration
    each(json, function(key, val) {
      if (typeof val == 'function') {
        listeners[key] = val;
        delete json[key]; 
      }
    });  
    
    // core plugin methods    
    extend(this, {
  
      // speed and fn are optional
      animate: function(props, speed, fn) { 
        if (!props) {
          return self;  
        }
        
        if (typeof speed == 'function') { 
          fn = speed; 
          speed = 500;
        }
        
        if (typeof props == 'string') {
          var key = props;
          props = {};
          props[key] = speed;
          speed = 500; 
        }
        
        if (fn) {
          var fnId = makeId();
          listeners[fnId] = fn;
        }
    
        if (speed === undefined) { speed = 500; }
        json = player._api().fp_animate(name, props, speed, fnId);  
        return self;
      },
      
      css: function(props, val) {
        if (val !== undefined) {
          var css = {};
          css[props] = val;
          props = css;          
        }
        json = player._api().fp_css(name, props);
        extend(self, json);
        return self;
      },
      
      show: function() {
        this.display = 'block';
        player._api().fp_showPlugin(name);
        return self;
      },
      
      hide: function() {
        this.display = 'none';
        player._api().fp_hidePlugin(name);
        return self;
      },
      
      // toggle between visible / hidden state
      toggle: function() {
        this.display = player._api().fp_togglePlugin(name);
        return self;
      },      
      
      fadeTo: function(o, speed, fn) {
        
        if (typeof speed == 'function') { 
          fn = speed; 
          speed = 500;
        }
        
        if (fn) {
          var fnId = makeId();
          listeners[fnId] = fn;
        }       
        this.display = player._api().fp_fadeTo(name, o, speed, fnId);
        this.opacity = o;
        return self;
      },
      
      fadeIn: function(speed, fn) { 
        return self.fadeTo(1, speed, fn);       
      },
  
      fadeOut: function(speed, fn) {
        return self.fadeTo(0, speed, fn); 
      },
      
      getName: function() {
        return name;  
      },
      
      getPlayer: function() {
        return player;  
      },
      
      // internal method. should be made private some day
         _fireEvent: function(evt, arg, arg2) {
        
            // update plugins properties & methods
            if (evt == 'onUpdate') {
               var json = player._api().fp_getPlugin(name); 
          if (!json) { return;  }         
          
               extend(self, json);
               delete self.methods;
          
               if (!hasMethods) {
                  each(json.methods, function() {
                     var method = "" + this;       
              
                     self[method] = function() {
                        var a = [].slice.call(arguments);
                        var ret = player._api().fp_invoke(name, method, a); 
                        return ret === 'undefined' || ret === undefined ? self : ret;
                     };
                  });
                  hasMethods = true;         
               }
            }
            
            // plugin callbacks
            var fn = listeners[evt];

        if (fn) {
          fn.apply(self, arg);
          
          // "one-shot" callback
          if (evt.slice(0, 1) == "_") {
            delete listeners[evt];  
          } 
            }
            
            return self;
         }
      
    });

  };


//}}}


function Player(wrapper, params, conf) {   
  
  // private variables (+ arguments)
  var self = this, 
    api = null, 
    isUnloading = false,
    html, 
    commonClip, 
    playlist = [], 
    plugins = {},
    listeners = {},
    playerId,
    apiId,
    
    // n'th player on the page
    playerIndex,
    
    // active clip's index number
    activeIndex,
    
    swfHeight,
    wrapperHeight;  

  
// {{{ public methods 
  
  extend(self, {
      
    id: function() {
      return playerId;  
    }, 
    
    isLoaded: function() {
      return (api !== null && ! isUnloading); 
    },
    
    getParent: function() {
      return wrapper; 
    },
    
    hide: function(all) {
      if (all) { wrapper.style.height = "0px"; }
      if (self.isLoaded()) { api.style.height = "0px"; } 
      return self;
    },

    show: function() {
      wrapper.style.height = wrapperHeight + "px";
      if (self.isLoaded()) { api.style.height = swfHeight + "px"; }
      return self;
    }, 
          
    isHidden: function() {
      return self.isLoaded() && parseInt(api.style.height, 10) === 0;
    },
    
    load: function(fn) { 

      if (!self.isLoaded() && self._fireEvent("onBeforeLoad") !== false) {
        
        var onPlayersUnloaded = function() {
          html = wrapper.innerHTML;       
        
          // do not use splash as alternate content for flashembed
          if (html && !flashembed.isSupported(params.version)) {
            wrapper.innerHTML = "";         
          }         
        
          // install Flash object inside given container
          flashembed(wrapper, params, {config: conf});
        
          // onLoad listener given as argument
          if (fn) {
            fn.cached = true;
            bind(listeners, "onLoad", fn);  
          }
        };
        
        
        // unload all instances
        var unloadedPlayersNb = 0;
        each(players, function()  {
          this.unload(function(wasUnloaded) {
            if ( ++unloadedPlayersNb == players.length ) {
              onPlayersUnloaded();
            }
          });   
        });
      }
      
      return self;  
    },
    
    unload: function(fn) {
      
      
      // if we are fullscreen on safari, we can't unload as it would crash the PluginHost, sorry
      if (this.isFullscreen() && /WebKit/i.test(navigator.userAgent)) {
        if ( fn ) { fn(false); }
        return self;
      }
      
      
      // unload only if in splash state
      if (html.replace(/\s/g,'') !== '') {
        
        if (self._fireEvent("onBeforeUnload") === false) {
          if ( fn ) { fn(false); }
          return self;
        } 
        
        isUnloading = true;
        // try closing
        try {
          if (api) { 
            api.fp_close();
            
            // fire unload only when API is present
            self._fireEvent("onUnload");
          }       
        } catch (error) {}        
        
        var clean = function() {
          api = null;       
          wrapper.innerHTML = html;
          isUnloading = false;
          
          if ( fn ) { fn(true); }
        };
        
        setTimeout(clean, 50);      
      } 
      else if ( fn ) { fn(false); }
      
      return self;
    
    },
    
    getClip: function(index) {
      if (index === undefined) {
        index = activeIndex;  
      }
      return playlist[index];
    },
    
    
    getCommonClip: function() {
      return commonClip;  
    },    
    
    getPlaylist: function() {
      return playlist; 
    },
    
      getPlugin: function(name) {  
         var plugin = plugins[name];
         
      // create plugin if nessessary
         if (!plugin && self.isLoaded()) {
        var json = self._api().fp_getPlugin(name);
        if (json) {
          plugin = new Plugin(name, json, self);
          plugins[name] = plugin;             
        } 
         }        
         return plugin; 
      },
    
    getScreen: function() { 
      return self.getPlugin("screen");
    }, 
    
    getControls: function() { 
      return self.getPlugin("controls")._fireEvent("onUpdate");
    }, 
    
    // 3.2
    getLogo: function() {
      try {
        return self.getPlugin("logo")._fireEvent("onUpdate");
      } catch (ignored) {}
    },
    
    // 3.2
    getPlay: function() {
      return self.getPlugin("play")._fireEvent("onUpdate");
    },
    

    getConfig: function(copy) { 
      return copy ? clone(conf) : conf;
    },
    
    getFlashParams: function() { 
      return params;
    },    
    
    loadPlugin: function(name, url, props, fn) { 

      // properties not supplied      
      if (typeof props == 'function') { 
        fn = props; 
        props = {};
      } 
      
      // if fn not given, make a fake id so that plugin's onUpdate get's fired
      var fnId = fn ? makeId() : "_"; 
      self._api().fp_loadPlugin(name, url, props, fnId); 
      
      // create new plugin
      var arg = {};
      arg[fnId] = fn;
      var p = new Plugin(name, null, self, arg);
      plugins[name] = p;
      return p;     
    },
    
    
    getState: function() {
      return self.isLoaded() ? api.fp_getState() : -1;
    },
    
    // "lazy" play
    play: function(clip, instream) {
      
      var p = function() {
        if (clip !== undefined) {
          self._api().fp_play(clip, instream);
        } else {
          self._api().fp_play();  
        }
      };
      
      if (self.isLoaded()) {
        p();  
      } else if ( isUnloading ) {
        setTimeout(function() { 
          self.play(clip, instream); 
        }, 50);
        
      } else {
        self.load(function() { 
          p();
        });
      }
      
      return self;
    },
    
    getVersion: function() {
      var js = "flowplayer.js 3.2.0";
      if (self.isLoaded()) {
        var ver = api.fp_getVersion();
        ver.push(js);
        return ver;
      }
      return js; 
    },
    
    _api: function() {
      if (!self.isLoaded()) {
        throw "Flowplayer " +self.id()+ " not loaded when calling an API method";
      }
      return api;       
    },
    
    setClip: function(clip) {
      self.setPlaylist([clip]);
      return self;
    },
    
    getIndex: function() {
      return playerIndex; 
    }
    
  }); 
  
  
  // event handlers
  each(("Click*,Load*,Unload*,Keypress*,Volume*,Mute*,Unmute*,PlaylistReplace,ClipAdd,Fullscreen*,FullscreenExit,Error,MouseOver,MouseOut").split(","),
    function() {     
      var name = "on" + this;
      
      // before event
      if (name.indexOf("*") != -1) {
        name = name.slice(0, name.length -1); 
        var name2 = "onBefore" + name.slice(2);
        self[name2] = function(fn) {
          bind(listeners, name2, fn); 
          return self;
        };            
      }
      
      // normal event
      self[name] = function(fn) {
        bind(listeners, name, fn);  
        return self;
      };       
    }
  ); 
  
  
  // core API methods
  each(("pause,resume,mute,unmute,stop,toggle,seek,getStatus,getVolume,setVolume,getTime,isPaused,isPlaying,startBuffering,stopBuffering,isFullscreen,toggleFullscreen,reset,close,setPlaylist,addClip,playFeed,setKeyboardShortcutsEnabled,isKeyboardShortcutsEnabled").split(","),    
    function() {     
      var name = this;
      
      self[name] = function(a1, a2) {
        if (!self.isLoaded()) { return self; }
        var ret = null;
        
        // two arguments
        if (a1 !== undefined && a2 !== undefined) { 
          ret = api["fp_" + name](a1, a2);
          
        } else { 
          ret = (a1 === undefined) ? api["fp_" + name]() : api["fp_" + name](a1);
        }
        
        return ret === 'undefined' || ret === undefined ? self : ret;
      };       
    }
  );    
  
//}}}


// {{{ public method: _fireEvent
    
  self._fireEvent = function(a) {   
    
    if (typeof a == 'string') { a = [a]; }
    
    var evt = a[0], arg0 = a[1], arg1 = a[2], arg2 = a[3], i = 0;     
    
    if (conf.debug) { log(a); }       
    
    // internal onLoad
    if (!self.isLoaded() && evt == 'onLoad' && arg0 == 'player') {            
      
      api = api || el(apiId); 
      swfHeight = api.clientHeight;
      
      each(playlist, function() {
        this._fireEvent("onLoad");    
      });
      
      each(plugins, function(name, p) {
        p._fireEvent("onUpdate");   
      }); 
      
      commonClip._fireEvent("onLoad");  
    }
    
    // other onLoad events are skipped
    if (evt == 'onLoad' && arg0 != 'player') { return; }
    
    
    // "normalize" error handling
    if (evt == 'onError') { 
      if (typeof arg0 == 'string' || (typeof arg0 == 'number' && typeof arg1 == 'number'))  {
        arg0 = arg1;
        arg1 = arg2;
      }      
    }
    
    
      if (evt == 'onContextMenu') {
         each(conf.contextMenu[arg0], function(key, fn)  {
            fn.call(self);
         });
         return;
      }

    if (evt == 'onPluginEvent') { 
      var name = arg0.name || arg0;
      var p = plugins[name];

      if (p) {
        p._fireEvent("onUpdate", arg0);
        p._fireEvent(arg1, a.slice(3));   
      }
      return;
    }   

    // replace whole playlist
    if (evt == 'onPlaylistReplace') {
      playlist = [];
      var index = 0;
      each(arg0, function() {
        playlist.push(new Clip(this, index++, self));
      });   
    }
    
    // insert new clip to the playlist. arg0 = clip, arg1 = index 
    if (evt == 'onClipAdd') {
      
      // instream clip additions are ignored at this point
      if (arg0.isInStream) { return; }
      
      // add new clip into playlist     
      arg0 = new Clip(arg0, arg1, self);
      playlist.splice(arg1, 0, arg0);
      
      // increment index variable for the rest of the clips on playlist 
      for (i = arg1 + 1; i < playlist.length; i++) {
        playlist[i].index++;  
      }
    }
    
    
    var ret = true;
    
    // clip event
    if (typeof arg0 == 'number' && arg0 < playlist.length) {
      
      activeIndex = arg0;
      var clip = playlist[arg0];      
      
      if (clip) {
        ret = clip._fireEvent(evt, arg1, arg2); 
      } 
      
      if (!clip || ret !== false) {

        // clip argument is given for common clip, because it behaves as the target
        ret = commonClip._fireEvent(evt, arg1, arg2, clip); 
      }  
    }
    
    
    // trigger player event
    each(listeners[evt], function() {
      ret = this.call(self, arg0, arg1);    
      
      // remove cached entry
      if (this.cached) {
        listeners[evt].splice(i, 1);  
      }
      
      // break loop
      if (ret === false) { return false;   }
      i++;
      
    }); 

    return ret;
  };

//}}}
 

// {{{ init
  
   function init() {
    
    // replace previous installation 
    if ($f(wrapper)) {
      $f(wrapper).getParent().innerHTML = ""; 
      playerIndex = $f(wrapper).getIndex();
      players[playerIndex] = self;
      
    // register this player into global array of instances
    } else {
      players.push(self);
      playerIndex = players.length -1;
    }
    
    wrapperHeight = parseInt(wrapper.style.height, 10) || wrapper.clientHeight;     
    
    // playerId 
    playerId = wrapper.id || "fp" + makeId();
    apiId = params.id || playerId + "_api";
    params.id = apiId;
    conf.playerId = playerId;
    

    // plain url is given as config
    if (typeof conf == 'string') {
      conf = {clip:{url:conf}}; 
    } 
    
    if (typeof conf.clip == 'string') {
      conf.clip = {url: conf.clip}; 
    }
    
    // common clip is always there
    conf.clip = conf.clip || {};  
    
    
    // wrapper href as common clip's url
    if (wrapper.getAttribute("href", 2) && !conf.clip.url) { 
      conf.clip.url = wrapper.getAttribute("href", 2);      
    } 
    
    commonClip = new Clip(conf.clip, -1, self); 
    
    // playlist
    conf.playlist = conf.playlist || [conf.clip]; 
    
    var index = 0;
    
    each(conf.playlist, function() {
      
      var clip = this;      
      
      /* sometimes clip is given as array. this is not accepted. */
      if (typeof clip == 'object' && clip.length) {
        clip = {url: "" + clip};  
      }     
      
      // populate common clip properties to each clip
      each(conf.clip, function(key, val) {
        if (val !== undefined && clip[key] === undefined && typeof val != 'function') {
          clip[key] = val;  
        }
      }); 
      
      // modify playlist in configuration
      conf.playlist[index] = clip;      
      
      // populate playlist array
      clip = new Clip(clip, index, self);
      playlist.push(clip);            
      index++;      
    });       
    
    // event listeners
    each(conf, function(key, val) {
      if (typeof val == 'function') {
        
        // common clip event
        if (commonClip[key]) {
          commonClip[key](val);
          
        // player event
        } else {
          bind(listeners, key, val);  
        }       
        
        // no need to supply for the Flash component
        delete conf[key]; 
      }
    });    
    
    
    // plugins
    each(conf.plugins, function(name, val) {
      if (val) {
        plugins[name] = new Plugin(name, val, self);
      }
    });
    
    
    // setup controlbar plugin if not explicitly defined
    if (!conf.plugins || conf.plugins.controls === undefined) {
      plugins.controls = new Plugin("controls", null, self);  
    }
    
    // setup canvas as plugin
    plugins.canvas = new Plugin("canvas", null, self);    
    
    // click function
    function doClick(e) { 
      if (!self.isLoaded() && self._fireEvent("onBeforeClick") !== false) {
        self.load();    
      } 
      return stopEvent(e);          
    }
    
    // defer loading upon click
    html = wrapper.innerHTML;
    if (html.replace(/\s/g, '') !== '') {  
      
      if (wrapper.addEventListener) {
        wrapper.addEventListener("click", doClick, false);  
        
      } else if (wrapper.attachEvent) {
        wrapper.attachEvent("onclick", doClick);  
      }
      
    // player is loaded upon page load 
    } else {
      
      // prevent default action from wrapper. (fixes safari problems)
      if (wrapper.addEventListener) {
        wrapper.addEventListener("click", stopEvent, false);  
      }
      // load player
      self.load();
    }
  }

  // possibly defer initialization until DOM get's loaded
  if (typeof wrapper == 'string') { 
    var node = el(wrapper); 
      
    if (!node) {
      throw "Flowplayer cannot access element: " + wrapper; 
    } else {
      wrapper = node; 
      init();         
    } 
    
  // we have a DOM element so page is already loaded
  } else {    
    init();
  }
  
  
//}}}


}


// {{{ flowplayer() & statics 

// container for player instances
var players = [];


// this object is returned when multiple player's are requested 
function Iterator(arr) {
  
  this.length = arr.length;
  
  this.each = function(fn)  {
    each(arr, fn);  
  };
  
  this.size = function() {
    return arr.length;  
  };  
}

// these two variables are the only global variables
window.flowplayer = window.$f = function() {

  var instance = null;
  var arg = arguments[0]; 
  
  // $f()
  if (!arguments.length) {
    each(players, function() {
      if (this.isLoaded())  {
        instance = this;  
        return false;
      }
    });
    
    return instance || players[0];
  } 
  
  if (arguments.length == 1) {
    
    // $f(index);
    if (typeof arg == 'number') { 
      return players[arg];  
  
      
    // $f(wrapper || 'containerId' || '*');
    } else {
      
      // $f("*");
      if (arg == '*') {
        return new Iterator(players); 
      }
      
      // $f(wrapper || 'containerId');
      each(players, function() {
        if (this.id() == arg.id || this.id() == arg || this.getParent() == arg)  {
          instance = this;  
          return false;
        }
      });
      
      return instance;          
    }
  }       

  // instance builder 
  if (arguments.length > 1) {   

    // flashembed parameters
    var params = arguments[1],
       conf = (arguments.length == 3) ? arguments[2] : {};
          
    
    if (typeof params == 'string') {
      params = {src: params}; 
    } 
    
    params = extend({
      bgcolor: "#000000",
      version: [9, 0],
      expressInstall: "http://static.flowplayer.org/swf/expressinstall.swf",
      cachebusting: true
      
    }, params);   
    
    if (typeof arg == 'string') {
      
      // select arg by classname
      if (arg.indexOf(".") != -1) {
        var instances = [];
        
        each(select(arg), function() { 
          instances.push(new Player(this, clone(params), clone(conf)));     
        }); 
        
        return new Iterator(instances);
        
      // select node by id
      } else {    
        var node = el(arg);
        return new Player(node !== null ? node : arg, params, conf);    
      } 
      
      
    // arg is a DOM element
    } else if (arg) {
      return new Player(arg, params, conf);           
    }
    
  } 
  
  return null; 
};
  
extend(window.$f, {

  // called by Flash External Interface     
  fireEvent: function() {
    var a = [].slice.call(arguments);
    var p = $f(a[0]);   
    return p ? p._fireEvent(a.slice(1)) : null;
  },
  
  
  // create plugins by modifying Player's prototype
  addPlugin: function(name, fn) {
    Player.prototype[name] = fn;
    return $f;
  },
  
  // utility methods for plugin developers
  each: each,
  
  extend: extend
  
});

  
//}}}


//{{{ jQuery support

if (typeof jQuery == 'function') {
  
  jQuery.fn.flowplayer = function(params, conf) {  
    
    // select instances
    if (!arguments.length || typeof arguments[0] == 'number') {
      var arr = [];
      this.each(function()  {
        var p = $f(this);
        if (p) {
          arr.push(p);  
        }
      });
      return arguments.length ? arr[arguments[0]] : new Iterator(arr);
    }
    
    // create flowplayer instances
    return this.each(function() { 
      $f(this, clone(params), conf ? clone(conf) : {}); 
    }); 
    
  };
  
}

//}}}


})();
/**
 * @license 
 * jQuery Tools 3.2.0 / Flashembed - New wave Flash embedding
 * 
 * NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
 * 
 * http://flowplayer.org/tools/toolbox/flashembed.html
 *
 * Since : March 2008
 * Date  : @DATE 
 */ 
(function() {
    
  var IE = document.all,
     URL = 'http://www.adobe.com/go/getflashplayer',
     JQUERY = typeof jQuery == 'function', 
     RE = /(\d+)[^\d]+(\d+)[^\d]*(\d*)/,
     GLOBAL_OPTS = { 
      // very common opts
      width: '100%',
      height: '100%',   
      id: "_" + ("" + Math.random()).slice(9),
      
      // flashembed defaults
      allowfullscreen: true,
      allowscriptaccess: 'always',
      quality: 'high',  
      
      // flashembed specific options
      version: [3, 0],
      onFail: null,
      expressInstall: null, 
      w3c: false,
      cachebusting: false          
  };
  
  // version 9 bugfix: (http://blog.deconcept.com/2006/07/28/swfobject-143-released/)
  if (window.attachEvent) {
    window.attachEvent("onbeforeunload", function() {
      __flash_unloadHandler = function() {};
      __flash_savedUnloadHandler = function() {};
    });
  }
  
  // simple extend
  function extend(to, from) {
    if (from) {
      for (key in from) {
        if (from.hasOwnProperty(key)) {
          to[key] = from[key];
        }
      }
    } 
    return to;
  } 

  // used by asString method  
  function map(arr, func) {
    var newArr = []; 
    for (var i in arr) {
      if (arr.hasOwnProperty(i)) {
        newArr[i] = func(arr[i]);
      }
    }
    return newArr;
  }

  window.flashembed = function(root, opts, conf) {
  
    // root must be found / loaded  
    if (typeof root == 'string') {
      root = document.getElementById(root.replace("#", ""));
    }
    
    // not found
    if (!root) { return; }
    
    if (typeof opts == 'string') {
      opts = {src: opts}; 
    }

    return new Flash(root, extend(extend({}, GLOBAL_OPTS), opts), conf); 
  };  
  
  // flashembed "static" API
  var f = extend(window.flashembed, {
    
    conf: GLOBAL_OPTS,
  
    getVersion: function()  {
      var ver;
      
      try {
        ver = navigator.plugins["Shockwave Flash"].description.slice(16); 
      } catch(e) {
        
        try  {
          var fo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");
          ver = fo && fo.GetVariable("$version");  
        } catch(err) {
            
        } 
      }
      
      ver = RE.exec(ver);
      return [ver[1], ver[3]];
    },
    
    asString: function(obj) { 

      if (obj === null || obj === undefined) { return null; }
      var type = typeof obj;
      if (type == 'object' && obj.push) { type = 'array'; }
      
      switch (type){  
        
        case 'string':
          obj = obj.replace(new RegExp('(["\\\\])', 'g'), '\\$1');
          
          // flash does not handle %- characters well. transforms "50%" to "50pct" (a dirty hack, I admit)
          obj = obj.replace(/^\s?(\d+\.?\d+)%/, "$1pct");
          return '"' +obj+ '"';
          
        case 'array':
          return '['+ map(obj, function(el) {
            return f.asString(el);
          }).join(',') +']'; 
          
        case 'function':
          return '"function()"';
          
        case 'object':
          var str = [];
          for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
              str.push('"'+prop+'":'+ f.asString(obj[prop]));
            }
          }
          return '{'+str.join(',')+'}';
      }
      
      // replace ' --> "  and remove spaces
      return String(obj).replace(/\s/g, " ").replace(/\'/g, "\"");
    },
    
    getHTML: function(opts, conf) {

      opts = extend({}, opts);
      
      /******* OBJECT tag and it's attributes *******/
      var html = '<object width="' + opts.width + 
        '" height="' + opts.height + 
        '" id="' + opts.id + '"' + 
        '" name="' + opts.id + '"';
      
      if (opts.cachebusting) {
        opts.src += ((opts.src.indexOf("?") != -1 ? "&" : "?") + Math.random());    
      }     
      
      if (opts.w3c || !IE) {
        html += ' data="' +opts.src+ '" type="application/x-shockwave-flash"';    
      } else {
        html += ' classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"';  
      }
      
      html += '>'; 
      
      /******* nested PARAM tags *******/
      if (opts.w3c || IE) {
        html += '<param name="movie" value="' +opts.src+ '" />';  
      } 
      
      // not allowed params
      opts.width = opts.height = opts.id = opts.w3c = opts.src = null;
      opts.onFail = opts.version = opts.expressInstall = null;
      
      for (var key in opts) {
        if (opts[key]) {
          html += '<param name="'+ key +'" value="'+ opts[key] +'" />';
        }
      } 
    
      /******* FLASHVARS *******/
      var vars = "";
      
      if (conf) {
        for (var k in conf) { 
          if (conf[k]) {
            var val = conf[k]; 
            vars += k +'='+ (/function|object/.test(typeof val) ? f.asString(val) : val) + '&';
          }
        }
        vars = vars.slice(0, -1);
        html += '<param name="flashvars" value=\'' + vars + '\' />';
      }
      
      html += "</object>";  
      
      return html;        
    },
    
    isSupported: function(ver) {
      return VERSION[0] > ver[0] || VERSION[0] == ver[0] && VERSION[1] >= ver[1];     
    }   
    
  });
  
  var VERSION = f.getVersion(); 
  
  function Flash(root, opts, conf) {  
                                                  
    // version is ok
    if (f.isSupported(opts.version)) {
      root.innerHTML = f.getHTML(opts, conf);
      
    // express install
    } else if (opts.expressInstall && f.isSupported([6, 65])) {
      root.innerHTML = f.getHTML(extend(opts, {src: opts.expressInstall}), {
        MMredirectURL: location.href,
        MMplayerType: 'PlugIn',
        MMdoctitle: document.title
      }); 
      
    } else {
      
      // fail #2.1 custom content inside container
      if (!root.innerHTML.replace(/\s/g, '')) {
        root.innerHTML = 
          "<h2>Flash version " + opts.version + " or greater is required</h2>" + 
          "<h3>" + 
            (VERSION[0] > 0 ? "Your version is " + VERSION : "You have no flash plugin installed") +
          "</h3>" + 
          
          (root.tagName == 'A' ? "<p>Click here to download latest version</p>" : 
            "<p>Download latest version from <a href='" + URL + "'>here</a></p>");
          
        if (root.tagName == 'A') {  
          root.onclick = function() {
            location.href = URL;
          };
        }       
      }
      
      // onFail
      if (opts.onFail) {
        var ret = opts.onFail.call(this);
        if (typeof ret == 'string') { root.innerHTML = ret; } 
      }     
    }
    
    // http://flowplayer.org/forum/8/18186#post-18593
    if (IE) {
      window[opts.id] = document.getElementById(opts.id);
    } 
    
    // API methods for callback
    extend(this, {
        
      getRoot: function() {
        return root;  
      },
      
      getOptions: function() {
        return opts;  
      },

      
      getConf: function() {
        return conf;  
      }, 
      
      getApi: function() {
        return root.firstChild; 
      }
      
    }); 
  }
  
  // setup jquery support
  if (JQUERY) {
    
    // tools version number
    jQuery.tools = jQuery.tools || {version: '3.2.0'};
    
    jQuery.tools.flashembed = {  
      conf: GLOBAL_OPTS
    };  
    
    jQuery.fn.flashembed = function(opts, conf) {   
      return this.each(function() { 
        $(this).data("flashembed", flashembed(this, opts, conf));
      });
    }; 
  } 
  
})();

