There are 16 previous versions of this script.
Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
// ==UserScript==
// @name NicoVideo Additional MyList
// @namespace http://castor.s26.xrea.com/
// @description Nicovideo mylist for local
// @include http://www.nicovideo.jp/watch/*
// @include http://www.nicovideo.jp/my/mylist*
// @version 1.0.1.20110605
// @author castor <castor.4bit@gmail.com>
// ==/UserScript==
(function() {
// Number of items to display per page
var NUM_DISPLAY_ITEMS = 100;
// button image
var IMAGE_BUTTON = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFsAAAAgCAMAAABKDfSpAAAAwFBMVEUIDQ0IDQ0OEREUGRkUGRkUGRkdIiIkKSkkKSkkKSktMjI1Ojo1Ojo1Ojo8QEBCR0dCR0dCR0dMUFBYXFxYXFxYXFxeYWFlaWllaWllaWlucnJ3enp3enp3enp+gYGChYWChYWChYWNkJCUlpaUlpaeoKClp6elp6elp6eusLC1urq1urq1urq+wMDIysrIysrIysrP0NDW19fW19fm5ubm5ubv8PD8/Pz8/PwQFKAoBAAAwH4koOD5IAEB1dSR0Huo5mniAAAEb0lEQVRIibVW7YrkKhAVBQUFAxE6kMAEIhtIoAVFrPd/s3vKpGd6dhfuj909MBM/j1WnyrIF/TsIosOpvw93MPchtfn70PIAt9P2/2HM7wa+Rn+etlY7Ek32c+YHVppj650+OQDeY1S/YD7bvOSblVfvax0g24u7JjOEQDmEAR0E4nHF42GGab4wwboxdHiTqtd232/OOS1g9NdksN+47dTK47zYfsCJ5UmBnuu6fjiTPuO+6eVu1dBodZnId2cmDM3a5Ht2/+LG9ICRdrZxXcfRG+OGHdyMZsx4Wx1p1/PrmPmgCu82G6zmE9EOeitArtSs5mDe3MbX7M4KT/uRT+wOdH58fCwsolL8t9OkzDBP47gTDf2UxZy0KFCf9qDmVddn+M4N/WvS4G5nD0aG7COV5/PZ+/OilYnEbQcbdKasLSyd9Mn/Cp1G2dT8fJ7HcSTm1i9uwNf2fJaBVmP7QY9HonICKx9FMWRKFuZDvAwTRymh8Y5Q7FqPMxuHjeUWjLn1Jze2tJjPmUL7gS4vyNCwPCtze960XRnWo1kR5a0xy8a+xANGHLPyE2fJQk2rN24zIwdb3Mk+C9u9HTTSmuoDLkNrW2jn5VB9bO8Vozil1XpbO4x8JYaJWnBv3F0GmktxoOcucoZWeJP6pPKNBiZH/kCGPXfEwt465bYtU9mm8nVmfucOLR105ees9WOuZSXkU6vTRT7ujrkb62HuIqEHTv0DhUmuFJU626dPx7smuDLjsf/4CL5FzeEH9f7h7fPjkqIboHRucSvthZrW1DY5Zs7pfOJaWoRmdO5bLGHJVSWMHllCM1qlruKAr5I+6Fc3v8nNGa/ldneWWgOmnXCrVzf3q+Rqe1arWwKdo034xNes9Op+0KvGI9HiMDKGUGkVGLLLfFJeJr7zc6KZx0H6zq3diUugqcBOR1EMmakbLdh/kruXIdmDuCALLTxqDzch8Mnpg1K+0+edG1cA9wvKtJtbdlFw04Nkuwcp5W132mIHcobPlSPFmbYNZ6ppZO5o5Ce3VFd27RCPueXNrZRwrerOTeds+9D5rvfIvltKyJODvNDLBO6qWakXt9/SFRnYRUVJLO/cUiW2RqorPze0pF0KHbAcOZ0nxdy61kixNOUza1Jpl592SxlhVtg6N9ySUgzQGx/FbmMeb581c4ELGBQRgQYOfFgoKU/aaQsBpbEMuXFp78MXt5+8Rv4zN/ZiP9rY7RKdmbaLWwjN1JgMKBhCmEYzL2VL1hWywPWoRCaDKrbywoubk1cxd98s8QRAM4u8jRJ5sYrOzbR+RxGF4ungGpjQW5XQc+zXPVkTkH9WYKP+5O64uYU9G9VBOHjI2YbHcxDnzb3QL9DXYMOLKbjakBFymuR37rmFzu1bWQ0U2Cbd9fHRiAg/+rnbz5ggSTrW0UBMMdXWIodEvLjFrYS7JBG35veK3lD66ojfAPtfDc69ay8HogmO0j+Bxe+q+Ftr/hhIbIF3ycq/D4s3D79jUXpvvDX/FBnvxH9swQR6Xa5ZUQAAAABJRU5ErkJggg==';
var FILE_BUTTON = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAVCAMAAAAdDWyfAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAMAUExURQAAAEBAQIiKhYqLiYuNiJOUkpmZmZubm+np6erq6uvr6+zs6+zs7O3t7e7u7e/v7vDw7/Dw8PHx8fLy8fLy8vPz8/T08/X19Pb29ff39vj49/39/f7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA7tdoAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGXRFWHRTb2Z0d2FyZQBQYWludC5ORVQgdjMuNS44NzuAXQAAAJdJREFUOE+NktkSgyAQBD2TqDFqxMUY/f/PjFTBwqYsR/qVZoZjk18UCbC+YY21MhOUh72sZZugANriWaF2dyxY044LLbL05phxqU37zNprqcXcOzwISzSFNLakxqWjKLVZf2lEahz67jXIs1lLpJlDkVJvI8IraC5toUamr33W1aOB2urZzv80LwT56YRczDCaXrE1UtsBCZE5jxq1hSYAAAAASUVORK5CYII=';
var MODE_STORAGE = 1;
var MODE_GMVALUE = 2;
var w = unsafeWindow || window, document = w.document;
var compatible = false;
// --------------------------------------------
// Models
// --------------------------------------------
var ListItem = function(id, v, title) {
this.id = id; // id (watch/XXXXXX)
this.v = v; // id (smXXXXXX)
this.t = title; // title
this.c = ''; // comment
this.tags = []; // tags
this.reg = new Date(); // registered date
};
var Tag = function(id, name) {
this.id = id; // id
this.t = name; // name
};
var Store = function() {};
Store.prototype = {
save: function(key, value) {
var d = this._toSource(value)
var s = this._getLocalStorage();
return (s != null)? s.setItem(key, d) : GM_setValue(key, d);
},
load: function(key, target) {
var s = this._getLocalStorage();
var target = target || ((s != null)? MODE_STORAGE : MODE_GMVALUE);
var result;
if (target == MODE_STORAGE) {
return (s != null)? s.getItem(key) : null;
} else {
return GM_getValue(key, null);
}
},
clear: function(key) {
var s = this._getLocalStorage();
if (s) {
s.removeItem(key);
}
},
_getLocalStorage: function() {
if (compatible) return null;
if (typeof localStorage == "object") {
return localStorage;
} else if (typeof globalStorage == "object") {
return globalStorage[location.host];
}
return null;
},
_toSource: function(value) {
if (!Object.prototype.toSource) {
return JSON.stringify(value);
}
return value.toSource();
}
};
var ItemList = function(key, items) {
this.key = key;
this.items = items || [];
this.store = new Store();
};
ItemList.prototype = {
get: function(idx) {
if (idx < this.items.length) {
return this.items[idx];
}
return null;
},
add: function(item) {
if (this.find(item.id) === false) {
this.items.push(item);
return true;
}
return false;
},
remove: function(item) {
var idx = this.find(item.id);
if (idx !== false) {
this.items.splice(idx, 1);
return true;
}
return false;
},
find: function(id) {
var len = this.items.length;
for (var i=0; i<len; ++i) {
if (id == this.items[i].id) {
return i;
}
}
return false;
},
save: function() {
return this.store.save(this.key, this.items);
},
load: function() {
var d = this.store.load(this.key, MODE_STORAGE)
|| this.store.load(this.key, MODE_GMVALUE);
if ((d == null) || (typeof d == "undefined")) {
d = "[]";
}
var _items = eval('('+ d +')');
if (_items) {
this.items = _items;
}
return _items;
},
clear: function() {
this.store.clear(this.key);
},
length: function() {
return this.items.length;
}
};
var MyList = function() {
this.initialize();
};
MyList.prototype = {
STORE_KEY_ITEMS: "additional_mylist",
STORE_KEY_TAGS: "additional_mylist_tags",
items: null,
tags: null,
sort: 0,
filter: 0,
initialize: function() {
this.items = new ItemList(this.STORE_KEY_ITEMS);
this.tags = new ItemList(this.STORE_KEY_TAGS);
this.items.load();
this.tags.load();
},
addItem: function(item) {
this.items.load();
if (this.items.add(item)) {
this.items.save();
return true;
}
return false;
},
removeItem: function(item) {
this.items.load();
if (this.items.remove(item)) {
this.items.save();
this.compactionTag();
return true;
}
return false;
},
findItem: function(id) {
var idx = this.items.find(id);
if (idx !== null) {
return this.items.get(idx);
}
return null;
},
addTag: function(name) {
this.tags.load();
if (name.length == 0) return false;
if (this.searchTag(name) != null) return true;
var id = 1;
for (var i=0; i<this.tags.length(); ++i) {
var tag = this.tags.get(i);
if (id <= tag.id) id = tag.id + 1;
}
if (this.tags.add(new Tag(id, name))) {
this.tags.save();
return id;
}
return null;
},
findTag: function(id) {
var idx = this.tags.find(id);
if (idx !== null) {
return this.tags.get(idx);
}
return null;
},
searchTag: function(name) {
for (var i=0; i<this.tags.length(); ++i) {
var tag = this.tags.get(i);
if (name == tag.t) return tag;
}
return null;
},
setFilter: function(filter) {
this.filter = filter;
},
setSortOrder: function(sort) {
this.sort = sort;
},
getItemList: function(offset, limit) {
// filter
var i, j;
var items = [];
var len = this.items.length();
for (i=0; i<len; ++i) {
var skip = false;
var item = this.items.get(i);
if (this.filter > 0) {
skip = true;
for (j=0; j<item.tags.length; ++j) {
if (this.filter == item.tags[j]) {
skip = false;
break;
}
}
}
if (!skip) {
items.push(item);
}
}
// sort
if (this.sort == 0) {
items.reverse();
}
// slice
offset = offset || 0;
if (limit) {
items = items.slice(offset, offset + limit);
} else {
items = items.slice(offset);
}
return items;
}
,
compactionTag: function() {
var i;
var refs = {};
for (i=0; i<this.items.length(); ++i) {
var _tags = this.items.get(i).tags;
for (var j in _tags) {
if (!refs[_tags[j]]) {
refs[_tags[j]] = 0;
}
refs[_tags[j]]++;
}
}
for (i=this.tags.length()-1; i>=0; --i) {
var tag = this.tags.get(i);
if (!refs[tag.id] || (refs[tag.id] == 0)) {
this.tags.remove(tag);
}
}
this.tags.save();
},
serialize: function() {
var tagmap = {};
for (i=0; i<this.tags.length(); ++i) {
var tag = this.tags.get(i);
tagmap[tag.id] = tag.t;
}
var data = '';
for (var i=0; i<this.items.length(); ++i) {
var item = this.items.get(i);
var line = [];
line.push(item.id.match(/^[a-z0-9]+/));
line.push(item.v);
line.push(Punycode.decode(item.t));
line.push((Punycode.decode(item.c)).replace(/[\r\n]/g, ' '));
line.push(Date.parse(item.reg));
for (var j=0; j<item.tags.length; ++j) {
line.push(tagmap[item.tags[j]]);
}
data += line.join("\t") +"\r\n";
}
return data;
}
}
// --------------------------------------------
// Views
// --------------------------------------------
var View = function(){};
View.prototype = {
initialize: function() {
},
render: function(template, params) {
var pattern;
var params = params || {};
for (var i in params) {
pattern = new RegExp("{"+ i +"}", "ig");
template = template.replace(pattern, params[i]);
}
return template;
},
sync: function() {
},
focus: function() {
},
command: function(node, type, func, options) {
var self = this;
var node = ((typeof node == "string")? $(node) : node);
node.addEventListener(type, function() {
self.elem = this;
self.options = options || {};
return func.apply(self, arguments);
}, false);
},
commands: function(className, type, func, options) {
var elems = document.getElementsByClassName(className);
for (var i=0; i<elems.length; ++i) {
this.command(elems[i], type, func, options);
}
}
}
// Mypage
var MyView = $extend(View, function(){});
MyView.prototype.TRIGGER_HASH = '#/extmylist';
MyView.prototype.page = 0;
MyView.prototype.isEditting = false;
MyView.prototype.hasUpdate = false;
MyView.prototype.initialize = function() {
var target = $('myNavMylistMemory');
if (target) {
this._initialize();
} else {
target = $('myNavMylist');
target.addEventListener('DOMNodeInserted', function() {
if ($('myNavMylistMemory')) {
target.removeEventListener('DOMNodeInserted', arguments.callee, false);
view._initialize();
}
}, false);
}
}
MyView.prototype._initialize = function() {
this.initLayout();
if (document.location.hash == this.TRIGGER_HASH) {
this.navigate();
}
this.command('myNaviExtMylist', 'click', this.navigate);
}
MyView.prototype.initLayout = function() {
var menu = $node(this.render(this.MENU_LINK));
var target = $('myNavMylistTemp');
target.parentNode.insertBefore(menu, target);
}
MyView.prototype.moveTo = function() {
if (!this.confirmCancelEdit()) return;
this.page = parseInt(this.elem.getAttribute("data-moveto")) - 1;
this.drawList();
}
MyView.prototype.navigate = function() {
this.page = 0;
this.initList();
this.drawList();
this.command('mylist_select_sort', 'change', this.selectSort)
this.command('mylist_select_tag', 'change', this.selectFilter);
this.command('mylist_import', 'change', this.importData);
this.command('popupExtMylistButton', 'click', this.togglePopup);
}
MyView.prototype.sync = function() {
this.hasUpdate = true;
}
MyView.prototype.focus = function() {
if (!this.hasUpdate) return;
if (document.location.hash != this.TRIGGER_HASH) return;
this.hasUpdate = false;
if (confirm('リストに更新がありました。再読み込みしますか?')) {
if (!this.confirmCancelEdit()) return;
mylist.initialize();
this.drawList();
}
}
MyView.prototype.initList = function() {
// init
document.title = "拡張マイリスト - ニコニコ動画(原宿)";
document.location.hash = this.TRIGGER_HASH;
var dda = $xa('//dd[contains(@class, "active")]');
for (var i in dda) {
var el = dda[i];
el.className = el.className.replace("-active", "");
el.className = el.className.replace( "active", "");
}
$('myNaviExtMylist').setAttribute('class', 'folder1-locked-active active');
$x('//div[@id="myContHead"]/h3').innerHTML = '拡張マイリスト';
// check update
if (this.hasUpdate) mylist.initialize();
// base
var params = {
count: mylist.items.length(),
button: FILE_BUTTON,
export: Base64.encode(mylist.serialize())
};
var src = $node(this.render(this.LIST_BASE, params));
var dst = $('myContBody');
ElementUtil.replaceChildren(src, dst);
}
MyView.prototype.drawList = function() {
var offset = this.page * NUM_DISPLAY_ITEMS;
var limit = NUM_DISPLAY_ITEMS;
var items = mylist.getItemList(offset, limit);
ElementUtil.removeAllChildren($('SYS_page_items'));
if (items.length > 0) {
$('noListMsg').style.display = 'none';
var html = '';
for (var i in items) {
var id = items[i].v || items[i].id;
var num = parseInt(id.replace(/^[a-z]+/, ''));
var item = {
id: items[i].id,
vid: items[i].id,
title: Punycode.decode(items[i].t),
comment: StringUtil.escape(Punycode.decode(items[i].c)),
thumb_url: 'http://tn-skr'+ ((num % 4) + 1) +'.smilevideo.jp/smile?i='+ num,
date: StringUtil.dateFormat(items[i].reg)
};
item.comm_suffix = (item.comment.length > 0)? '' : '_hidden';
html += this.render(this.LIST_ITEM, item);
}
$('SYS_page_items').appendChild($node(html));
this.commands('SYS_btn_edit_memo', 'click', this.beginEdit);
this.commands('SYS_btn_remove_item', 'click', this.removeItem);
// tags
for (var i in items) {
this.drawItemTags(items[i].id);
}
} else {
$('noListMsg').style.display = 'block';
}
this.drawPager();
this.drawTagList();
this.drawCount();
this.hasUpdate = false;
}
MyView.prototype.drawPager = function() {
var items = mylist.getItemList();
var showLinks = 10;
var showLinksHalf = parseInt(showLinks / 2);
var pageCurrent = this.page + 1;
var pageBegin = (pageCurrent - showLinksHalf < 1)? 1 : (pageCurrent - showLinksHalf);
var pageMax = parseInt((items.length - 1) / NUM_DISPLAY_ITEMS) + 1;
var pageEnd = ((pageBegin + showLinks) < pageMax)? (pageBegin + showLinks) : pageMax;
if (pageBegin > (pageEnd - showLinks)) {
pageBegin = ((pageEnd - showLinks) < 1)? 1 : (pageEnd - showLinks);
}
var targets = $xa('//div[@class="pager"]');
for (var pos in targets) {
var pager = '';
// prev
if (this.page > 0) {
pager += this.render(this.PAGER_ARROW, {'class':'prev', 'text':'前へ', 'moveto':(pageCurrent-1)});
}
// pages
for (i=pageBegin; i<=pageEnd; ++i) {
pager += this.render(
((i == pageCurrent)? this.PAGER_ITEM_CURRENT : this.PAGER_ITEM),
{page: i}
);
}
// next
if (this.page < (pageMax - 1)) {
pager += this.render(this.PAGER_ARROW, {'class':'next', 'text':'次へ', 'moveto':(pageCurrent+1)});
}
var src = $node(pager);
var dst = targets[pos];
ElementUtil.replaceChildren(src, dst);
}
this.commands('GM_mylist_move', 'click', this.moveTo);
}
MyView.prototype.drawTagList = function() {
var params = { value:0, text:'(すべて表示)' };
var options = this.render(this.TAGLIST_ITEM, params);
for (var i=0; i<mylist.tags.length(); ++i) {
var tag = mylist.tags.get(i);
params.value = tag.id;
params.text = tag.t;
options += this.render(this.TAGLIST_ITEM, params);
}
var src = $node(options, 'option');
var dst = $('mylist_select_tag');
ElementUtil.replaceChildren(src, dst);
ElementUtil.setValue(dst, mylist.filter);
}
MyView.prototype.drawItemTags = function(id) {
var item = mylist.findItem(id);
if (item == null) return;
var tags = this.render(this.LIST_ITEM_TAG_HEADER);
for (var i=0; i<item.tags.length; ++i) {
var _id = item.tags[i];
var tag = mylist.findTag(item.tags[i]);
if (tag !== null) {
tags += this.render(this.LIST_ITEM_TAG, {id:id, tag_id:tag.id, name:tag.t});
}
}
var src = $node(tags)
var dst = $('am_tags_'+ id);
ElementUtil.replaceChildren(src, dst);
var dd = $xa('.//a', dst);
for (i=0; i<dd.length; ++i) {
this.command(dd[i], 'click', this.clickFilter);
}
}
MyView.prototype.drawCount = function() {
$('mylist_items_length').innerHTML = mylist.items.length();
}
MyView.prototype.selectSort = function() {
if (!this.confirmCancelEdit()) return;
var sort = 0;
var t = $('mylist_select_sort');
if (t && (t.selectedIndex == 1)) {
sort = 1;
}
mylist.setSortOrder(sort);
this.drawList();
}
MyView.prototype.clickFilter = function() {
var tid = ElementUtil.getSuffix(this.elem);
this.setFilter(tid);
}
MyView.prototype.selectFilter = function() {
var tid = 0;
var t = $('mylist_select_tag');
if (t) {
tid = t.options[t.selectedIndex].value;
}
this.setFilter(tid);
}
MyView.prototype.setFilter = function(tid) {
if (!this.confirmCancelEdit()) return;
if (tid != mylist.filter) {
mylist.setFilter(tid);
this.page = 0;
this.drawList();
}
}
MyView.prototype.beginEdit = function() {
if (!this.confirmCancelEdit()) return;
this.closePopup();
var id = ElementUtil.getSuffix(this.elem);
var item = mylist.findItem(id);
if (!item) return;
var params = {
target_id: id,
comment: StringUtil.escape(Punycode.decode(item.c))
}
$('SYS_box_memo_'+ id).style.display = 'none';
$('commentEditForm_'+ id).innerHTML = this.render(this.EDIT_PANEL, params);
$('am_edit_comment').focus();
target = $('am_edit_tags');
for (var i=0; i<item.tags.length; ++i) {
var tag = mylist.findTag(item.tags[i]);
var node = $node(this.render(this.EDIT_TAG_ITEM, {name:tag.t}), 'tr');
target.appendChild(node);
}
if (item.tags.length == 0) {
$('am_no_tags').style.display = 'block';
}
this.commands('am_delete_tag', 'click', this.deleteTag);
this.command('am_add_tag', 'click', this.addTag);
this.command('am_editend', 'click', this.endEdit);
this.command('am_append_tag_text', 'keydown', this.onFormKeyDown);
this.isEditting = true;
}
MyView.prototype.endEdit = function() {
if (!this.isEditting) return;
var id = $('am_edit_target').value;
var panel = $('commentEditForm_'+ id);
if (panel && panel.hasChildNodes()) {
var i;
var comment = $('am_edit_comment').value;
var memo_box = $('SYS_box_memo_'+ id);
var memo_body = $x('div//span[@class="SYS_box_memo_body"]', memo_box);
var _tags = [];
var tags = document.getElementsByClassName('am_edit_tag_item');
for (i=0; i<tags.length; ++i) {
var name = tags[i].textContent;
var tag = mylist.searchTag(name);
if (tag !== null) {
tid = tag.id;
} else {
tid = mylist.addTag(name);
}
if (tid !== null) {
_tags.push(tid);
}
}
mylist.findItem(id).c = Punycode.encode(comment);
mylist.findItem(id).tags = _tags;
mylist.items.save();
var xa = $xa('.//div[contains(@class, "myListComment")]', memo_box);
for (var i in xa) {
var el = xa[i];
if (comment.length > 0) {
el.className = el.className.replace("_hidden", "");
} else {
el.className = el.className + "_hidden";
}
}
memo_body.innerHTML = comment;
memo_box.style.display = "block";
ElementUtil.removeAllChildren(panel);
mylist.compactionTag();
this.drawTagList();
this.drawItemTags(id);
}
this.isEditting = false;
}
MyView.prototype.addTag = function() {
var target = $('am_edit_tags');
var form = $('am_append_tag_text');
var tags = form.value;
if (tags.length == 0) return;
tags = tags.replace(' ', ' ').split(' ');
for (var i=0; i<tags.length; ++i) {
var tag = tags[i].replace(/^\s+|\s+$/g, '');
if (tag.length == 0) continue;
var node = $node(this.render(this.EDIT_TAG_ITEM, {name:tag}), 'tr');
var button = node.getElementsByClassName('am_delete_tag')[0];
this.command(button, 'click', this.deleteTag);
target.appendChild(node);
}
form.value = '';
form.focus();
$('am_no_tags').style.display = 'none';
}
MyView.prototype.deleteTag = function() {
var target = this.elem;
while (target.parentNode) {
var target = target.parentNode;
if (target.className == 'am_edit_tag_item_block') {
target.parentNode.removeChild(target);
}
}
var tags = document.getElementsByClassName('am_edit_tag_item_block');
if (tags.length == 0) {
$('am_no_tags').style.display = 'block';
}
}
MyView.prototype.onFormKeyDown = function(e) {
if (e.keyCode == 13) this.addTag();
}
MyView.prototype.confirmCancelEdit = function() {
if (this.isEditting) {
/*
if (!confirm('編集中のマイリストコメントを終了しますか?')) {
return false;
}
*/
this.endEdit();
}
return true;
}
MyView.prototype.removeItem = function() {
if (!this.confirmCancelEdit()) return;
var id = ElementUtil.getSuffix(this.elem);
var item = mylist.findItem(id);
if (!item) return;
var msg = '「'+ Punycode.decode(item.t) +'」を削除します。\r\n本当によろしいですか?';
if (confirm(msg)) {
if (mylist.removeItem(item)) {
this.closePopup();
this.drawList();
} else {
alert('削除できませんでした');
}
}
}
MyView.prototype.togglePopup = function() {
if ($('popupExtMylist').style.display == 'block') {
this.closePopup();
} else {
this.openPopup();
}
}
MyView.prototype.openPopup = function() {
$('popupExtMylist').style.display = 'block';
$('mylist_export').href = "data:application/octet-stream;base64,"+ Base64.encode(mylist.serialize());
}
MyView.prototype.closePopup = function() {
$('popupExtMylist').style.display = 'none';
}
MyView.prototype.importData = function() {
var file = $('mylist_import').files[0];
if (!file) return;
var self = this;
var reader = new FileReader();
reader.onload = function(e) {
var imported = 0;
var text = e.target.result;
var lines = text.split(/[\r\n]+/g);
for (var i=0; i<lines.length; ++i) {
var params = lines[i].split(/\t/g);
if (params.length < 4) continue;
var item = new ListItem(params[0], params[1], Punycode.encode(params[2]));
if (mylist.addItem(item)) {
var _tags = [];
for (var j=5; j<params.length; ++j) {
var name = params[j];
var tag = mylist.searchTag(name);
if (tag !== null) {
tid = tag.id;
} else {
tid = mylist.addTag(name);
}
if (tid !== null) {
_tags.push(tid);
}
}
mylist.findItem(params[0]).c = Punycode.encode(params[3]);
mylist.findItem(params[0]).tags = _tags;
mylist.items.save();
imported++;
}
}
if (imported > 0) {
mylist.initialize();
self.drawList();
self.openPopup();
alert(imported +"件がインポートされました");
} else {
alert("インポートされたデータはありません");
}
}
reader.onerror = function() {
alert('ファイルの読み込みに失敗しました');
}
reader.readAsText(file, 'utf-8');
}
MyView.prototype.MENU_LINK = '<dd id="myNaviExtMylist" class="folder1-locked"><a href="javascript:void(0);">拡張マイリスト</a></dd>';
MyView.prototype.LIST_BASE = ''
// header
+ '<div class="mylistFormWrap">'
+ '<div class="spBox">'
+ '<form class="sortForm SYS_box_sort" action="">'
+ '<div style="float:left">'
+ '<span class="itemNum">全<span id="mylist_items_length" style="margin-right:0">{count}</span>件</span>'
+ '<select name="sort" id="mylist_select_sort" class="mylist_select_sort">'
+ '<option value="1" selected="true">登録が新しい順</option>'
+ '<option value="2">登録が古い順</option>'
+ '</select>'
+ ' タグで絞り込み:<select style="width: 120px;" id="mylist_select_tag" class="mylist_select_tag"></select>'
+ ' '
+ '<img id="popupExtMylistButton" src="{button}" alt="エクスポート/インポート" title="エクスポート/インポート">'
+ '</div>'
+ '<div class="pagerWrap"><div class="pager"></div></div>'
+ '<div style="clear:both"></div>'
+ '<div id="popupExtMylist"><dl>'
+ '<dd>エクスポート:<a href="data:application/octet-stream;base64,{export}" id="mylist_export">ファイルダウンロード</a></dd>'
+ '<dd>インポート:<input type="file" id="mylist_import"></dd>'
+ '</dl></div>'
+ '</form>'
+ '<p class="spMsg">500件を超えても消えてしまいませんが、ニコニコ動画のデザイン変更でしばしば悲しいことになります。</p>'
+ '</div>'
+ '</div>'
+ '<style type="text/css">'
+ 'input.submit, button.submit {'
+ 'font-size:12px; background:#CCC url("http://res.nimg.jp/img/btn/bg_submit_01.gif") repeat-x;'
+ 'border:solid 1px; border-color:#C9CFCF #999F9F #696F6F; margin:2px 0; padding:2px 4px; cursor:pointer;'
+ '}\n'
+ '.am_item_tag{'
+ 'font-size:11px;'
+ '}\n'
+ '.am_edit_tag_item_block td {'
+ 'font-size:12px; border-bottom:dotted 1px #666;'
+ '}\n'
+ '.am_edit_tag_item {'
+ 'padding: 2px 3em 0 4px;'
+ '}\n'
+ '#popupExtMylist {'
+ 'display:none; margin:4px 60px 0; background-color:#fff; border:1px solid #333; padding:4px 8px;'
+ '}\n'
+ '#popupExtMylistButton {'
+ 'cursor:pointer;'
+ '}'
+ '</style>'
+ '</div>'
// main
+ '<div id="noListMsg" class="noListMsg" style="display:none">'
+ '<p class="att">拡張マイリストに動画は登録されていません。<br>好きな動画を登録しましょう!</p>'
+ '</div>'
+ '<ul id="SYS_page_items" class="myContList videoList"></ul>'
// footer
+ '<div class="pagerWrap"><div class="pager"></div></div>'
;
MyView.prototype.LIST_ITEM = ''
+ '<li class="SYS_box_item">'
+ '<a class="mypageThumb" href="watch/{vid}"><img class="lazyimage" src="{thumb_url}"></a>'
+ '<div class="mylistVideo">'
+ '<h4><a href="/watch/{vid}">{title}</a></h4>'
+ '<p class="mylistTime">{date} 登録</p>'
+ '<dl id="am_tags_{id}">'
+ '<dt>登録タグ:</dt>'
+ '</dl>'
+ '<div class="comment SYS_box_memo" id="SYS_box_memo_{id}">'
+ '<div class="myListCommentT{comm_suffix}">'
+ '<div class="myListCommentB{comm_suffix}">'
+ '<div class="myListCommentL{comm_suffix}">'
+ '<div class="myListCommentR{comm_suffix}">'
+ '<p><span class="SYS_box_memo_body">{comment}</span>'
+ '<img class="editBtnComment SYS_btn_edit_memo" id="SYS_btn_edit_memo_{id}" alt="マイリストコメント編集" src="http://uni.res.nimg.jp/img/blank.gif"></p>'
+ '</div></div></div></div>'
+ '</div>'
+ '<div id="commentEditForm_{id}"></div>'
+ '<div class="buttons">'
+ '<a class="editBtnDel SYS_btn_remove_item" id="SYS_btn_remove_item_{id}" href="javascript:void(0);">削除</a>'
+ '</div>'
+ '</div>'
+ '</li>'
;
MyView.prototype.LIST_ITEM_TAG_HEADER = '<dt>登録タグ: </dt>';
MyView.prototype.LIST_ITEM_TAG = '<dd><a href="javascript:void(0);" id="am_item_tag_{id}_{tag_id}" class="am_item_tag">{name}</a></dd>';
MyView.prototype.PAGER_ARROW = '<a href="javascript:void(0)" class="GM_mylist_move {class}" data-moveto="{moveto}">{text}</a>';
MyView.prototype.PAGER_ITEM = '<a href="javascript:void(0)" class="GM_mylist_move SYS_btn_pager" data-moveto="{page}">{page}</a>';
MyView.prototype.PAGER_ITEM_CURRENT = '<span class="active">{page}</span>';
MyView.prototype.TAGLIST_ITEM = '<option value="{value}">{text}</option>'
MyView.prototype.EDIT_PANEL = ''
+ '<div id="commentEditForm_body" style="border:1px solid #95B9DE; padding: 4px">'
+ '<div style="color:#666">マイリストコメント</div>'
+ '<textarea id="am_edit_comment" style="width:98%; margin:2px 0 0.5em 0; padding:2px; border:solid 1px #aaa; font-size:12px">'
+ '{comment}'
+ '</textarea>'
+ '<div style="color:#666; margin-top:4px">タグ</div>'
+ '<input type="text" id="am_append_tag_text" style="border:solid 1px #aaa; font-size:12px"> '
+ '<input type="button" id="am_add_tag" class="submit" value="登録">'
+ '<table cellspacing="2" cellpadding="2" border="0" style="font-size:12px; line-height:1; margin:4px 2px; padding-left:16px;">'
+ '<tbody id="am_edit_tags">'
+ '<tr id="am_no_tags" style="display:none"><td style="font-size:12px; border-bottom:dotted 1px #666;">登録されているタグはありません</td></tr>'
+ '</tbody>'
+ '</table>'
+ '<div style="margin:4px">'
+ '<input type="hidden" id="am_edit_target" value="{target_id}">'
+ '<input type="button" id="am_editend" style="vertical-align:bottom" class="submit" value="編集を終了する">'
+ '</div>'
+ '</div>';
MyView.prototype.EDIT_TAG_ITEM = '<tr class="am_edit_tag_item_block">'
+ '<td class="am_edit_tag_item">{name}</td>'
+ '<td><input type="button" class="submit am_delete_tag" value="削除"></td>'
+ '</tr>'
;
// Watchpage
var WatchView = $extend(View, function(){});
WatchView.prototype.initialize = function() {
this.initLayout();
this.command('gm_add_mylist', 'click', this.addItem);
}
WatchView.prototype.initLayout = function() {
var MSG_deflist = $('MSG_deflist');
// button
var button = $node(this.render(this.ADD_BUTTON, {data:IMAGE_BUTTON}));
MSG_deflist.parentNode.appendChild(button);
// popup window
var popup = $('MSG_deflist_success').cloneNode(true);
popup.setAttribute('id', 'MSG_gm_mylist_message');
popup.setAttribute('style', 'background-color:#696F6F; opacity:0.8; display:none');
MSG_deflist.appendChild(popup);
// fix
$('video_tags').style.width = "576px";
}
WatchView.prototype.addItem = function() {
var video = this.getVideo();
if (!video) {
this.popupMessage('動画情報の取得に失敗しました…orz');
return;
}
var id = video.v;
var v = video.id;
var t = video.encodedTitle;
var item = new ListItem(id, v, t);
if (mylist.addItem(item)) {
this.popupMessage('<a href="/my/mylist/#/extmylist" style="color:#fff">拡張マイリストに登録しました(多分)。</a>');
} else {
this.popupMessage('すでに登録されています。');
}
}
WatchView.prototype.getVideo = function() {
var video = w.Video;
if (!video) {
var scripts = document.getElementsByTagName('script');
for (var i in scripts) {
if (scripts[i].text) {
var script = scripts[i].text;
if (script.match(/var Video/)) {
eval(script.replace(/(<!--|-->)/g, ''));
video = Video;
break;
}
}
}
}
if (video.id && video.title) {
video.encodedTitle = Punycode.encode(video.title);
return video;
}
return null;
}
WatchView.prototype.popupMessage = function(msg) {
var msgs = $xa('//p[starts-with(@id, "MSG_deflist_")]');
for (var i in msgs) {
if (msgs[i].style) msgs[i].style.display = 'none';
};
// show
$('MSG_gm_mylist_message').innerHTML = msg;
$('MSG_gm_mylist_message').style.display = 'block';
$('MSG_deflist').style.display = 'block';
// hide
setTimeout(function() {
$('MSG_deflist').style.display = 'none';
$('MSG_gm_mylist_message').style.display = 'none';
$('MSG_gm_mylist_message').innerHTML = '';
}, 3000);
}
WatchView.prototype.ADD_BUTTON = '<div style="position:absolute;left:-91px"><a id="gm_add_mylist" href="javascript:void(0);"><img src="{data}" alt="拡張マイリストに登録"></a></div>';
// --------------------------------------------
// Utilities
// --------------------------------------------
function $(id) {
return document.getElementById(id);
}
function $e(type) {
return document.createElement(type);
}
function $t(text) {
return document.createTextNode(text);
}
function $x(xpath, node) {
node = node || document;
var result = document.evaluate(xpath, node, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue? result.singleNodeValue : null;
}
// reference from http://d.hatena.ne.jp/os0x/20081024/1224863727
function $xa(exp, context) {
context || (context = document);
var expr = (context.ownerDocument || context).createExpression(exp, function (prefix) {
return document.createNSResolver(context.documentElement || context).lookupNamespaceURI(prefix) ||
context.namespaceURI || document.documentElement.namespaceURI || "";
});
var result = expr.evaluate(context, XPathResult.ANY_TYPE, null);
switch (result.resultType) {
case XPathResult.STRING_TYPE : return result.stringValue;
case XPathResult.NUMBER_TYPE : return result.numberValue;
case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
var ret = [], i = null;
while (i = result.iterateNext()) ret.push(i);
return ret;
}
return null;
};
function $node(html, type, p) {
var tmp;
var p = p || 0;
var container = document.createDocumentFragment();
switch (type) {
case 'tr':
p++;
tmp = document.createElement('table');
break;
case 'option':
tmp = document.createElement('select');
break;
default:
tmp = document.createElement('div');
}
tmp.style.display = "none";
document.body.appendChild(tmp);
tmp.innerHTML = html;
while (tmp.firstChild) {
container.appendChild(tmp.firstChild);
}
document.body.removeChild(tmp);
for (var i=0; i<p; ++i) {
if (container.firstChild) container = container.firstChild;
}
return container;
};
function $extend(s, c) {
var f = function(){};
f.prototype = s.prototype;
c.prototype = new f();
c.prototype._super = s.prototype;
c.prototype._super.constructor = s;
c.prototype.constructor = c;
return c;
};
StringUtil = {
escape: function(str) {
return str.replace(/&/,'&').replace(/</g,'<').replace(/>/,'>');
},
dateFormat: function(str) {
var date = new Date(str);
var _ = {
'year' : date.getYear(),
'month': date.getMonth() + 1,
'date' : date.getDate(),
'hour' : date.getHours(),
'min' : date.getMinutes(),
'sec' : date.getSeconds()
};
for (var key in _) {
if (key != 'year') {
_[key] = (_[key] < 10)? ('0'+ _[key]) : _[key];
} else {
_[key] = (_[key] < 2000)? (_[key] + 1900) : _[key];
}
}
return _['year'] +'年'+ _['month'] +'月'+ _['date'] +'日 '
+ _['hour'] +':'+ _['min'];
}
}
ElementUtil = {
replaceChildren: function(src, dst) {
ElementUtil.removeAllChildren(dst);
while (src.firstChild) {
dst.appendChild(src.firstChild);
}
},
removeAllChildren: function(elem) {
if (elem.hasChildNodes()) {
while (elem.firstChild) {
elem.removeChild(elem.firstChild);
}
}
},
getSuffix: function(elem) {
return elem.id.substring(elem.id.lastIndexOf('_')+1);
},
setValue: function(elem, value) {
switch (elem.nodeName.toLowerCase()) {
case 'select':
for (var i=0; i<elem.options.length; ++i) {
elem.options[i].selected = ((elem.options[i].value == value)? true : false);
}
break;
default:
elem.value = value;
}
}
}
// http://coderepos.org/share/browser/lang/javascript/Base64/trunk/base64.js
Base64 = {
utob: function(uni){
return uni.replace(/[^\x00-\x7f]/g,
function(m){
var n = m.charCodeAt(0);
return n < 0x800 ? String.fromCharCode(0xc0 | (n >>> 6))
+ String.fromCharCode(0x80 | (n & 0x3f))
: String.fromCharCode(0xe0 | ((n >>> 12) & 0x0f))
+ String.fromCharCode(0x80 | ((n >>> 6) & 0x3f))
+ String.fromCharCode(0x80 | (n & 0x3f));
}
);
},
encode: function(u){ return btoa(Base64.utob(u)) }
};
// Punycode for JavaScript
// Copyright (c)2005 Airemix. All rights reserved.
// This software is licensed under BSDL.
var Punycode = new function()
{
var MAXINT = 0x7fffffff;
var BASE = 36;
var TMIN = 1;
var TMAX = 26;
var SKEW = 38;
var DAMP = 700;
var INITIAL_BIAS = 72;
var INITIAL_N = 0x80;
var DELIMITER = 0x2D;
/* basic(cp) tests whether cp is a basic code point: */
function basic(cp){
return cp < 0x80;
// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./
}
function decode_digit(cp){
return cp < 58 ? cp - 22 : cp < 91 ? cp - 65 : cp < 123 ? cp - 97 : BASE;
}
function encode_digit(d){
return d < 26 ? d + 97 : d + 22;
}
/*** Bias adaptation function ***/
function adapt(delta, numpoints, firsttime){
var k;
delta = firsttime ? parseInt(delta / DAMP) : delta >> 1;
delta += parseInt(delta / numpoints);
for (k = 0; delta > ((BASE - TMIN) * TMAX) >> 1; k += BASE) {
delta = parseInt(delta / (BASE - TMIN));
}
return parseInt(k + (BASE - TMIN + 1) * delta / (delta + SKEW));
}
/*** Main encode function ***/
function encode(input){
if(typeof(input)=='string'){
var str = input;
input = new Array;
for(var i=0; i< str.length; i++)
input.push(str.charCodeAt(i));
}
var output = _encode( input );
for (var j = 0; j < output.length; ++j) {
var c = output[j];
if(c >= 0 && c <= 127); else break;
output[j] = String.fromCharCode(c);
}
return output.join('');
}
this.encode = encode;
function _encode( input ){
var output = new Array;
var n = INITIAL_N;
var delta = 0;
var bias = INITIAL_BIAS;
for (j = 0; j < input.length; ++j)
if (basic(input[j])) output.push(input[j]);
var bcp = output.length;
var handled = bcp;
if (bcp > 0) output.push(DELIMITER);
while (handled < input.length) {
var m = MAXINT;
for (j = 0; j < input.length; ++j)
if (input[j] >= n && input[j] < m) m = input[j];
if (m - n > (MAXINT - delta) / (handled + 1)) throw 'overflow';
delta += (m - n) * (handled + 1);
n = m;
for (j = 0; j < input.length; ++j) {
if (input[j] < n && ++delta == 0) throw 'overflow';
if (input[j] == n) {
var q = delta;
for (var k = BASE; ; k += BASE) {
var t = k <= bias ? TMIN : k >= bias + TMAX ? TMAX : k - bias;
if (q < t) break;
output.push(encode_digit(t + (q - t) % (BASE - t)));
q = parseInt((q - t) / (BASE - t));
}
output.push(encode_digit(q));
bias = adapt(delta, handled + 1, handled == bcp);
delta = 0;
++handled;
}
}
++delta, ++n;
}
return output;
}
/*** Main decode function ***/
function decode(input)
{
if(typeof(input)=='string'){
var str = input;
input = new Array;
for(var i=0; i< str.length; i++)
input.push(str.charCodeAt(i));
}
var output = '';
try {
output = _decode( input );
} catch (e) {
output = input;
}
for (var j = 0; j < output.length; ++j)
output[j] = String.fromCharCode(output[j]);
return output.join('');
}
this.decode = decode;
function _decode( input )
{
var j;
var output = new Array;
var n = INITIAL_N;
var bias = INITIAL_BIAS;
var bcp = 0;
for (j = input.length; j >= 0; --j)
if (input[j] == DELIMITER){ bcp = j; break; }
for (j = 0; j < bcp; ++j) {
if (!basic(input[j])) throw 'bad_input';
output.push( input[j] );
}
var i = 0;
var inp = bcp > 0 ? bcp + 1 : 0;
while( inp < input.length ){
var oldi = i;
for (var w = 1, k = BASE; ; k += BASE) {
if (inp >= input.length) throw 'bad_input';
var digit = decode_digit(input[inp++]);
if (digit >= BASE) throw 'bad_input';
if (digit > (MAXINT - i) / w) throw 'overflow';
i += digit * w;
var t = k <= bias ? TMIN : k >= bias + TMAX ? TMAX : k - bias;
if (digit < t) break;
if (w > MAXINT / (BASE - t)) throw 'overflow';
w *= (BASE - t);
}
bias = adapt(i - oldi, output.length + 1, oldi == 0);
oldi = parseInt(i / (output.length + 1));
if (oldi > MAXINT - n) throw 'overflow';
n += oldi;
i %= (output.length + 1);
output.splice(i++,0,n);
}
return output;
}
}
// --------------------------------------------
// Events
// --------------------------------------------
w.addEventListener('storage', function(e) {
if (view) view.sync();
}, false);
w.addEventListener('focus', function(e) {
if (view) view.focus();
}, false);
// --------------------------------------------
// main
// --------------------------------------------
var view;
var mylist = new MyList();
if (location.href.indexOf('.nicovideo.jp/watch') != - 1) {
view = new WatchView();
} else {
view = new MyView();
}
view.initialize();
// --------------------------------------------
// TEST
// --------------------------------------------
function test() {
// button image
var img = document.createElement('img');
document.body.appendChild(img);
img.src = IMAGE_BUTTON;
assertEqual(img.width, 91);
assertEqual(img.height, 32);
document.body.removeChild(img);
// store
var store = new Store();
var test_key = "_test";
var test_value = 0;
GM_setValue(test_key, null);
localStorage.removeItem(test_key);
assertEqual(null, store.load(test_key, MODE_STORAGE));
assertEqual(null, store.load(test_key, MODE_GMVALUE));
test_value = 123;
store.save(test_key, test_value);
assertEqual(toSource(test_value), store.load(test_key));
test_value = "abcde";
store.save(test_key, test_value);
assertEqual(toSource(test_value), store.load(test_key));
test_value = ["a", 123, [1, 2, 3], {"z":"test"}];
store.save(test_key, test_value);
assertEqual(toSource(test_value), store.load(test_key));
// itemlist (tag)
var taglist = new ItemList(test_key);
GM_setValue(test_key, null);
localStorage.removeItem(test_key);
var item = new Tag(10, "foo");
assertEqual(0, taglist.length());
taglist.add(item);
assertEqual(1, taglist.length());
item = new Tag(25, "bar");
taglist.add(item);
assertEqual(2, taglist.length());
item = new Tag(123456, "baz");
taglist.add(item);
assertEqual(3, taglist.length());
item = new Tag(25, "qux");
taglist.add(item);
assertEqual(3, taglist.length());
assertEqual(1, taglist.find(item.id));
taglist.remove(item);
assertEqual(2, taglist.length());
// itemlist (video)
test_key = "__test";
var videolist = new ItemList(test_key);
GM_setValue(test_key, null);
localStorage.removeItem(test_key);
item = new ListItem(10, 20, "hoge");
assertEqual(0, videolist.length());
videolist.add(item);
assertEqual(1, videolist.length());
assertEqual(2, taglist.length());
item = new ListItem(25, 100, "fuga");
videolist.add(item);
assertEqual(2, videolist.length());
item = new ListItem(456789, 999999, "hogehoge");
videolist.add(item);
assertEqual(3, videolist.length());
assertEqual(2, videolist.find(item.id));
videolist.remove(item);
assertEqual(2, videolist.length());
// Punycode
var src = "abcXYZ123[]!@#$%^&*()-+=?<>/;:'\"\\";
var dst = Punycode.encode(src);
assertEqual(src, Punycode.decode(dst));
src = "逝ってしまったわ、円環の理に導かれて・・・";
dst = Punycode.encode(src);
assertTrue((src != dst));
assertEqual(src, Punycode.decode(dst));
// test method
function assertTrue(a) {
console.log(a? "OK":"NG");
}
function assertEqual(a, b) {
var msg = (a == b)? ("OK: "+ a) : ("NG: "+ a +", "+ b);
console.log(msg);
}
// util
function toSource(value) {
if (!Object.prototype.toSource) {
return JSON.stringify(value);
}
return value.toSource();
}
}
})();