Greasy Fork is available in English.

GM_option

support tool for UserConfig.(library)

このスクリプトは単体で利用できません。右のようなメタデータを含むスクリプトから、ライブラリとして読み込まれます: // @require https://update.greasyfork.org/scripts/9507/151184/GM_option.js

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name        GM_option
// @namespace   https://greasyfork.org/ja/scripts/9507
// @homepageURL https://greasyfork.org/ja/scripts/9507
// @license     http://creativecommons.org/licenses/by-nc-sa/4.0/
// @include     http://*
// @include     https://*
// @include     file:*
// @copyright   Noi & Noisys & NoiSystem & NoiProject
// @author      noi
// @description support tool for UserConfig.(library)
// @version     1.04
// @grant       GM_registerMenuCommand
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_deleteValue
// @grant       GM_log
// ==/UserScript==

/*************************************************************************
[about]

This script is the library to make a setting screen.
I made this support tool in order to make more easily.

*************************************************************************
[How to use]

Sample code is end of this Script.
Plz test it.


If savedata is none....?
<first start-up or no savedata or parameter(HTML tag) changed>
This script make GM_option window and save default.
<next time>
This script never make GM_option window without running open-function.

<@grant>----------------------------------------------
you need to add @grant.
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_deleteValue
// @grant       GM_registerMenuCommand

GM_setValue and GM_getValue and GM_deleteValue is required.
GM_registerMenuCommand is optional.


<start function>----------------------------------------------
GM_option.open(String,String,HashArray);

first String is HTML Tag(header ex: style or script or etc...)
second String is HTML Tag(document.body).
third HashArray is optional,it's messages for dialog.

Message List
'save'   -  when click save button.
'reset'  -  when click reset button.(discard changes)
'clear'  -  when click clear button.(delete savedata)
'delete' -  when click del button.(delete multiple)


<user function>--------------------------------------
At first,get user's settings.
GM_option.get();

return hashArray(Associative array).


<HTML tag>-------------------------------------------
use form inputs.
'name' is save-key.
'value' is save-data.

ex)
HTML tag is...
<input type="text" name="this_is_Key" value="this_is_Data" />

Save Data is...
{"this_is_Key":"this_is_Data"}


<multiple text>--------------------------------------
'name' is ****_multiple9999.(required)
9999 is number of array.(required)
'value' is default.(optional)
'defArray' is default array.(optional)
<button id="this_is_required"> is addButton.(required)

ex)
HTML tag is...
<input type="text" name="freeName_multiple2" value="data1" /> <input type="text" name="freeName_multiple2" value="data2" /> <button id="addButton">add</button>

Save Data is...
{"freeName_multiple2":[["data1","data2"],["data3","data4"], and more ]}
"data3" and "data4" are input by user.

ex2)
HTML tag is...
<input type="text" name="freeName_multiple2" defArray="data1-2,data1-3,data1-4" value="data1-1" /> <input type="text" name="freeName_multiple2" defArray="data2-2,data2-3,data2-4" value="data2-1" /> <button id="addButton">add</button>

Save Data is...
{"freeName_multiple2":[["data1-1","data2-1"],["data1-2","data2-2"],["data1-3","data2-3"],["data1-4","data2-4"],["data1-5","data2-5"], and more ]}
"data1-5" and "data2-5" are input by user.

## caution ##
If elements's 'name' are same, defArray are same array.length.

<select-multiple(HTML5)>----------------------------
selected is checked.(optional)

ex)
HTML tag is...
<select name="selectName" multiple size=5>
<option name="optionName1" value="data1"         >item1</option>
<option name="optionName2" value="data2" selected>item2</option>
<option name="optionName3" value="data3"         >item3</option>
<option name="optionName4" value="data4" selected>item4</option>
<option name="optionName5" value="data5"         >item5</option>

Save Data is...
{"selectName":"{\"data2\":true,\"data4\":true}"}

To use...
var userSettings = GM_option.get();
var selectArray = JSON.parse(userSettings['selectName']);


<addEventListener>----------------------------------
'GM_option_loaded'

this event is dispatched...
when made GM_option frame                    (@first start-up)
when loaded User's Settings from GM_getValue (@next time)

ex)
window.addEventListener('GM_option_loaded',userFunction,false);


<access GM_option document>----------------------------------
GM_option.frame
GM_option.doc

ex)
var frame = GM_option.frame;
var doc = GM_option.doc.head;
var head = GM_option.doc.body;
var form = GM_option.doc.opForm;


<other function>----------------------------------
after GM_option opened
GM_option.show();  - show GM_option
GM_option.close(); - hide GM_option
GM_option.save();  - save settings & reload
GM_option.reset(); - discard changes
GM_option.clear(); - delete SaveData & reload


*************************************************************************
[history]

10/07/2016 - v1.04 add:multiple設定でreadonly属性の引き継ぎ
04/30/2015 - v1.03 add:sandbox
04/29/2015 - v1.01 add:第二引数(body)に変更があった場合は変更箇所の初期値を保存する。初期値配列の指定方法追加。
04/29/2015 - v1.01 fix:入力欄でENTERキーを押すとaddボタンを押した時と同じ動作をするバグ。第一引数(head)にスクリプトタグを指定しても動作しないバグ修正。resetボタン修正
04/26/2015 - v1.0  release
*************************************************************************/

var GM_option = {
	init: function(){
		GM_option.defScroll = window.onscroll;	//save default scroll
		var head = GM_option.head;
		var htmlText = GM_option.htmlText;

		//iframe
		var obj = GM_option.frame = window.document.createElement('iframe');
		obj.id = 'GM_option';
		obj.srcdoc = '<head>' + GM_option.head + '</head><body style="background:white;">' + GM_option.htmlText + '</body>';
		obj.setAttribute('style','position:fixed;top:0;left:0;right:0;bottom:0; margin:auto; z-index:999999;width:calc(100% - 40px);height:calc(100% - 60px); max-height:90%; max-width:90%; overflow:auto; visibility:hidden; border:none;');
		obj.sandbox = "allow-same-origin  allow-scripts";
		document.body.appendChild(obj);

		obj.addEventListener('load', GM_option.make(head,htmlText) ,false);
	},
	open: function(head,htmlText,msgArray){
		GM_option.get();
		if(htmlText) GM_option.htmlTextCheck(htmlText);

		var oType = Object.prototype.toString.call(htmlText).slice(8, -1);
		if(!htmlText && htmlText != 'String' && !htmlText.match(/\<.*?\>/)){
			alert('second parameter(htmltext) is not correct.');
			return;
		} 
		if(!GM_option.event){
			GM_option.event = document.createEvent('HTMLEvents');
		        GM_option.event.initEvent('GM_option_loaded', true, false);

			GM_option.msgArray = msgArray;
		}

		if(!GM_option.makeFlg && GM_option.saveData){
			GM_option.makeFlg = true;
			
			window.dispatchEvent(GM_option.event);
			return;
		}

		GM_option.head = head || '';
		GM_option.htmlText = htmlText = [
			'<form name="opForm" action="javascript:void(0);" onSubmit="return false;">',
			'<table id="opTable" style="padding:20px;height:98%;width:98%;"><tr><td align="center" valign="middle">',
			htmlText,
			'<span id="consol">',
			'<input type="button" id="saveConfig" value="save" title="save option" /> ',
			'<input type="button" id="resetConfig" value="reset" title="Revert To Last Save" /> ',
			'<input type="button" id="clearConfig" value="clear" title="clear userSettings" /> ',
			'<button id="closeConfig" title="close option window">close</button>',
			'</span>',
			'</td></tr></table></form>'
		].join('');

		if(htmlText && (!GM_option.frame || !GM_option.layer)) GM_option.init();
		GM_option.show();
	},
	make: function() { return function(){
		var head = GM_option.head;
		var htmlText = GM_option.htmlText;

		this.removeEventListener("load", GM_option.make(head,htmlText) ,false);

		var opDoc = GM_option.doc = this.contentDocument;

		//close
		var closeButton = opDoc.getElementById('closeConfig');
		closeButton.addEventListener('click',GM_option.close,false);

		//save
		var saveButton = opDoc.getElementById('saveConfig');
		saveButton.addEventListener('click',GM_option.save,false);

		//clear
		var defaultButton = opDoc.getElementById('clearConfig');
		defaultButton.addEventListener('click',GM_option.clear,false);

		//reset
		var resetButton = opDoc.getElementById('resetConfig');
		resetButton.addEventListener('click',GM_option.reset,false);

		GM_option.load();	//デフォルト読み込み
		if(!GM_option.saveData || GM_option.htmlChangeFlg) GM_option.save();
		

		//layer
		var layer = GM_option.layer = window.document.createElement('div');
		layer.id = 'opLayer';
		layer.setAttribute('style','position:fixed; top:0px; left:0px; width:100%; height:100%; background-color:black; z-index:5000; opacity:0.7;');
		document.body.appendChild(layer);
		
		layer.addEventListener('click', GM_option.close,false);

		window.addEventListener('beforeunload', function() {
			window.removeEventListener("beforeunload", arguments.callee,false);
			layer.removeEventListener('click', GM_option.close,false);
			closeButton.removeEventListener('click',GM_option.close,false);
			saveButton.removeEventListener('click',GM_option.save,false);
			defaultButton.removeEventListener('click',GM_option.clear,false);
			resetButton.removeEventListener('click',GM_option.reset,false);

			GM_option.remove();
		},false);

		if(GM_option.makeFlg) GM_option.show();
		else{
			GM_option.load();
			GM_option.close();
			
			window.dispatchEvent(GM_option.event);
		}
		GM_option.makeFlg = true;

		opDoc = style = head = htmlText = null;
	}},
	show: function(){
		if(!GM_option.frame || !GM_option.layer) return;
		GM_option.scrollStop(true);
		GM_option.frame.style.visibility = 'visible';
		GM_option.layer.style.visibility = 'visible';
	},
	save: function(clickFlg){
		if(clickFlg){
			var txt = 'Do you want to save?(ok:reload)';
			if(GM_option.msgArray) txt = GM_option.msgArray['save'] || txt;
			if(confirm(txt) == false) return
		}

		var userData = GM_option.get();
		var form = GM_option.doc.opForm;
		var array={};
		var multiArray = {};
		for(var i=0,j=form.length;i<j;i++){
			var obj = form[i];
			var name =obj.name;
			if(!name) continue;
			switch(obj.type){
				case 'checkbox':
					if(obj.checked)array[name] = true;
					else array[name] = false;
					continue;
				case 'radio':
					if(obj.checked)array[name] = obj.value;
					continue;
				case 'text':
					if(!name.match(/_multiple(\d+)$/)){
						array[name] = obj.value;
						continue;
					}else{
						var num = RegExp.$1;
						if(!array[name])array[name] = [];
						var arrayNum = array[name].length - 1;
						if(arrayNum < 0) arrayNum = 0;

						if(!array[name][arrayNum]) array[name][arrayNum] = [];
						if(array[name][arrayNum].length < num) array[name][arrayNum].push(obj.value);
						else{
							arrayNum++;
							if(!array[name][arrayNum]) array[name].push([]);
							array[name][arrayNum].push(obj.value);
						}

						if(!obj.hasAttribute('defArray') || (userData && userData[name])) continue;

						if(!multiArray[name]) multiArray[name] = [];
						multiArray[name].push(obj.getAttribute('defArray'));

						continue;
					}
					continue;
				case 'select-one':
					var opt = obj.getElementsByTagName('option');
					for(var op=0,opLength=opt.length;op<opLength;op++){
						var optObj = opt[op];
						if(optObj.selected){ array[name] = optObj.value; break;}
					}
					continue;
				case 'select-multiple':
					var opt = obj.getElementsByTagName('option');
					var tmpArray = {};
					for(var op=0,opLength=opt.length;op<opLength;op++){
						var optObj = opt[op];
						if(optObj.selected) tmpArray[optObj.value] = true;
					}
					array[name] = JSON.stringify(tmpArray);
					continue;
				default:

					array[name] = obj.value;
					continue;
			}
		}
		//example) multiArray is {"textTest5_5_multiple3":["aa,22,33,44","bb,44,55,66","cc,88,99,00"]}
		//want to change "textTest5_5_multiple3" to "textTest5_5_multiple3":[["dataA","Multi test2","Multi testA"],["aa","bb","cc"],["22","44","88"],["33","55","99"],["44","66","00"]]
		for(var key in multiArray){
			var num = parseInt(key.replace(/.*_multiple(\d+)$/,"$1"));
			var multiList = multiArray[key];
			for(var i=0,j=multiList.length;i<j;i++){
				var multiData = multiList[i].split(',');
				for(var x=0,y=multiData.length;x<y;x++){
					var next = x+1;
					if(!array[key][next]) array[key][next] = [];
					array[key][next][i] = multiData[x];
				}
			}
		}

		GM_option.saveData = JSON.stringify(array);

		GM_setValue('opData',GM_option.saveData);
		if(clickFlg) location.reload();
	},
	//discard changes
	reset: function(){
		var txt = 'discard changes?';
		if(GM_option.msgArray) txt = GM_option.msgArray['reset'] || txt;
		if(confirm(txt) == false) return;

		var dels = GM_option.doc.getElementsByClassName('delSpans');
		for (var i = dels.length-1; i >= 0; i--) {
			dels[i].parentNode.removeChild(dels[i]);
		}
		GM_option.doc.opForm.reset();
		GM_option.load();

	},
	//delete SaveData
	clear: function(){
		var txt = 'delete settings?(ok:reload)';
		if(GM_option.msgArray) txt = GM_option.msgArray['clear'] || txt;
		if(confirm(txt) == false) return;

		GM_deleteValue('opData');
		GM_option.close();
		location.reload();
	},
	//delete line for Multiple
	deleteLine: function(e){
		var txt = 'delete this line?';
		if(GM_option.msgArray) txt = GM_option.msgArray['delete'] || txt;
		if(confirm(txt) == false) return;

		e.target.removeEventListener("click", arguments.callee,false);
		e.target.parentNode.parentNode.removeChild(e.target.parentNode);
	},
	//GM_option.saveData is String,return object Array
	get: function(){
		if(GM_option.saveData) return JSON.parse(GM_option.saveData);
		if(!GM_getValue('opData')) return undefined;

		GM_option.saveData = GM_getValue('opData');
		return JSON.parse(GM_option.saveData);
	},
	//set data
	load: function(){
		var userSet = GM_option.get();
		if(userSet == undefined) return;
		var form = GM_option.doc.opForm;
		var lastName = "";
		var cntMulti = 0;
		var multipleArray = {};

		for(var i=0,j=form.length;i<j;i++){
			var obj = form[i];
			var name = obj.name;
			var readonly = '';
			if(obj.getAttribute('readonly') == 'readonly') readonly = 'readonly="readonly"';
			var userData = userSet[name];
			if(userData == undefined) continue;
			switch(obj.type){
				case 'checkbox':
					if(userData)obj.setAttribute('checked',true);
					else obj.removeAttribute('checked');
					continue;
				case 'radio':
					if(obj.value == userData)obj.setAttribute('checked',true);
					else obj.removeAttribute('checked');
					continue;
				case 'textarea':
					obj.innerHTML = obj.value = userData;
					continue;
				case 'text':
					if(!name.match(/_multiple(\d+)$/)){
						obj.setAttribute('value',userData);
						continue;
					}else{
						var num = RegExp.$1;
						if(lastName != name){
							lastName = name;
							cntMulti = 0;
						}


						obj.setAttribute('value',userData[Math.floor(cntMulti / num)][cntMulti % num]);

						cntMulti++;

						if(num == cntMulti){
							var addObj = obj.parentNode.getElementsByTagName('button');
							if(!addObj[0]){
								alert('addButton is nothing for multiple select.');
								GM_option.remove();
								return;
							}
							var id = addObj[0].id;

							GM_option.addMultiple(name,num,id,readonly);
							j = form.length;
						}


						continue;
					}
					continue;
				case 'select-one':
					var opt = obj.getElementsByTagName('option');
					for(var op=0,opLength=opt.length;op<opLength;op++){
						var optObj = opt[op];
						if(optObj.value == userData) optObj.setAttribute('selected',true);
						else optObj.removeAttribute('selected');
					}
					continue;
				case 'select-multiple':
					var tmpArray = JSON.parse(userData);
					var opt = obj.getElementsByTagName('option');
					for(var op=0,opLength=opt.length;op<opLength;op++){
						var optObj = opt[op];
						if(tmpArray[optObj.value]) optObj.setAttribute('selected',true);
						else optObj.removeAttribute('selected');
					}
					continue;
				default:
					obj.setAttribute('value',userData);
					continue;
			}
		}
	},
	close: function(){
		GM_option.scrollStop(false); 
		if(!GM_option.frame) return;

		GM_option.frame.style.visibility = 'hidden';
		if(!GM_option.layer) return;
		GM_option.layer.style.visibility = 'hidden';
	},
	remove: function(){
		if(GM_option.frame) document.body.removeChild(GM_option.frame);
		if(GM_option.layer) document.body.removeChild(GM_option.layer);
		GM_option = null;
	},
	scrollStop: function(stopFlg){
		GM_option.scrollY = document.documentElement.scrollTop || document.body.scrollTop;

		if(stopFlg) window.onscroll = function () { window.scrollTo(0, GM_option.scrollY); };
		else window.onscroll = GM_option.defScroll;
	},
	//make line for Multiple
	addLine: function(obj,txt,addID){
		var num = parseInt(obj.getAttribute('num')) || 0;
		var strID = addID + '_delItem' + num;
		if(GM_option.doc.getElementById(strID)) return false;
		obj.parentNode.insertAdjacentHTML('beforeend',txt + '<button id="' + strID + '">del</button></span>');

		//del button
		var delButton = GM_option.doc.getElementById(strID);
		delButton.addEventListener('click',GM_option.deleteLine,true);
		window.addEventListener('beforeunload', function() {
			window.removeEventListener("beforeunload", arguments.callee,false);
			delButton.removeEventListener('click',GM_option.deleteLine,true);
		},false);


		obj.setAttribute('num',num + 1);
		return true;
	},
	//Multiple main
	addMultiple: function(name,num,addID,readonly){
		var addButton = GM_option.doc.getElementById(addID);
		var strTxt = '<span class="delSpans"><br>';
		for(var x=0;x<num;x++){
			strTxt += '<input type="text" name="' + name + '" ' + readonly + ' /> ';
		}

		//addEvent
		if(!addButton.hasAttribute('num')){
			var addEvent = function(e){
				GM_option.addLine(e.target,strTxt,addID);
			};
			addButton.addEventListener('mouseup',addEvent,false);
			window.addEventListener('beforeunload', function() {
				window.removeEventListener("beforeunload", arguments.callee,false);
				addButton.removeEventListener('click',addEvent,false);
			},false);
		}

		//addInput(複数選択肢)
		var testArray = GM_option.get()[name];
		addButton.setAttribute('num',1);
		for(var i=1,j=testArray.length;i<j;i++){
			if(!GM_option.addLine(addButton,strTxt,addID)) break;
		}
	},
	htmlTextCheck: function(htmlText){
		var txt = GM_getValue('htmlText');
		if(!txt || (txt && txt != htmlText)){
			GM_setValue('htmlText',htmlText);
			if(!txt) return;
			GM_option.saveData = null;
			GM_option.htmlChangeFlg = true;
		}
	}
};



//sample code
/*********************************************************************************************************************


//sample header(ex:style, script etc...)
const strHeader = [
	'<style id ="cfgCSS" type="text/css">',
	'#cfgTable{ border: solid #ccc 1px; border-spacing:0; border-radius:6px 6px 0 0; -webkit-border-radius:6px 6px 0 0; box-shadow: 0 1px 1px #ccc; -webkit-box-shadow: 0 1px 1px #ccc; }',
	'#cfgTable tr:hover{ transition: all 0.1s ease-in-out; background:#fbf8e9; -webkit-transition: all 0.1s ease-in-out; }',
	'#cfgTable th, #cfgTable td{ border-left: 1px solid #ccc; border-top: 1px solid #ccc; vertical-align: top; white-space: nowrap; padding: 3px; }',
	'#cfgTable th{ color: #151; background: #E3F6E7;}',
	'#cfgTitle, #cfgTable th:first-child, #cfgTable td:first-child { border-left: none;}',
	'#cfgTable tr:first-child th:first-child{ border-top: none; color: #151; border-bottom: 3px solid #036; background: #A0D0A0; padding: 6px; }',
	'#cfgTable label{ padding:1px 5px 2px 5px; }',
	'#cfgTable label:hover{ background:#ada; }',
	'</style>'
].join('');


//sample HTML
var strHTML = [
'<table id="cfgTable" align="center">',
 '<thead>',
 '<tr>',
  '<th id="cfgTitle" colspan=2>Config Title</th>',
 '</tr>',
 '<tr>',
  '<th>Settings</th>',
  '<th>Select</th>',
 '</tr>',
 '</thead>',
 '<tbody>',
 '<tr>',
  '<td>',
   'checkbox test',
  '</td>',
  '<td>',
   '<input type="checkbox" id="flg1" name="check1" checked />',
   '<label for="flg1">check1</label>',
   '<input type="checkbox" id="flg2" name="check2" />',
   '<label for="flg2">check2</label>',
   '<input type="checkbox" id="flg3" name="check3" />',
   '<label for="flg3">check3</label>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>',
   'radiobutton test1',
  '</td>',
  '<td>',
   '<input type="radio" id="firstOn" name="radio1" value=true checked />',
   '<label for="firstOn">on</label>',
   '<input type="radio" id="firstOff" name="radio1" value=false />',
   '<label for="firstOff">off</label>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>',
   'radiobutton test2',
  '</td>',
  '<td>',
   '<input type="radio" id="secondOn" name="radio2" value="1" />',
   '<label for="secondOn">type1</label>',
   '<input type="radio" id="secondOff" name="radio2" value="2" checked />',
   '<label for="secondOff">type2</label>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) test</td>',
  '<td>',
   '<input type="text" name="textTest1" value="input text" />',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>textarea test</td>',
  '<td>',
   '<textarea name="textarea">textarea test\rthis\ris\rsample\rdata</textarea>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) for Multiple test</td>',
  '<td>',
   '<input type="text" name="textTest3_multiple1" value="test" /> <button id="addItem">add</button>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) for Multiple test1</td>',
  '<td>',
   '<input type="text" name="textTest3_3_multiple1" defArray="BB,CC,DD" value="AA" /> <button id="addItem1">add</button>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) for Multiple test2</td>',
  '<td>',
   '<input type="text" name="textTest4_multiple2" value="data1" /> <input type="text" name="textTest4_multiple2" value="Multi test" /> <button id="addItem2">add</button>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) for Multiple test3</td>',
  '<td>',
   '<input type="text" name="textTest5_multiple3" value="dataA" /> <input type="text" name="textTest5_multiple3" value="Multi test2" /> <input type="text" name="textTest5_multiple3" value="Multi testA" /> <button id="addItem3">add</button>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>text(inline) for Multiple test4</td>',
  '<td>',
   '<input type="text" name="textTest5_5_multiple3" defArray="aa,22,33,44" value="dataA" readonly="readonly" /> <input type="text" name="textTest5_5_multiple3" defArray="bb,44,55,66" value="Multi test2" readonly="readonly" /> <input type="text" name="textTest5_5_multiple3" defArray="cc,88,99,00" value="Multi testA" readonly="readonly" /> <button id="addItem4">add</button>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>select option test</td>',
  '<td>',
   '<select name="select">',
    '<option name="nameop1" value="op1">item1</option>',
    '<option name="nameop2" value="op2" selected>item2</option>',
    '<option name="nameop3" value="op3">item3</option>',
   '</select>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>select option test2</td>',
  '<td>',
   '<select name="select2" size=5>',
    '<option name="nameopA" value="opA">itemA</option>',
    '<option name="nameopB" value="opB" selected>itemB</option>',
    '<option name="nameopC" value="opC">itemC</option>',
    '<option name="nameopD" value="opE">itemE</option>',
    '<option name="nameopE" value="opF">itemF</option>',
   '</select>',
  '</td>',
 '</tr>',
 '<tr>',
  '<td>select option test3 multiple</td>',
  '<td>',
   '<select name="select3" multiple size=5>',
    '<option name="nameopAA" value="opAA">itemAA</option>',
    '<option name="nameopBB" value="opBB" selected>itemBB</option>',
    '<option name="nameopCC" value="opCC">itemCC</option>',
    '<option name="nameopDD" value="opEE" selected>itemEE</option>',
    '<option name="nameopEE" value="opFF">itemFF</option>',
   '</select>',
  '</td>',
 '</tr>',
 '</tbody>',
'</table>',
].join('');




//userFunction(sample)
var userConfig = function(){
	var options = GM_option.get();	//load userConfig Data(hashArray)

	//example
	var strTxt = "";
	var check1 = options['check1'];
	strTxt += 'check1:' + check1 + '\n';

	var check2 = options['check2'];
	strTxt += 'check2:' + check2 + '\n';

	var check3 = options['check3'];
	strTxt += 'check3:' + check3 + '\n';

	var radio1 = options['radio1'];
	if(radio1 == 'true') strTxt += 'radio1:on\n';
	else  strTxt += 'radio1:off\n';

	var radio2 = options['radio2'];
	strTxt += 'radio2:' + radio2 + '\n';

	var textTest1 = options['textTest1'];
	strTxt += 'textTest1:' + textTest1 + '\n';

	var textarea = options['textarea'];
	textarea = textarea.split('\n');
	for(var i=0,j=textarea.length;i<j;i++){
		strTxt += 'textarea[' + i + ']:' + textarea[i] + '\n';
	}

	var expand = function(text){
		var obj = eval(text);
		for(var i=0,j=obj.length;i<j;i++){
			var objTemp = obj[i];
			for(var x=0,y=objTemp.length;x<y;x++){
				strTxt += text + '[' + i + '][' + x + ']:' + objTemp[x] + '\n';
			}
		}
	};

	var textTest3_multiple1 = options['textTest3_multiple1'];
	expand('textTest3_multiple1');


	var textTest3_3_multiple1 =  options['textTest3_3_multiple1'];
	expand('textTest3_3_multiple1');

	var textTest4_multiple2 = options['textTest4_multiple2'];
	expand('textTest4_multiple2');


	var textTest5_multiple3 = options['textTest5_multiple3'];
	expand('textTest5_multiple3');

	var textTest5_5_multiple3 = options['textTest5_5_multiple3'];
	expand('textTest5_5_multiple3');

	var select = options['select'];
	strTxt += 'select:' + select + '\n';

	var select2 = options['select2'];
	strTxt += 'select2:' + select2 + '\n';

	var select3 = JSON.parse(options['select3']);
	for(var key in select3){
		strTxt += 'select3:' + key + '\n';
	}

	alert(strTxt);


	//make OpenButton(sample)
	var openButton = document.createElement('button');
	openButton.id = 'openButton';
	openButton.innerHTML = 'click here!(open GM_option)';
	openButton.style = 'position:fixed;top:0;left:0;right:0;bottom:0;margin:auto; height:100px;';
	document.body.appendChild(openButton);
	
	var openConfig = function(){
		GM_option.open(strHeader,strHTML);
	}
	openButton.addEventListener('click', openConfig,false);

	window.addEventListener('beforeunload', function() {
		window.removeEventListener("beforeunload", arguments.callee,false);
		openButton.removeEventListener('click',openConfig,false);
		userConfig = null;
	},false);
}

//optional messages
var msgArray = {
	'save':'save?(ok:reload)',
	'reset':'discard changes?(load savedata)',
	'clear':'delete your settings?(ok:reload)',
	'delete':'delete this line?(Don\'t forget to save.)',
};
window.addEventListener('GM_option_loaded',userConfig,false);	//userFunction
GM_option.open(strHeader,strHTML,msgArray);	//load

//script menu
GM_registerMenuCommand('Open GM_option',function(){
	GM_option.open(strHeader,strHTML,msgArray);
});



window.addEventListener('beforeunload', function() {
	window.removeEventListener("beforeunload", arguments.callee,false);
	window.removeEventListener('GM_option_loaded',userConfig,false);
},false);



*********************************************************************************************************************/