Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
// ==UserScript==
// @name View image links
// @namespace http://gaia.freera.net/~horance/userscripts/
// @description generate album view for image links. all button has access key (the first letter).
// @include http://www.wretch.cc/album/album.php*
// @include http://*diggirl.net/*detail.jsp*
// @include http://photo.xuite.net/*/*
// @include http://www.pixnet.net/album/*
// ==/UserScript==
// $Author: horance $
(function(){
const MY_NAME = 'View image links';
const MY_NAMESPACE = 'http://gaia.freera.net/~horance/userscripts/';
const UPDATE_URL = 'https://opensvn.csie.org/MirrorScripts/userscripts/view.image.links.user.js';
const FILTER_URL = 'https://opensvn.csie.org/MirrorScripts/userscripts/view.image.links.filter.js';
checkUpdate();
/*
* see https://opensvn.csie.org/traccgi/MirrorScripts/trac.cgi/log/userscripts/view.image.links.user.js for change logs.
*/
var EMPTY_FEED_XML = '<?xml version="1.0" encoding="ISO-8859-1" ?><rss xmlns:media="http://search.yahoo.com/mrss" version="2.0"><channel></channel></rss>';
// PicLens API URL
//var PICLENS_API_URL = 'http://fundnav.googlepages.com/piclens.js';
var PICLENS_API_URL = 'http://lite.piclens.com/current/piclens.js';
loadPicLensAPI();
// filter map
var filterMap = {};
// current matched url regex
var currentMatch = '';
// filter
var filter = defaultFilter;
//filtering links
var urls = new Array();
//preload 5 images by default. if i == 0, this will load 0,1,2 elements in urls array.
//if i == 2, then 0,1,2,3,4 will be pre-loaded
var preload = 5;
//refresh url
urls = refreshURLs();
var piclensfeeddata = generateFeedDataForPicLens(urls);
//current edit
var curEdit = 1;
//initialize cache
if(preload % i == 0){
preload ++;
}
//current image index
var i = 0;
var c_url = '';
var c_desc = '';
if(urls[i]){
c_url = urls[i].src;
c_desc = urls[i].desc;
}
// HTML injection ;)
var div = document.createElement('div');
div.innerHTML = '<div id="vil_display" style="display: none;" class="vil"><div id="vil_ctrlbar" class="vil">' +
'<input type="button" id="vil_top" name="vil_top" accesskey="t" value="Top" class="vil"/>' +
'<input type="button" id="vil_prev" name="vil_prev" accesskey="p" value="Prev"/>' +
'<input type="button" id="vil_next" name="vil_next" accesskey="n" value="Next"/>' +
'<input type="button" id="vil_end" name="vil_end" accesskey="e" value="End"/>' +
'<input type="text" id="vil_input" name="vil_input" size="3" accesskey="i" value="'+i+'"/>/'+(urls.length-1)+
'<input type="button" id="vil_jump" name="vil_jump" accesskey="g" value="Go"/>' +
'fit <select id="vil_fit" name="vil_fit" accesskey="f"><option value="n">None</option>' +
'<option value="h">Height</option><option value="w">Width</option><option value="b">Both</option></select> to: ' +
'<input type="text" id="vil_width" name="vil_width" size="3" accesskey="w" value="800"/>px ' +
'Style: <select id="vil_style" name="vil_style" accesskey="s"></select>' +
'<input type="button" id="vil_hide" name="vil_hide" accesskey="h" value="Hide"/></div>' +
'<div id="vil_infobar" class="vil"><a id="vil_url" href="'+ c_url +
'" accesskey="o" target="_blank" title="open in new window">' + c_desc +'</a></div> ' +
'<div id="vil_image" class="vil"><img id="vil_img" src="' + c_url +'"/>' +
'</div></div><div id="vil_control" class="vil" style="display: block">' +
'<input type="button" id="vil_show" name="vil_show" accesskey="v" value="View"/> ' +
'<input type="button" id="vil_piclens" name="vil_piclens" accesskey="p" value="PicLens"/> ' +
'<input type="button" id="vil_download" name="vil_download" accesskey="d" value="Download"/> ' +
'<input type="button" id="vil_conf" name="vil_conf" accesskey="c" value="Config"/><br/> ' +
'<p id="vil_download_panel" style="display: none">' +
'Files to be downloaded: <br/><select multiple="true" id="vil_flist" name="vil_flist" size="20"></select><br/>' +
'<input type="checkbox" id="check_save_index" name="check_save_index" value="1">Save index.html<br/>' +
'Output Folder: <br/><input type="text" id="vil_output_path" name="vil_output_path" size="30" readonly="true">' +
'<input type="button" id="vil_browse" name="vil_browse" accesskey="b" value="Browse"/><br/>' +
'<input type="button" id="vil_start" name="vil_start" accesskey="s" value="Start"/>' +
'</p>'+
'<p id="vil_config" style="display: none">' +
'Current Editing: <input type="radio" id="radio_filter" name="current_edit" accesskey="1" value="1" checked="checked">Filter ' +
'<input type="radio" id="radio_style" name="current_edit" accesskey="2" value="2"> Style<br/>' +
'<select id="vil_list" name="vil_list" accesskey="l"></select>' +
'<input type="button" id="vil_del" name="vil_del" accesskey="d" value="Delete"/><br/>' +
'URL Regex: <input type="text" id="vil_regex" name="vil_regex" accesskey="u" value="^.*foo.bar.com.*$"/><br/>' +
'Filter function: <br/><textarea id="vil_filter" name="vil_filter" rows="20" accesskey="f"></textarea><br/>' +
'<input type="checkbox" id="vil_autocheck" name="vil_autocheck" accesskey="a" value="1"/>Auto-Update? ' +
'<input type="button" id="vil_checknow" name="vil_checknow" accesskey="n" value="Check Now"/><br/>' +
'<span id="vil_ac_config" style="display: none">Check Interval?(days):<input type="text" id="vil_ac_int" name="vil_ac_int" accesskey="i" value="1" size="2"/><br/></span>' +
'<input type="button" id="vil_save" name="vil_save" accesskey="s" value="Save"/>' +
'</p></div>';
document.body.insertBefore(div, document.body.firstChild);
// get object references
var img = _gel('vil_img');
var btn_prev = _gel('vil_prev');
var btn_next = _gel('vil_next');
var btn_top = _gel('vil_top');
var btn_end = _gel('vil_end');
var btn_jump = _gel('vil_jump');
var btn_del = _gel('vil_del');
var vil_fit = _gel('vil_fit');
var ctrl = _gel('vil_control');
var disp = _gel('vil_display');
var conf = _gel('vil_config');
var text_input = _gel('vil_input');
var text_width = _gel('vil_width');
var text_fg = _gel('vil_fg');
var text_bg = _gel('vil_bg');
var text_regex = _gel('vil_regex');
var text_filter = _gel('vil_filter');
var vil_list = _gel('vil_list');
var vil_style = _gel('vil_style');
var link_href = _gel('vil_url');
var radio_filter = _gel('radio_filter');
var radio_style = _gel('radio_style');
var disp_image = _gel('vil_image');
// main function to refresh config and URLs
function refreshURLs(){
//load filters
filterMap = loadFilterMap();
//get filter
filter = getFilter();
//execute filter
return getURLs();
}
//function to load filter map by GM_getValue
function loadFilterMap(){
var filterSrc = GM_getValue('filters', 'no filter defined');
if(filterSrc != 'no filter defined'){
try {
eval( 'var tmpMap = ' + filterSrc );
return tmpMap;
} catch (err){
GM_log(filterSrc);
alert('error parsing filter config! ' + err);
// return empty map
return {};
}
} else {
return {};
}
}
//get current filter by matching current window.location.href
function getFilter(){
//filter loaded, matching document.location
var loc = window.location.href;
for( key in filterMap ){
var regex = new RegExp(key, 'ig');
if(regex && loc.match(regex)){
filter = filterMap[key];
currentMatch = key;
return filter;
}
}
}
//exec filter to get URLS
function getURLs(){
//get urls
if(filter){
urls = filter();
//invalid return
if(!urls || 'object' != typeof urls){
alert('invalid return from filter function:' + urls);
//fallback to default
urls = defaultFilter();
}
} else {
urls = defaultFilter();
}
if('string' == typeof urls[0]){
for(var i=0;i<urls.length;i++){
urls[i] = {src: urls[i], desc: urls[i]};
}
}
//if div is added
if(text_input){
//update total count
text_input.nextSibling.nodeValue = '/'+(urls.length-1);
//reset image
i = 0;
showImage();
}
return urls;
}
//default filter function
function defaultFilter(){
var myurls = new Array();
var links = document.getElementsByTagName('a');
for(var i=0;i<links.length;i++){
if(links[i].href.match(/.*\.(jpg|gif|png)$/i)){
myurls.push({ 'src': links[i].href, 'desc': links[i].innerHTML});
}
}
return myurls;
}
//auto resize image, called by Event Listener
function resize(){
link_href.innerHTML=c_desc;
var w = parseInt(text_width.value);
if(w){
var f = vil_fit.value;
if(f == 'w' || f == 'b'){// fit width
if(f != 'b' || img.width > img.height){
if(img.width > w){
img.height = Math.round(w/img.width*img.height);
img.width = w;
}
}
}
if(f == 'h' || f == 'b'){
if(f != 'b' || img.height > img.width){
if(img.height > w){
img.width = Math.round(w/img.height*img.width);
img.height = w;
}
}
}
if(f == 'n'){
//reset inmage size
img.removeAttribute('width');
img.removeAttribute('height');
}
}else{
alert('invalid width: '+text_width);
}
preloadImage();
setButtons();
}
function MM_preload() { //v2.1 (OSL)
if (document.images) {
var imgFiles = MM_preload.arguments;
if (document.preloadArray==null) document.preloadArray = new Array();
document.preloadArray.length = imgFiles.length;
var i = document.preloadArray.length;
with (document) for (var j=0; j<imgFiles.length; j++) {
preloadArray[i] = new Image;
var idx = imgFiles[j];
preloadArray[i].addEventListener('error', function(event){
handleError(event, this , idx);
}, true);
preloadArray[i++].src = (urls[idx].src)?(urls[idx].src):(urls[idx]);
}
}
}
function preloadImage(){
var size = ((preload-1)/2);
var start = (i-size)>0?(i-size):0;
var end = (i+size > urls.length)?urls.length:i+size;
var str = '';
for(var j = start;j<end;j++){
str += j;
if((j+1)<end){
str += ', ';
}
}
eval('MM_preload('+str+')');
}
//navigation functions
//jump by user input
function jumpto(){
var idx = parseInt(text_input.value);
if(!idx || idx<0 || idx >= urls.length){
alert('Invalid input: no such image');
}else{
i = idx;
showImage();
}
}
//prev image
function prev(){
i--;
if(i<0){
alert('This is the first picture!');
}else{
showImage();
}
}
//next image
function next(){
i++;
if(i >= urls.length){
alert('This is the last picture!');
}else{
showImage();
}
}
// first image
function top(){
i=0;
showImage();
}
// last image
function end(){
i=urls.length-1;
showImage();
}
// show display panel
function show(){
if(urls.length==0){
alert('no image links!');
return;
}
ctrl.style.display = 'none';
disp.style.display = 'block';
}
// hide display panel
function hide(){
ctrl.style.display = 'block';
disp.style.display = 'none';
}
// toggle config panel
function toggle(tid, flag){
var obj = _gel(tid);
if(obj){
if(flag){
obj.style.display = flag;
}else{
if(obj.style.display != 'none'){
obj.style.display = 'none';
}else{
obj.style.display = '';
}
}
}
}
//show image!
function showImage(){
if(urls[i]){
c_url = urls[i].src;
c_desc = urls[i].desc;
link_href.href=c_url;
link_href.innerHTML=loadingimg+' loading......';
//reset image size
img.removeAttribute('width');
img.removeAttribute('height');
//load image
img.src=c_url;
img.alt=c_desc;
}
}
// set navigation button state
function setButtons(){
text_input.value=i;
btn_top.disabled = false;
btn_prev.disabled = false;
btn_next.disabled = false;
btn_end.disabled = false;
if(i<=0){
btn_top.disabled = true;
btn_prev.disabled = true;
}
if(i>=urls.length-1){
btn_next.disabled = true;
btn_end.disabled = true;
}
}
// prepare config list
function prepareConfigList(){
var label1 = 'URL Regex: ';
var label2 = 'Filter function:';
var label3 = 'New Filter';
var selectedValue = currentMatch;
var dataMap = filterMap;
if(curEdit == 2){
label1 = 'CSS Name : ';
label2 = 'CSS Style:';
label3 = 'New Style';
selectedValue = cssName;
dataMap = cssMap;
}
text_regex.previousSibling.nodeValue = label1;
text_filter.previousSibling.previousSibling.nodeValue = label2;
var tmpstr = '<option value="--">'+label3+'</option>';
for(key in dataMap){
tmpstr += '<option value="'+key+'" '+((key == selectedValue)?'selected':'')+'>'+key+'</option>';
}
vil_list.innerHTML = tmpstr;
listChange();
var ac = GM_getValue('checkInterval','1');
var isac = (parseInt(ac) > 0);
_gel('vil_autocheck').checked = isac;
_gel('vil_ac_int').value = (isac?ac:'1');
toggle('vil_ac_config', (isac?'block':'none'));
}
// update UI, call by event listener
function listChange(){
var isFilter = (curEdit == 1);
var dataMap = (isFilter)?filterMap:cssMap;
var def_value = (isFilter)?defaultFilter.toString():defaultStyle;
var key = vil_list.value;
if(key == '--'){
btn_del.disabled = true;
key = (isFilter?'^.*foo\\.bar\\.com.*$':'default');
}else{
btn_del.disabled = false;
}
text_regex.value = key;
text_filter.value = (dataMap[key])?dataMap[key].toString():def_value;
if(!isFilter){
changeStyle(key);
}
}
//save CSS or filter config
function saveData(){
//check auto-update setting
var ac = _gel('vil_autocheck').checked;
if(ac){
var days = parseInt(_gel('vil_ac_int').value);
if(isNaN(days) || days < 1){
alert('invalid check interval! (must be an integer and > 0)');
_gel('vil_ac_int').focus();
return;
}else{
GM_setValue('checkInterval',''+days);
}
}else{
GM_setValue('checkInterval','-1');
}
var dataMap = filterMap;
var dataValue = null;
var saveTag = 'filters';
var refreshFunction = refreshURLs;
if(curEdit == 1){ // filter
try {
//test URL Regexp
new RegExp(text_regex.value);
//test filter code
eval('var f = ' + text_filter.value);
if( 'function' != typeof f ){
throw 'invalid filter function';
} else { //check return
var us = f();
if( 'object' != typeof us ){
throw 'invalid return from filter: '+us;
}
}
dataValue = f;
}catch(ex){
alert('error parsing URL Pattern or filter: ' + ex);
return false;
}
}else if(curEdit == 2){
saveTag = 'styleMap';
dataMap = cssMap;
dataValue = text_filter.value;
refreshFunction = reloadStyle;
}
//all passed, store filter
dataMap[text_regex.value] = dataValue;
//save filterMap
GM_setValue(saveTag, serializeMap(dataMap));
//reload
refreshFunction();
//tell user we've done
alert('data saved!');
//reset config list
prepareConfigList();
}
//delete CSS or filter
function deleteData(){
var dataMap = filterMap;
var saveTag = 'filters';
var refreshFunction = refreshURLs;
var confirmStr = 'Delete filter for ';
if(curEdit == 2){
saveTag = 'styleMap';
dataMap = cssMap;
refreshFunction = reloadStyle;
confirmStr = 'Delete style: ';
}
if(confirm(confirmStr + text_regex.value + ' ?')){
//there no way to remove value from map, instead we set it to null,
// and skip it in function serializeMap()
dataMap[text_regex.value] = null;
//save
GM_setValue(saveTag, serializeMap(dataMap));
//reload
refreshFunction();
//reset config list
prepareConfigList();
}
}
//convert filter config to String
function serializeMap(map){
var res = '{';
for(key in map){
if(map[key]){
var mapValue = map[key];
if(curEdit == 2){
mapValue = '"' + map[key].replace(/\n/mg,'\\n').replace(/\r/mg,'\\r') + '"';
}
res+=('"' + key + '" : ' + mapValue + ',');
}
}
res += '}';
return res;
}
//change panel color
function changeColor(){
disp.style.color = text_fg.value;
disp.style.backgroundColor = text_bg.value;
}
//addGlobalStyle -- derived from http://diveintogreasemonkey.org/patterns/add-css.html
function addGlobalStyle(css) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = css;
head.appendChild(style);
return style;
}
//register event listener for click
document.addEventListener('click', function(event) {
if(event.target.name && event.target.name.match(/^vil/)){
if(event.target.name == 'vil_next'){
next();
}else if(event.target.name == 'vil_prev'){
prev();
}else if(event.target.name == 'vil_show'){
show();
}else if(event.target.name == 'vil_piclens'){
startPicLensLite();
}else if(event.target.name == 'vil_hide'){
hide();
}else if(event.target.name == 'vil_top'){
top();
}else if(event.target.name == 'vil_end'){
end();
}else if(event.target.name == 'vil_jump'){
jumpto();
}else if(event.target.name == 'vil_color'){
changeColor();
}else if(event.target.name == 'vil_conf'){
toggle('vil_download_panel','none');
prepareConfigList();
toggle('vil_config');
}else if(event.target.name == 'vil_save'){
saveData();
}else if(event.target.name == 'vil_del'){
deleteData();
}else if(event.target.name == 'vil_download'){
toggle('vil_config','none');
prepareFileList();
toggle('vil_download_panel');
}else if(event.target.name == 'vil_browse'){
browseOutputFolder();
}else if(event.target.name == 'vil_start'){
startDownload();
}else if(event.target.name == 'vil_autocheck'){
var ac = _gel('vil_autocheck').checked;
toggle('vil_ac_config', (ac?'block':'none'));
}else if(event.target.name == 'vil_checknow'){
var ac = _gel('vil_checknow').checked;
checkUpdate(true);
}
}
}, true);
function handleError(event, imgobj, idx){
GM_log('error loading img:'+imgobj.src + "current idx: "+idx);
}
//register event listener for img.onload
img.addEventListener('load', function(event){
resize();
}, true);
img.addEventListener('error', function(event){
handleError(event, this, i);
}, true);
//register event listener for select.onchange
vil_list.addEventListener('change', function(event){
listChange();
}, true);
//register event listener for select.onchange
vil_style.addEventListener('change', function(event){
changeStyle(vil_style.value);
}, true);
//register event listener for select.onchange
vil_fit.addEventListener('change', function(event){
resize();
}, true);
//register event listener for radio.onchange
radio_filter.addEventListener('click', function(event){
curEdit = 1;
prepareConfigList();
}, true);
//register event listener for radio.onchange
radio_style.addEventListener('click', function(event){
curEdit = 2;
prepareConfigList();
}, true);
//loading animation icon
const loadingimg = '<img src="data:image/gif;base64,'+
'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" alt="loading" border="0"/>';
const defaultStyle = '#vil_control {\n' +
' position: absolute;\n' +
' right: 0px;\n' +
' top: 0px;\n' +
'}\n' +
'div.vil {\n' +
' margin: 5px;\n' +
' padding: 10px;\n' +
' z-index: 999;\n' +
' background-color: #E5ECF9;\n' +
'}\n' +
'.vil, div.vil > input, div.vil > p > textarea , div.vil > p > select, div.vil > p > input {\n' +
' color: black;\n' +
' font-style: normal;\n' +
' font-weight: 400;\n' +
' font-size: 13px;\n' +
' font-family: arial;\n' +
'}\n' +
'div.vil > p > textarea, #vil_flist {\n' +
' width: 285px;\n' +
'}\n' +
'#vil_style {\n' +
' width: 50px;\n' +
'}\n' +
'div.vil > p > select {\n' +
' width: 225px;\n' +
'}\n' +
'#vil_del {\n' +
' color: red;\n' +
' margin-left: 5px;\n' +
' width: 50px;\n' +
'}\n' +
'#vil_regex {\n' +
' margin: 5 0 5 0;\n' +
' vertical-align: middle;\n' +
' width: 210px;\n' +
'}\n' +
'#vil_save, #vil_start {\n' +
' position: absolute;\n' +
' right: 15px;\n' +
'}\n' +
'#vil_url > img {\n' +
' margin-right: 5px;\n' +
' border: 0px;\n' +
' vertical-align: middle;\n' +
'}\n' +
'#vil_img {\n' +
' margin-top: 5px;\n' +
'}\n';
//setting up style
function reloadStyle(){
cssMap = loadStyleMap();
cssName = GM_getValue('style', 'default');
var cssStyleStr = defaultStyle;
if(cssMap[cssName]){
cssStyleStr = cssMap[cssName];
}
setStyle(cssStyleStr);
//reset style list
var tmpStr = '';
for(key in cssMap){
tmpStr += '<option value="'+key+'"'+ ((key == cssName)? 'selected':'') + '>' + key + '</option>';
}
vil_style.innerHTML = tmpStr;
}
// set current style
function setStyle(cssStyleStr){
if(cssStyleElement){
cssStyleElement.innerHTML = cssStyleStr;
}else{
cssStyleElement = addGlobalStyle(cssStyleStr);
}
}
//load style map with GM_getValue();
function loadStyleMap(){
var styleSrc = GM_getValue('styleMap', 'no style defined');
if(styleSrc != 'no style defined'){
try {
eval( 'var tmpCSSMap = ' + styleSrc );
return tmpCSSMap;
} catch (err){
GM_log(styleSrc);
alert('error parsing CSS config! ' + err);
// return empty map
return {};
}
} else {
return {};
}
}
//change style - called by event listener
function changeStyle(key){
if(cssMap[key]){
cssName = key;
setStyle(cssMap[cssName]);
GM_setValue('style', cssName);
}
}
//add for PicLens support
function parseXMLtoDOM(xmlstring){
var parser = new DOMParser();
try{
return parser.parseFromString(xmlstring, 'application/xml');
}catch(err){
GM_log('error parsing xml, reason:'+err);
}
}
/**
* using current imgage url object, attributes are:
* src: image url (required)
* desc: description text of the image (optional, using image url if empty),
* thumburl: thumbnail image url (optional, using image url if empty),
* link: link url (optional, using image url if empty)
**/
function generateFeedDataForPicLens(turls){
var feedxml = parseXMLtoDOM(EMPTY_FEED_XML);
var channelNode = feedxml.documentElement.firstChild;
for(i=0;i<turls.length;i++){
var imgobj = turls[i];
var imgurl = imgobj.src;
var descval = (imgobj.desc?imgobj.desc:imgurl);
var linkurl = (imgobj.link?imgobj.link:imgurl)
var thumburl = (imgobj.thumb?imgobj.thumb:imgurl)
//GM_log("imgobj.desc:"+imgobj.desc+" imgobj.link:"+imgobj.link+" imgobj.thumb:"+imgobj.thumb);
channelNode.appendChild(generateItemXML(feedxml,thumburl,imgurl,linkurl,descval));
}
return feedxml;
}
// generate item node
function generateItemXML(xmldoc, thumburl, imgurl , linkurl, descval){
var itemNode = xmldoc.createElement('item');
var titleNode = xmldoc.createElement('title');
var titleNodeText = xmldoc.createTextNode(descval);
titleNode.appendChild(titleNodeText);
var linkNode = xmldoc.createElement('link');
var linkNodeText = xmldoc.createTextNode(linkurl);
linkNode.appendChild(linkNodeText);
var media_title = xmldoc.createElement('media:title');
media_title.setAttribute('type','plain');
media_title.appendChild(titleNodeText);
var media_thumbnail = xmldoc.createElement('media:thumbnail');
media_thumbnail.setAttribute('url',thumburl);
var media_content = xmldoc.createElement('media:content');
media_content.setAttribute('url',imgurl);
var media_description = xmldoc.createElement('media:description');
media_description.setAttribute('type',"plain");
media_description.appendChild(titleNodeText);
var guidNode = xmldoc.createElement('guid');
guidNode.appendChild(linkNodeText);
itemNode.appendChild(titleNode);
itemNode.appendChild(linkNode);
itemNode.appendChild(media_title);
itemNode.appendChild(media_thumbnail);
itemNode.appendChild(media_content);
itemNode.appendChild(media_description);
itemNode.appendChild(guidNode);
return itemNode;
}
// load PicLens API
function loadPicLensAPI(){
if (typeof unsafeWindow.PicLensLite == 'undefined'){
GM_log('PicLensLite object not found, insert <script> tag ...');
var head = document.getElementsByTagName('head')[0];
if(!head){ // head element not found ?!
head = document.createElement('head');
document.documentElement.insertBefore(head,document.body);
}
var stag = document.createElement('script');
stag.setAttribute('type', 'text/javascript');
stag.setAttribute('src', PICLENS_API_URL);
head.appendChild(stag);
}
}
// start!
function startPicLensLite(){
if (typeof unsafeWindow.PicLensLite == 'undefined'){
GM_log('PicLensLite object not found, waiting ...');
window.setTimeout(startPicLensLite, 1000);
}else{
var hasClient = unsafeWindow.PicLensLite.hasPicLensClient();
GM_log('PicLensLite.hasPicLensClient() = '+hasClient);
var temprss = false;
if(hasClient && (temprss = writeTempFeedFile(piclensfeeddata)) ){
unsafeWindow.PicLensLite.start({feedUrl:temprss});
}else{
var feedtext = serializeXMLtoString(piclensfeeddata);
unsafeWindow.PicLensLite.start({feedData:feedtext});
}
}
}
//serialize XML to String
function serializeXMLtoString(xmldoc){
var se = new XMLSerializer();
return se.serializeToString(xmldoc);
}
//thanks to tiddly wiki source..:p
function writeTempFeedFile(feeddata){
var filePath;
try{
unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
filePath = writeToFile(manualConvertUnicodeToUTF8(serializeXMLtoString(feeddata)), null, 'gm_vil_temp_rss.xml');
}catch(ex){
alert("Oops! I can't create temp feed file for PicLens extension,\n"+
"I will use Lite version instead...\n"+
"you can set \"signed.applets.codebase_principal_support\" = true in \"about:config\""
+"to avoid this problem.");
GM_log("error creating file:"+filePath+", reason:"+ex);
}
return filePath;
}
function writeToFile(content, outputPath, fname){
var filePath;
if(Components) {
// ask user permission
//unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var fileObj;
if(!outputPath){
// get home directory
fileObj= Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("Home", Components.interfaces.nsILocalFile);
}else{
fileObj = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
fileObj.initWithPath(outputPath);
}
// append file name
fileObj.append(fname);
// file is nsIFile
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var fileHandler = ios.getProtocolHandler("file")
.QueryInterface(Components.interfaces.nsIFileProtocolHandler);
filePath = fileHandler.getURLSpecFromFile(fileObj);
// get file:// URL
GM_log("filePath:"+filePath);
if(!fileObj.exists())
//0 for File, 0600 is permission
fileObj.create(0,0600);
//prepare output stream
var out = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
out.init(fileObj,0x20|0x02,00004,null);
//it's very strange that the PicLens extension refuse to load feed which contains UTF-8 chinese character,
//manualConvertUnicodeToUTF8 will convert unicode character to &#nnnn notation.
out.write(content,content.length);
out.flush();
out.close();
return filePath;
}
return false;
}
//using XPCOM converter service
function mozConvertUnicodeToUTF8(s)
{
//unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
try {
var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
} catch(ex) {
GM_log("error convert from mozConvertUnicodeToUTF8:"+ex);
return manualConvertUnicodeToUTF8(s);
} // fallback
var u = converter.ConvertFromUnicode(s);
var fin = converter.Finish();
return fin.length > 0 ? u + fin : u;
}
//convert unicode to &#nnnnn;
function manualConvertUnicodeToUTF8(s)
{
var re = /[^\u0000-\u007F]/g ;
return s.replace(re,function($0) {return "&#" + $0.charCodeAt(0).toString() + ";";});
}
//
function downloadFile(httpLoc, outputFolder, localfile, listener, flag) {
try {
//new obj_URI object
var obj_URI = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI(httpLoc, null, null);
//new file object
var obj_TargetFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
//set file with path
obj_TargetFile.initWithPath(outputFolder);
obj_TargetFile.append(localfile);
//if file doesn't exist, create
if(!obj_TargetFile.exists()) {
obj_TargetFile.create(0x00,0644);
}
//new persitence object
var obj_Persist = Components.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].createInstance(Components.interfaces.nsIWebBrowserPersist);
obj_Persist.persistFlags = Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
obj_Persist.persistFlags |= Components.interfaces.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
if(flag){
obj_Persist.persistFlags |= flag;
}
//save file to target
obj_Persist.progressListener = (listener?listener:defaultProgressListener);
obj_Persist.saveURI(obj_URI,null,null,null,null,obj_TargetFile);
} catch (e) {
alert(e);
}
}
function prepareFileList(){
var obj = _gel('vil_flist');
var opts = obj.options;
for(var j=0;j<urls.length;j++){
var fn = urls[j].src.replace(/^.*\//,'');
var label = fn;
if(urls[j].desc != urls[j].src){
label += ' : ' + urls[j].desc;
}
opts[j] = new Option(label,fn);
}
}
function browseOutputFolder(){
try{
unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
const nsIFilePicker = Components.interfaces.nsIFilePicker;
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, "Album Output Folder", nsIFilePicker.modeGetFolder);
var rv = fp.show();
if (rv == nsIFilePicker.returnOK) {
_gel('vil_output_path').value = fp.file.path;
}
}catch(ex){
alert("Oops! Can't open FilePicker dialog,\n"+
"you can set \"signed.applets.codebase_principal_support\" = true in \"about:config\""
+"to avoid this problem.");
GM_log("error init FilePicker, reason:"+ex);
}
}
var downloadIdx = 0;
var defaultProgressListener = {
onStateChange: function(awsp, areq, aStateflag, astatus){
if(aStateflag & Components.interfaces.nsIWebProgressListener.STATE_STOP){ //finished
var opts = _gel('vil_flist').options;
if(downloadIdx+1 != urls.length){
if(opts[downloadIdx]){
opts[downloadIdx].selected = true;
}
downloadIdx++;
//GM_log('start download idx:' + downloadIdx);
}else{
if(opts[downloadIdx]){
opts[downloadIdx].selected = true;
}
alert('all files downloaded!');
}
}
},
onProgressChange: function(){}, //do nothing
onStatusChange: function(){}, //do nothing
onLocationChange: function(){}, //do nothing
onSecurityChange: function(){} //do nothing
}
function startDownload(){
alert('DO NOT check "remember my decision" on next confirm dialog unless you know how to remove them.');
try {
unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
downloadIdx = 0;
if(_gel('check_save_index').checked){
try{
writeToFile(generateIndexText(), _gel('vil_output_path').value, "index.html");
}catch(ex){
alert('error writing index, reason: '+ex);
}
}
for(var j=0;j<urls.length;j++){
doDownload(j);
}
}catch(ex){
alert("Oops! Can't open local directory\n"+
"you can set \"signed.applets.codebase_principal_support\" = true in \"about:config\""
+"to avoid this problem.");
GM_log("error init download, reason:"+ex);
}
}
function doDownload(idx){
var ofolder = _gel('vil_output_path').value;
if(!ofolder){
alert('please select output folder first!');
return false;
}
if(urls[idx]){
var opts = _gel('vil_flist').options;
var src = urls[idx].src;
var fn = opts[idx].value;
downloadFile(src,ofolder,fn);
}
}
function generateIndexText(){
var idxdata = '<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>';
var opts = _gel('vil_flist').options;
for(var j=0;j<urls.length;j++){
idxdata += '<img src="'+opts[j].value+'">' + urls[j].desc + '\n';
}
idxdata += '</body></html>';
//GM_log(mozConvertUnicodeToUTF8(idxdata));
return mozConvertUnicodeToUTF8(idxdata);
}
function _gel(tid){
return document.getElementById(tid);
}
function checkUpdate(forcechk){
//last check time as long
var lastcheck = parseInt(GM_getValue('last_check','0'));
GM_log('last check is '+lastcheck);
var filterlastcheck = parseInt(GM_getValue('filter_last_check','0'));
GM_log('filter last check is '+filterlastcheck);
//days
var checkInterval = forcechk?0:parseInt(GM_getValue('check_interval','1'));
var nowtime = (new Date()).getTime();
//start check if over interval
if(checkInterval >= 0 && (nowtime - lastcheck)>(checkInterval * 86400000)){
GM_log('start checking from '+UPDATE_URL);
sendHTTPRequest(UPDATE_URL,'HEAD', {'If-None-Match': 'never match'},function(rspDtls){
var checked = onCheckRevSuccess(rspDtls,'revision',startUpdateProcess, forcechk);
if(checked){
GM_setValue('last_check', ''+nowtime);
}
});
}
if(checkInterval >= 0 && (nowtime - filterlastcheck)>(checkInterval * 86400000)){
GM_log('start checking from '+FILTER_URL);
sendHTTPRequest(FILTER_URL,'GET', {'If-None-Match': 'never match'},function(rspDtls){
var checked = onCheckRevSuccess(rspDtls,'filter.revision',startFilterUpdateProcess,forcechk);
if(checked){
GM_setValue('filter_last_check', ''+nowtime);
}
});
}
}
function onCheckRevSuccess(rspDtls, revtag, updateFunction, showOptMessage) {
if('200' != rspDtls.status){
GM_log('check failed, response HTTP status: '+rspDtls.status);
return;
}else{
var myrev = parseInt(GM_getValue(revtag,'0'));
var remoterev = parseInt(rspDtls.responseHeaders.match(/Etag:\s+"([0-9]+).*"/)[1]);
GM_log('remote rev is ' + remoterev + ', current rev is ' + myrev);
if(myrev < remoterev){
return updateFunction(rspDtls, remoterev);
}else{
if(showOptMessage){
alert('remote rev is ' + remoterev + ', my rev is ' + myrev + ', no need to update.');
}
return true;
}
}
}
function sendHTTPRequest(targeturl, reqMethod, headerMap, onSuccess){
GM_xmlhttpRequest({
method: reqMethod,
url: targeturl,
headers: headerMap,
onload: onSuccess
});
}
function startFilterUpdateProcess(responseDetail, remoterev){
if(!confirm('new filter configs found (rev: ' + remoterev + ' )\nUpdate now?')){
GM_setValue('filter_last_check',''+(new Date()).getTime());
return true;
}
try {
//looking up gm_script dir
eval('var remoteFilters = ' + responseDetail.responseText);
if(filterMap.length == 0){
filterMap = loadFilterMap();
}
GM_log('got filters :'+responseDetail.responseText);
for(var key in remoteFilters){
var remote = remoteFilters[key];
GM_log('remote filter key:'+key + 'filter: '+remote.filter.toString());
if(filterMap[key]){
if(filterMap[key].toString() != remote.filter.toString()){ //exists
if(confirm('filter for '+key+' is exist, overwrite? (new filter will be logged to console if choose cancel)')){
filterMap[key] = remote.filter;
}else{
GM_log('new filter for '+key+' is \n'+remote.filter.toString());
}
}
}else{
alert('got new filter for '+key+', please add "'+remote.include+'" to "included pages" of greasemonkey');
GM_log('got new filter for '+key+', please add "'+remote.include+'" to "included pages" of greasemonkey');
filterMap[key] = remote.filter;
}
}
GM_setValue('filters', serializeMap(filterMap));
GM_setValue('filter.revision',remoterev);
alert('filter updated successful! reload page to see changes!');
return true;
}catch(ex){
alert('update filter failed! reason:' + ex);
}
return false;
}
function startUpdateProcess(responseDetail, remoterev){
if(!confirm('new revision found (rev: ' + remoterev + ' )\nUpdate now?')){
GM_setValue('last_check',''+(new Date()).getTime());
return true;
}
alert('Please Allow Privilege request to start update process.\nDO NOT check "remember my decision" on next confirm dialog unless you know how to remove them.');
try {
unsafeWindow.netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
//looking up gm_script dir
var fname = getInstalledFileName();
var updateProgressListener = {
onStateChange: function(awsp, areq, aStateflag, astatus){
if(aStateflag & Components.interfaces.nsIWebProgressListener.STATE_STOP){ //finished
GM_setValue('last_check',''+(new Date()).getTime());
GM_setValue('revision',remoterev);
alert('script updated, reload to see change!');
}
},
onProgressChange: function(){}, //do nothing
onStatusChange: function(){}, //do nothing
onLocationChange: function(){}, //do nothing
onSecurityChange: function(){} //do nothing
}
//download and bypass cache
downloadFile(UPDATE_URL, getScriptDir().path, fname, updateProgressListener, 2);
return true;
}catch(ex){
alert('update failed! reason:' + ex);
}
return false;
}
function getInstalledFileName(){
var installedFilename = '';
var configContents = getContents(getScriptFileURI("config.xml"));
var domParser = new DOMParser();
var doc = domParser.parseFromString(configContents, "text/xml");
var nodes = doc.evaluate("/UserScriptConfig/Script", doc, null, 0, null);
for (var node = null; (node = nodes.iterateNext()); ) {
var fname = node.getAttribute("filename");
var name = node.getAttribute("name");
var namespace = node.getAttribute("namespace");
if(name == MY_NAME && namespace == MY_NAMESPACE){
installedFilename = fname;
break;
}
}
return installedFilename;
}
function getContents(aURL, charset){
if( !charset ) {
charset = "UTF-8"
}
var ioService=Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var scriptableStream=Components
.classes["@mozilla.org/scriptableinputstream;1"]
.getService(Components.interfaces.nsIScriptableInputStream);
// http://lxr.mozilla.org/mozilla/source/intl/uconv/idl/nsIScriptableUConv.idl
var unicodeConverter = Components
.classes["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
unicodeConverter.charset = charset;
var channel=ioService.newChannelFromURI(aURL);
var input=channel.open();
scriptableStream.init(input);
var str=scriptableStream.read(input.available());
scriptableStream.close();
input.close();
try {
return unicodeConverter.ConvertToUnicode(str);
} catch( e ) {
return str;
}
}
function getScriptFileURI(fileName) {
return Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newFileURI(getScriptFile(fileName));
}
function getScriptFile(fileName) {
var file = getScriptDir();
file.append(fileName);
return file;
}
function getScriptDir() {
var dir = getNewScriptDir();
if (dir.exists()) {
return dir;
} else {
var oldDir = getOldScriptDir();
if (oldDir.exists()) {
return oldDir;
} else {
// if we called this function, we want a script dir.
// but, at this branch, neither the old nor new exists, so create one
return GM_createScriptsDir(dir);
}
}
}
function getNewScriptDir() {
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsILocalFile);
file.append("gm_scripts");
return file;
}
function getOldScriptDir() {
var file = getContentDir();
file.append("scripts");
return file;
}
function getContentDir() {
var reg = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIChromeRegistry);
var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var proto = Components.classes["@mozilla.org/network/protocol;1?name=file"]
.getService(Components.interfaces.nsIFileProtocolHandler);
var chromeURL = ioSvc.newURI("chrome://greasemonkey/content", null, null);
var fileURL = reg.convertChromeURL(chromeURL);
var file = proto.getFileFromURLSpec(fileURL.spec).parent;
return file
}
// load CSS style map and last-used theme
var cssMap = {};
var cssName = 'default';
//add style to
var cssStyleElement = null;
reloadStyle();
})();//end mark for userscript