nully.ui.ColorTable = Class.create({
  initialize: function(element, options) {
    this.element = $(element);
    
    Object.extend(this, Object.extend({
      borderSpacing: '4px'
    }, options));

    var colortable = [
      ['800000', '8B4513', '006400', '2F4F4F', '000080', '4B0082', '800080', '000000'],
      ['FF0000', 'DAA520', '6B8E23', '708090', '0000CD', '483D8B', 'C71585', '696969'],
      ['FF4500', 'FFA500', '808000', '4682B4', '1E90FF', '9400D3', 'FF1493', 'A9A9A9'],
      ['FF6347', 'FFD700', '32CD32', '87CEEB', '00BFFF', '9370DB', 'FF69B4', 'DCDCDC'],
      ['FFDAB9', 'FFFFE0', '98FB98', 'E0FFFF', '87CEFA', 'E6E6FA', 'DDA0DD', 'FFFFFF']
    ];

    var table = this.element.append('table')
                            .addClassName('colortable')
                            .writeAttribute('cellspacing', 0)
                            .setStyle({borderSpacing: this.borderSpacing});
                
    // (IE) <tbody> 태그가 <tr> 태그 이쩐에 있어야 한다.
    var tbody = table.append('tbody');

    for (var i = 0; i < colortable.length; i++) {
      var tr = tbody.append('tr');
      for (var j = 0; j < colortable[i].length; j++) {
        var td = tr.append('td')
                 .writeAttribute('title', '#' + colortable[i][j])
                 .setStyle({backgroundColor: '#' + colortable[i][j]});
                 
        if (this.onclick) {
          td.observe('click', function(event, cell) {
            event.stop();
            table.select('td.selected').invoke('removeClassName', 'selected');
            cell.addClassName('selected');
            this.onclick(cell.style.backgroundColor);
          }.bindAsEventListener(this, td));
        }
      }
    }
  },
  markCurrentStyle: function(color) {
    this.element.select('td.selected').invoke('removeClassName', 'selected');
    this.element.select('td').each(function(cell) {
      if (cell.style.backgroundColor == color) {
        cell.addClassName('selected');
      }
    });
  }
});
/*
nully.ui.Slider = Class.create({
  initialize: function(wrap, options) {
    // this.wrap = $(id);
    
    var track  = $(wrap).select('.track')[0];
    var handle = $(wrap).select('.handle')[0];
    
    track.setStyle({width: track.getStyle('width')});
    handle.setStyle({width: handle.getStyle('width')});
    
    this.slider = new Control.Slider(handle, track, options);
  },
  draw: function(menu) {
    var wrap = $(this.prefix + menu);
    wrap.addClassName('slider');
    
    var p = document.createElement('p');
    p.setAttribute('id', this.prefix + menu + '_status');
    p.appendChild(document.createTextNode('0'));
    
    wrap.appendChild(p);
    
    var track = $(document.createElement('div'));
    track.id = this.prefix + menu + '_track';
    track.setStyle({
      width     : '200px',
      height    : '9px',
      background: 'url(' + Notoo.url.images + 'slider/track_right.png) no-repeat scroll right top'
    });
    
    var left = $(document.createElement('div'));
    left.setStyle({
      position  : 'absolute',
      width     : '5px',
      height    : '9px',
      background: 'url(' + Notoo.url.images + 'slider/track_left.png) no-repeat scroll left top'
    });
    track.appendChild(left);
    
    var handle = $(document.createElement('div'));
    handle.setAttribute('id', this.prefix + menu + '_handle');
    handle.setStyle({
      width: '19px',
      height: '20px'
    });
    
    var img = document.createElement('img');
    img.setAttribute('src', Notoo.url.images + 'slider/handle.png');
    img.setAttribute('alt', '');
    img.style.cssFloat = 'left';
    
    handle.appendChild(img);
    track.appendChild(handle);
    
    wrap.appendChild(track);
  },
  setValue: function(value) {
    this.slider.setValue(value);
  }
});
*/
nully.ui.Tabs = Class.create({
  initialize: function(element, options) {
    this.element = $(element);
    if (!this.element) throw 'TabMenu creation failed. Element is not exist.';
    
    Object.extend(this, Object.extend({
      tabSelector: 'a',      // 탭으로 사용할 엘리먼트의 CSS selector
      activeClass: 'current', // 활성화 탭에 지정할 클래스 명
      activate   : true,     // 첫 페이지가 열린 채로 시작한다.
      position   : null,     // 'absolute' 일 경우 페이지를 탭 위치에 맞춰 보여준다.
      toggle     : false      // 열린 탭을 다시 클릭하면 닫히도록 한다.
    }, options));
    
    this.current = {tab: null, page: null};
    
    this.element.select(this.tabSelector).each(this.insert.bind(this));
  },
  active: function() {
    return this.current.tab != null;
  },
  show: function(tab) {
    if (this.current.tab != tab || this.toggle) {
      this.current.tab  && this.current.tab.removeClassName(this.activeClass);
      this.current.page && this.current.page.hide();
    }
    var page = this._getPage(tab);
    if (this.current.tab != tab && page) {
      this.current.tab  = tab.addClassName(this.activeClass);
      this.current.page = page;
      this._calcPosition();
      page.show();
    } else if (!page) {
      this.current.tab  = null;
      this.current.page = null;
    }
  },
  hide: function() {
    this.current.tab  && this.current.tab.removeClassName(this.activeClass);
    this.current.page && this.current.page.hide();
    this.current.tab  = null;
    this.current.page = null;
  },
  insert: function(tab) {
    tab.addClassName('tab');
    
    tab.observe('click', function(event) {
      event.stop();
      this.show(tab);
    }.bind(this));
    
    var page = this._getPage(tab);
    if (page) {
      page.hide();
      // 첫 페이지가 활성화된 채로 시작할 수 있다.
      if (this.activate && !this.active()) {
        this.show(tab);
      }
    }
  },
  remove: function(tab) {
    if (this.current.tab == tab) {
      this.hide();
    }
    tab.remove();
    var page = this._getPage(tab);
    page && page.remove();
    this._calcPosition();
  },
  _getPage: function(tab) {
    if (tab.getAttribute('href').match(/#(.+)$/)) {
      return $(RegExp.$1);
    }
    return null;
  },
  _calcPosition: function() {
    if (this.position == 'absolute' && this.current.tab && this.current.page) {
      var offsets = this.current.tab.cumulativeOffset();
      this.current.page.style.left = offsets.left + 'px';
      this.current.page.style.top  = offsets.top  + this.current.tab.getHeight() + 5 + 'px';
    }
  }
});

nully.ui.Logger = {
  message: function(kind, msg, options) {
    options = Object.extend({
      automatic: true
    }, options);
    
    if (!this.element) {
      this.element = $('logger') || $E('#logger').appendTo();
      this.contents = this.element.append();
      this.element.hide();
    }
    this.element.addClassName(kind);
    this.contents.update(msg);
    this.element.setStyle({left: ((document.body.clientWidth - this.element.getWidth()) / 2).toInt() + 'px'});
    this.element.slideDown();
    if (options.automatic) {
      this.element.slideUp({delay: 1, queue: 'end'});
    }
  }
};

nully.ui.Overlay = {
  initialize: function(options) {
    Object.extend(this, Object.extend({
      close: true
    }, options));

    this.overlay = $E('#overlay').appendTo().hide();
    this.wrap    = $E('#overlay_wrap').appendTo().hide();
    if (this.close) {
      this.wrap.append('a')
               .writeAttribute('href', '#')
               .addClassName('close')
               .update('닫기')
               .observe('click', function(event) {
                 event.stop();
                 this.hide();
               }.bind(this));
    }
  },
  show: function(element, options) {
    if (!this.overlay) {
      this.initialize(options);
    }

    // IE6 처리
    if (Prototype.Browser.IE && !window.XMLHttpRequest) {
      var viewport = document.viewport.getDimensions();
      this.overlay.setStyle({
        width : viewport.width + 'px',
        height: viewport.height + 'px'
      });
    }

    this.element = $(element);
    this.originalParent = this.element.parentNode;
    var viewports = document.viewport.getDimensions();
    var dimensions = this.element.getDimensions();

    this.wrap.appendChild(this.element);
    this.wrap.setStyle({
      left: ((viewports.width - dimensions.width) / 2).toInt() + 'px',
      top : ((viewports.height - dimensions.height) / 2).toInt() + 'px'
    });

    this.overlay.appear({duration: 0.5, to: 0.75, afterFinish: function(obj) {
      this.wrap.show();
      this.element.show();
    }.bind(this)});
  },
  hide: function() {
    this.wrap.hide();
    this.overlay.fade({duration: 0.5});
    this.originalParent.appendChild(this.element.hide());
  }
};

nully.ui.Graph = Class.create({
  initialize: function(wrap, options) {
    Object.extend(this, nully.Object.extend({
      graph: {width : 800, height: 250},
      margin: {
        top   : 0,
        left  : 25,
        bottom: 50,
        right : 25
      },
      grid: {
        x: 10,
        y: 10,
        attr: {
          'stroke': '#ccc',
          'stroke-width': 1
        }
      },
      max: {x: 100, y: 400},
      text: {
        attr: {
          'font-family': 'Georgia',
          'font-size': '12px'
        }
      },
      legend: {x: '레벨'}
    }, options));
    
    this.grid.width  = this.graph.width  / this.grid.x;
    this.grid.height = this.graph.height / this.grid.y;
    
    this.paper = Raphael(wrap, this.graph.width + this.margin.left + this.margin.right, this.graph.height + this.margin.top + this.margin.bottom);
    this.path = this.paper.path().attr({'fill': '#5af', 'fill-opacity': 0.5, 'stroke': '#39d', 'stroke-width': 2, 'stroke-linejoin': 'round'}).toFront();
    this.circles = [];
    
    this.drawAxis();
    this.drawGrid();
  },
  setGrid: function(options) {
    Object.extend(this.grid, Object.extend({
      x: 10,
      y: 10,
      attr: {
        'stroke': '#ccc',
        'stroke-width': 1
      }
    }, options));
  },
  drawAxis: function() {
    // Legend
    this.paper.text(this.margin.left + this.graph.width / 2, this.margin.top + this.graph.height + 30, this.legend.x)
              .attr(this.text.attr);

    // Label
    this.paper.text(this.margin.left + 0, this.margin.top + this.graph.height + 10, '0')
              .attr(this.text.attr);

    for (var i = 1; i <= this.grid.x; ++i) {
      this.paper.text(this.margin.left + this.grid.width * i, this.margin.top + this.graph.height + 10, (this.max.x / this.grid.x * i).toString())
                .attr(this.text.attr);
    }
  },
  drawGrid: function() {
    var width  = this.graph.width  / this.grid.x;
    var height = this.graph.height / this.grid.y;
    var path = [];
    for (var i = 0; i <= this.grid.x; ++i) {
      path = path.concat(['M', this.margin.left + this.grid.width * i, this.margin.top, 'V', this.margin.top + this.graph.height]);
    }
    for (var i = 0; i <= this.grid.y; ++i) {
      path = path.concat(['M', this.margin.left, this.margin.top + this.grid.height * i, 'H', this.margin.left + this.graph.width]);
    }
    this.paper.path().attr(this.grid.attr).attr({path: path}).toBack();
  },
  draw: function(data) {
    this.clear();

    var maxValue = (this.max.y || data.max()) * 1.1;
    var path = ['M', this.margin.left, this.margin.top + this.graph.height];

    for (var i = 0; i < data.length; ++i) {
      var x = this.margin.left + (this.graph.width / data.length) * (i + 1);
      var y = this.margin.top  + this.graph.height - this.graph.height * data[i] / maxValue;
      
      path = path.concat(['L', x, y]);
      
      if (data[i] > 0) {
        this.circles.push(this.paper.circle(x, y, 3).attr({fill: '#39d', stroke: '#5af'}));
      }
    }
    path = path.concat(['L', this.margin.left + this.graph.width, this.margin.top  + this.graph.height]);
    this.path.attr({path: path});
  },
  clear: function() {
    this.circles.invoke('remove');
    this.circles.clear();
  }
});

/**
 * 
 **/
nully.ui.ComboBox = Class.create({
  initialize: function(wrap, items, options) {
    Object.extend(this, Object.extend({
      defaultTitle: 'None Items'
    }, options));
    
    this.wrap = $(wrap);

    this.title = this.wrap.append('h4')
                          .observe('click', function(event) {
                            this.body.toggle();
                          }.bind(this));
    
    this.body = wrap.append().hide();
    
    this.body.append('p')
             .addClassName('commands')
             .append('a')
             .writeAttribute({href: '#'})
             .observe('click', function(event) {
               event.stop();
               this.body.hide();
             }.bind(this))
             .append('img')
             .writeAttribute({
               alt: '',
               src: '/images/close.gif'
             });
             
    var list = this.body.append('ul');

    items.each(function(item) {
      var li = list.append('li');
      this.drawItem();
      li.append('h4')
          .append('a')
          .writeAttribute({
            href: '#'
          })
          .update(study.collection_name)
          .observe('click', this.onSelect.bind(this));
      li.append('p')
          .update(study.words + '단어씩 ' + study.exclude);
      li.append('p')
          .update(study.game_name);
    }.bind(this));

    this.setTitle(items.length > 0 ? items[0] : this.defaultTitle);
    
    if (items.length > 0) {
      var default_study = items[0];
      this.title.innerHTML = default_study.collection_name;
      $('continue_study').setAttribute('href', '/study/' + default_study.id + '/step/1');
    }
  },
  getTitle: function(item) {
    return item.title;
  },
  createItem: function(item) {
    
  },
  onSelect: function(event) {
    event.stop();
    this.title.innerHTML = event.element().innerHTML;
    this.body.hide();
    $('continue_study').setAttribute('href', '/study/' + study.id + '/step/1');
  }
});

nully.ui.DefaultText = Class.create({
  initialize: function(id) {
    this.element = $(id);
    this.label   = this.element.previous('label');

    this.element.up().setStyle({position: 'relative'});

    this.label.setStyle({
      position: 'absolute',
      left: '0.5em',
      lineHeight: this.element.getHeight() + 'px'
    });
    
    if (this.element.defaultValue) {
      this.label.hide();
    }

    this.element.observe('focus', function(event) {
      this.label.hide();
    }.bind(this)).observe('blur', function(event) {
      if (this.element.value.length == 0) {
        this.label.show();
      }
    }.bind(this));
  }
});

nully.ui.Groups = Class.create({
  initialize: function(element, options) {
    this.element = $(element);
    
    Object.extend(this, Object.extend({
      selectedClass: 'selected',
      maxSelection: 1,
      selector: 'li>a',
      toggle: false
    }, options));
    
    this.element.select(this.selector).each(function(item) {
      this.insert(item);
    }.bind(this));
  },
  insert: function(item) {
    item.observe('click', this.click.bind(this));
  },
  remove: function(item) {
    item.remove();
  },
  getSelected: function() {
    return this.element.select(this.selector + '.' + this.selectedClass);
  },
  click: function(event) {
    event.stop();
    var element = event.findElement('a');
    
    if (element.hasClassName(this.selectedClass)) {
      if (this.toggle) {
        this.deselect(element);
      }
    } else {
      this.getSelected().each(function(item, index) {
        if (index + 1 >= this.maxSelection) {
          this.deselect(item)
        }
      }.bind(this));
      this.select(element);
    }
  },
  select: function(item) {
    item.addClassName(this.selectedClass);
  },
  deselect: function(item) {
    item.removeClassName(this.selectedClass);
  },
  deselectAll: function() {
    this.getSelected().each(function(item) {
      this.deselect(item);
    }.bind(this));
  }
});

nully.ui.Tabs2 = Class.create(nully.ui.Groups, {
  initialize: function($super, element, options) {
    $super(element, Object.extend({
      absolutize: false,
      startOpened: false
    }, options));
    
    if (this.startOpened) {
      this.select(this.element.select(this.selector)[0]);
    }
  },
  _getPage: function(tab, action) {
    if (tab.getAttribute('href').match(/#(.+)$/)) {
      var page = $(RegExp.$1);
      if (page) {
        if (this.absolutize && action == 'show') {
          var offset = tab.cumulativeOffset();
          page.setStyle({
            left: offset.left + 'px',
            top : offset.top + tab.getHeight() + 'px'
          });
        }
        page[action]();
      }
    }
  },
  insert: function($super, tab) {
    $super(tab);
    this._getPage(tab, 'hide');
  },
  select: function($super, tab) {
    $super(tab);
    this._getPage(tab, 'show');
  },
  deselect: function($super, tab) {
    $super(tab);
    this._getPage(tab, 'hide');
  }
});

