Simple Form Saver

Provides a small red button at the top left, click it to save or to fill in forms on the web. Auto-fill, click replay and pasting raw info into forms also featured.

  1. // ==UserScript==
  2. // @name Simple Form Saver
  3. // @version 1.8.3
  4. // @namespace SFS92
  5. // @description Provides a small red button at the top left, click it to save or to fill in forms on the web. Auto-fill, click replay and pasting raw info into forms also featured.
  6. // @icon https://bit.ly/1Qre8Je
  7. // @include *
  8. // @run-at document-end
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_log
  11. // @grant GM_getResourceText
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_deleteValue
  15. // @grant GM_addStyle
  16. // @grant GM_xmlhttpRequest
  17. // @require https://code.jquery.com/jquery-2.2.1.js
  18. // @require https://code.jquery.com/ui/1.11.4/jquery-ui.js
  19. // @require https://openuserjs.org/src/libs/slow!/GM_registerMenuCommand_Submenu_JS_Module.js
  20. // @resource jqueryuiCss https://code.jquery.com/ui/1.11.4/themes/vader/jquery-ui.css
  21. // ==/UserScript==
  22.  
  23. // @run-at document-start //problem, if at start, GM_registerMenuCommand does nothing.
  24.  
  25. //addEventListener("load", function() {GM_platform_wrapper("Simple Form Saver", "1aFdtRM", false);});
  26.  
  27. // @require https://code.jquery.com/jquery-2.2.1.js
  28. // @require https://code.jquery.com/ui/1.11.4/jquery-ui.js
  29. // @resource jqueryuiCss https://code.jquery.com/ui/1.11.4/themes/vader/jquery-ui.css
  30. // -------------
  31. // @require https://code.jquery.com/jquery-latest.js
  32. // @require https://code.jquery.com/ui/1.10.3/jquery-ui.js
  33. // @resource jqueryuiCss https://code.jquery.com/ui/1.10.3/themes/vader/jquery-ui.css
  34.  
  35.  
  36. //unsafeWindow.markT("Begin sfs");
  37. //
  38. // Data other than settings etc. is stored as GM get/setValues named: hostFormsList and formsAddressBook.
  39. //
  40. var old_GM_log=GM_log;
  41. log=function(t) { console.info(t); old_GM_log(t); }; //if (!Chrome) old_GM_log(t);};
  42. //GM_log=log;
  43. log2=log;
  44. log2=function(){};
  45. log=function(){};
  46.  
  47. //console.log("SimpleFormSave start. Iframe:", window.parent != window);
  48. GM_platform_wrapper("Simple Form Saver", 6, jsLoadedCB); // Adapter for Google Chrome.
  49.  
  50. //console.log("SFS startup, window.name "+window.name+" "+location);
  51.  
  52. if (/SFS:KeepFrame/.test(window.name)) {
  53. window.name=window.name.replace(/SFS:KeepFrame/,"");
  54. window.onbeforeunload = function(){
  55. return 'Are you sure you want to leave? You may want to set Simple Form Saver values from GM menu for frame.';
  56. };
  57. }
  58. //
  59. // History
  60. //
  61. // updated Sept 2013. v1.6.3 Can add further clicks to page
  62. // updated Sept 2013. v1.5.1 Address/Notebook (alt-a), unhide password & Export/Import added.
  63. // updated Sept 2013. v1.5.0 Updated to coax dynamic forms to work.
  64. // updated for firefox 22.0
  65. // updated March 2013. Fixed Chrome issue with recording a click. v.1.3.2
  66. // updated March 2013. Restricted regexp page matching. v.1.3.1
  67. // updated March 2013. Dialog window in new version of browser was tiny so text was unviewable, using own versions of dialog boxes, alert, confirm and prompt. v.1.3.0
  68. // updated February 2011. See userscripts.org description page for this script regarding change in tick box sizing. Changes only by request.
  69. // updated January 2011. Updated for Greasemonkey 0.9, javascript's basic function, eval(), no longer works fully in GM.
  70. // updated January 2011. Updated for Firefox 4.
  71. // updated December 2010. Allows editing of auto-click target element.
  72. // updated 17 October 2010. Google Chrome platform adaptation.
  73. // updated 7 October 2010. Added feature allowing data to be pasted directly into a form. See last part of description below.
  74. // updated 1st of March 2010. Auto delete an element on a page, select record mouse click and do control-shift-click on the element. Remove iframes.
  75. // updated 5th of February 2010. Can fill in pop-ups and other elements that appear during use of a page.
  76. // updated 29th of Janaury 2010. Facility for editing form storage manually.
  77. // updated 27th of Janaury 2010. Problem with duplicate input names solved.
  78. // updated 25th of Janaury 2010. Handling of input name changes and multiline textareas added.
  79. // updated 18th of Janaury 2010. Added ability to record a mouse click on a page with autoplay.
  80. // updated 16th of Janaury 2010. Enable selective fill-in of forms on user selected pages.
  81. // updated 16th of Janaury 2010. Enable iframes with forms to work, and forms that auto update on change.
  82.  
  83. //
  84. // Globals
  85. //
  86.  
  87. var inputs_on_page=0, text_inputs=0, no_of_inputs=0, special_ta;
  88. with (window.document)
  89. var selects=getElementsByTagName("select"), tareas=getElementsByTagName("textarea"), rinputs=getElementsByTagName("input");
  90. var inputs=[], input_names={}, old_elems=[];
  91. var automatic=false;
  92. var reddot=true, suspend_replay=true, full_icon=false, element_to_highlight, logos=[];
  93. var hash_host_list = new Object();
  94. var page_key, site, page_object, regexp_page_match, href, form_data, click, recording;
  95. var img, img2, link;
  96. var iframe;
  97. var uwin=unsafeWindow;
  98. var pasteWindow;
  99. var auto_fill=false, no_element_for_click=true, prev_msg, msg, unhide, init_fin, await_click;
  100. var ws=window.setTimeout, write_once=true; //noscript can block timeout.
  101. var widener="\n___________________________________________________\n";
  102. var bullet="\u26ab", bullet_tab="\u26ab\t", tab_bullet="\t\u26ab",separator=":\u200e:", mdash_char = "\u2014",
  103. bullet_regexp=/\s*\u26ab\s*/g,
  104. tick = "\u2714", ex="\u2717"; // bullet_regexp=RegExp("\s*\u26ab\s*", "g");
  105. var data_version=1.2;
  106. var awaiting_an_option;
  107. String.prototype.indexOfRegExp = function(regex, nomatch){ var match = this.match(regex); if (match) return this.indexOf(match[0]); else return (nomatch==undefined ? -1 : nomatch);};
  108. String.prototype.trim = function () { return this.replace(/^\s*|\s*$/g,""); };
  109. String.prototype.splitOnce = function (r) { r=RegExp(r.source||r, "g"); var a=r.exec(this); return [ this.substring(0, a?a.index:""), this.substr(r.lastIndex) ] ; };
  110. String.prototype.tail = function (n) { var last_pos = this.length - n; if (last_pos < 0) last_pos=0; return ( n < this.length ? "..." : "" )+this.substr(this.length-n); };
  111. Date.prototype.slashFormat=function(){var dd=this.getDate();if(dd<10)dd='0'+dd;var mm=this.getMonth()+1;if(mm<10)mm='0'+mm;var yyyy=this.getFullYear();return String(mm+"\/"+dd+"\/"+yyyy)};
  112. Date.prototype.ampmFormat=function() { var ap = "am", hour=this.getHours(); if (hour>11) ap = "pm"; if (hour > 12) hour = hour - 12; if (hour == 0) hour = 12; return hour+":"+this.getMinutes()+" "+ap;};
  113.  
  114. log("Form saver, at "+location+", readyState:"+document.readyState);
  115.  
  116. function jsLoadedCB() { //Called only on chrome from platform wrapper
  117. main();
  118. }
  119.  
  120. if (!Chrome) try {
  121. // addEventListener("abort", function(){log("ERROR");});
  122. // addEventListener("load", function() {
  123. if (document.readyState=="complete") main();
  124. addEventListener("DOMContentLoaded", function() {
  125. main();
  126. if (GM_getValue("paste_shortcut", false)) {
  127. if (uwin.focusControl)
  128. uwin.focusControl.blur();
  129. else document.activeElement.blur();
  130. }
  131. },0);
  132. } catch(e) { GM_log(e); throw(e) }
  133.  
  134. //
  135. //Mainline
  136. //
  137. function main() {
  138. log("Form saver, main()");
  139. with (window.document) { // init data not there at document-start
  140. selects=getElementsByTagName("select"), tareas=getElementsByTagName("textarea"),
  141. rinputs=getElementsByTagName("input");
  142. }
  143. if (window.parent != window) {
  144. iframe=true;
  145. // return;
  146. }
  147. readPersistentData();
  148. setTimeout(function(){updateValueTitles();},1000);
  149. //log("suspend_replay: "+suspend_replay);
  150. //log("page_object.automatic "+(page_object&&page_object.automatic));
  151. //log("startup, form_data: "+uneval(form_data)+", page key:"+page_key+", regexp match: "+regexp_page_match
  152. //+ ", inputs "+inputs.length+", jq ips "+$("input").length);
  153. //if (!Chrome) this.submenuModule.register("Simple Form Saver");
  154. this.submenuModule.register("Simple Form Saver");
  155. registerMenus();
  156. if (hash_host_list.msg || hash_host_list.prev_msg) {
  157. msg=hash_host_list.msg;
  158. prev_msg=hash_host_list.prev_msg;
  159. if ( ! checkIfIntervalLongEnough() && msg)
  160. window.status="Form saver msg: "+(msg);
  161. hash_host_list.prev_msg=msg;
  162. prev_msg=msg;
  163. if ( ! msg) delete hash_host_list.prev_msg;
  164. delete hash_host_list.msg;
  165. persistData();
  166. }
  167. if (inputs.length==0 && ! (form_data||click)) {
  168. log("No inputs");
  169. return;
  170. }
  171. if (getXPathElem())
  172. no_element_for_click=false;
  173. if ( page_object.automatic || (click && ! suspend_replay)) {
  174. addIcon(true); //will register menus
  175. if (checkIfIntervalLongEnough()) {
  176. auto_fill=true;
  177. if (page_object.automatic !== false && (page_object.automatic || ! no_element_for_click)) {
  178. fillForm(false, true);
  179. }
  180. log("interval passed, Filled form now replay click."+click);
  181. replayClick();
  182. inOutSet();
  183. auto_fill=false;
  184. }
  185. }
  186. if (page_object.click_evidence)
  187. delete page_object.click_evidence.sealed;
  188. if ( ! link )
  189. addIcon(true); //will register menus
  190. if (/Simple Form Saver replayed/.test(msg)) {
  191. inOutSet(true);
  192. }
  193. //keepEyeOnIcon();
  194. if (click && suspend_replay && ! page_object.automatic===true ) { GM_log("Suppressed replay on newly loaded page, auto-replay is suspended, to replay click icon.") }
  195. window.onbeforeunload=function(e) {
  196. if (recording) {
  197. var dialog=$("#sfsconfirm3"), openeddialogs;
  198. if (dialog.length && dialog.parent().css("display")!="none")
  199. openeddialogs=true;
  200.  
  201. log("onbeforeunload "+openeddialogs+" "+e.target.tagName+" cc "+await_click+" "+uneval(e)+" act:"+document.activeElement.tagName);
  202. var roll=""; for (i in e) roll+=i+" "+e[i]+"\n";
  203. var pn=e.explicitOriginalTarget.parentNode;
  204. log(" "+e.eventPhase+" "
  205. +e.originalTarget+" "
  206. +(pn?pn.tagName+" ":"")
  207. +e.timeStamp);
  208. var pev={}
  209. pev.target=e.explicitOriginalTarget.parentNode;
  210. if (await_click) setTimeout(function() { log("call cl"); recordClick(pev);}, 100);
  211. if(await_click || openeddialogs) {
  212. var p=prompt_interruption, note="No dialogs open"; //!!! for jdialog???
  213. if (prompt_interruption) { prompt3(p.a,p.b,p.c); }
  214. log("block exit, call confirm");
  215. confirm(dialog.parent().css("display")+". The page on which you are recording has in fact detected the click and wishes to unload. "
  216. +"However, you must complete interaction with the recording dialogs beforehand."
  217. +"\n\nPlease finish with recording dialogue in other window and ONLY then click OK or Cancel here in this window. A window may flash up momentarily, just ignore it. "
  218. );
  219. interrupted=true;
  220. }
  221. }//end if recording
  222. }; //end beforeunload
  223. window.addEventListener("unload", function(){
  224. //readPersistentData();
  225. log("unload");
  226. //alert("unload");
  227. var click_evidence=local_getValue("click_evidence", "");
  228. page_object.click_evidence=click_evidence;
  229. if (page_object.click_evidence) {
  230. if ( page_object.click_evidence.prejudice)
  231. delete page_object.click_evidence;
  232. else
  233. page_object.click_evidence.sealed=true;
  234. local_setValue("click_evidence", page_object.click_evidence);
  235. log("unload "+page_object.click_evidence+ " "+page_object.click_evidence.prejudice);
  236. //persistData();
  237. }
  238. if (pasteWindow && !pasteWindow.closed) pasteWindow.close();
  239. }, false); //end unload
  240. if (click || page_object.automatic) {
  241. uwin.addEventListener("DOMNodeInserted", handlePopups, false);
  242. }
  243. if (GM_getValue("paste_shortcut", false)) {
  244. addEventListener("keypress", function(e) {
  245. if (e.charCode==118 && e.ctrlKey) //118 is v
  246. if ( ! /INPUT|TEXTAREA/.test(e.target.tagName)) {
  247. special_ta=document.createElement("textarea");
  248. special_ta.id="speciality";
  249. document.body.appendChild(special_ta);
  250. //special_ta.focus();
  251. e.preventDefault();
  252. e.stopPropagation();
  253. setTimeout(function() {
  254. log("tout");
  255. pasteIntoForm();
  256. document.body.removeChild(special_ta);
  257. special_ta=null;
  258. }, 2);
  259. }
  260. return true;
  261. },0);
  262. }
  263. init_fin=true;
  264. }
  265.  
  266. function saveFormData(nosave) { try {
  267. var output_string = "";
  268. var i, plicate={}, saved_data={}, nothing=true;
  269. if (regexp_page_match && page_object.form_data && !nosave) saved_data=page_object.form_data;
  270. log("save data, beg "+inputs.length);
  271. for(i=0; i < inputs.length; i++) {
  272. var input=inputs[i];
  273. var value=input.value;
  274. var type=input.type;
  275. if ( value == "" && /text|textarea/.test(type))
  276. continue;
  277. nothing=false;
  278. var name=input.name;
  279. if ( ! name) name="yyNoName"+i;
  280. var checked=input.checked;
  281. value = value.replace(/\r\n/g,"\u200d");
  282. value = value.replace(/\n/g,"\u200d");
  283. if (type=="checkbox") {
  284. name+=mdash_char+value;
  285. if (checked)
  286. value=tick;
  287. else
  288. value=ex;
  289. }
  290. if ( ! plicate[name])
  291. plicate[name]=[];
  292. plicate[name].push(value) //save name and value in case more than one input with same name.
  293. if ( type == "radio")
  294. if ( ! checked ) {
  295. plicate[name].pop()
  296. continue;
  297. }
  298. if ( type == "select-multiple" ) {
  299. options_list = []
  300. for ( var index=0; index < input.options.length; index++ )
  301. if ( input.options.item(index).selected )
  302. options_list.push(index);
  303. value = options_list;
  304. }
  305. if ( type == "select-one" ) {
  306. if (input.selectedIndex==-1) continue;
  307. for ( var index=0; index < input.options.length; index++ )
  308. if ( input.options.item(index).selected )
  309. value=index;
  310. if (value.substr)
  311. value=input.selectedIndex;
  312. }
  313. log("Save On i:"+i+". Elem_name: "+name+". type: "+type+". elem_val:"+value+"."+", typeof value: "+typeof value+", ply: "+plicate[name].length);
  314. saved_data[name]={};
  315. saved_data[name].v=value;
  316. saved_data[name].i=i;
  317. if (type=="password") {
  318. saved_data[name].pw=1;
  319. }
  320. if (plicate[name].length > 1)
  321. saved_data[name].p=plicate[name];//.slice();
  322. else
  323. delete saved_data[name].p;
  324. log("Saved Data, val: "+saved_data[name].v+". i: "+saved_data[name].i);
  325. } // end for
  326. var request=true;
  327. if ( ! nothing || nosave) {
  328. if (iframe && ! regexp_page_match)
  329. prompt3("***Simple Form Saver***\n\nPage stored: "+page_key+widener+"This form is part of a subpage (an iframe) within the current window. "
  330. +"It is controlled via the icon, or via shortcuts, whereas any forms not in the subpage but in the main window are controlled from the GM menu."
  331. +"\n\nYou can get status information via the icon or GM menu and check to which page it is refers (first line of the status information window) "
  332. +"\n\nCancel and change the page name below or enter a pattern for which the form data shall be stored. Any page matching this pattern can then be used for form filling. Use just the site name for it to be in effect for entire site."
  333. +"\n\nHit OK to save as usual for the page: "+page_key
  334. , page_key
  335. ,function(reply) {
  336. if (reply && reply != page_key) {
  337. delete hash_host_list[page_key];
  338. page_key=reply
  339. page_object.form_data=saved_data;
  340. log("po "+page_object+"cnt "+ countMembers(page_object));
  341. persistData();
  342. window.status="Forms saved for this page"
  343. +", # input fields stored: "+i+". At: "+page_key;
  344. }
  345. }); //end function(reply)
  346. page_object.form_data=saved_data;
  347. log("po "+page_object+"cnt "+ countMembers(page_object));
  348. persistData();
  349. window.status="Forms saved for this page"
  350. +", # input fields stored: "+i+". At: "+page_key;
  351. } //endif ! nothing || nosave
  352. else {
  353. if (!nosave)
  354. window.status="Nothing on forms to save";
  355. else return saved_data;
  356. }
  357. } catch(e) {alert("Cannot save form, reload and try again.\n\nError was:"+e+" "+e.lineNumber);throw(e);}
  358. log("win.status "+window.status+", fd: "+form_data+", po.fd: "+ page_object.form_data+", sd: "+saved_data);
  359. }; //end saveFormData())
  360.  
  361. function fillForm(once_off, auto, tmp_data) { try { //also Click replay if necessary
  362. log("fillForm(once_off: "+once_off+", auto: "+ auto+", tmp: "+uneval(tmp_data));
  363. var saved_data= tmp_data ? tmp_data : form_data;
  364. var name, saved_obj, elem_name = "";
  365. var changed=null, kick, result=true, fill_msg;
  366. var type, value, indeterminate, plicate={};
  367. var input, option;
  368. //function setMember(obj, member, value) {
  369. function setMember(member, value) {
  370. if ( member != value) changed=input.name||true;
  371. return;
  372. /* if (eval(obj+"."+member) != value) { */
  373. /* changed=true; */
  374. /* var js=obj+"."+member+"='"+value+"'"; */
  375. /* if (typeof value=="boolean") */
  376. /* js=obj+"."+member+"="+value; */
  377. /* eval(js); */
  378. //}
  379. }
  380. var plicate={};
  381. var finputs=inputs;
  382. if (once_off) finputs=once_off;
  383.  
  384. for(var i=0; i<finputs.length; i++) {
  385. input=finputs[i]; this.input=input;
  386. name = elem_name = input.name;
  387. type=input.type;
  388. elem_value=input.value;
  389. value=undefined;
  390. //fakeClick(input);
  391. log2("Fill, On i:"+i+". Elem_name: "+elem_name+". type: "+type+". current elem_val: "+elem_value)
  392. if ( ! elem_name) name="yyNoName"+i;
  393. if (type=="checkbox")
  394. name=elem_name+mdash_char+elem_value;
  395. saved_obj=saved_data && saved_data[name];
  396. log2("saved_data["+name+"]="+(saved_obj ? ".v="+saved_obj.v+". (datatype:"+(typeof saved_obj.v)+"), .i= "+saved_obj.i + ". .p= "+saved_obj.p : " null"))
  397.  
  398. if (saved_obj && (saved_obj.i != undefined) && saved_obj.i != i && ! once_off) {
  399. if (finputs[saved_obj.i] && finputs[saved_obj.i].name == elem_name) {
  400. indeterminate=true;
  401. }
  402. else {
  403. saved_obj.i=i;
  404. }
  405. }
  406. if (saved_obj && saved_obj.p ) {
  407. log("plicate "+saved_obj.p);
  408. if ( ! plicate[name] && saved_data[name].p) {
  409. if (!saved_data[name].p.slice)
  410. saved_data[name].p = convert_obj_to_array(saved_data[name].p);
  411. plicate[name]=saved_data[name].p.slice(); // copy
  412. plicate[name].pop(); // top of stack is also the one in form_data.
  413. }
  414. value=plicate[name].shift();
  415. if (value && indeterminate)
  416. indeterminate=false;
  417. }
  418. if ( ! value && saved_obj && saved_obj.v) value=saved_obj.v;
  419. log("value: "+value+", indeterminate "+indeterminate);
  420. if (indeterminate) { indeterminate=false; value=undefined;} // i and saved_obj.i don't match
  421. if ( value === undefined && ! once_off && type !="radio") {
  422. var res=findRenamedInput(name, i); //checks for value in saved .i index
  423. value=res[0];
  424. if ( value) {
  425. fill_msg="The "+ordinal(i+1)+' input field, "'+name+'", may have been renamed, try re-saving form information if it is not an automatic renaming by the server. ';
  426. console.log(fill_msg+"It was filled in with the previously saved value, "
  427. +value+", for the "+ordinal(i+1)+" field,"+ordinal(res[2])+","+res[1]+". Page: "+page_key);
  428. }
  429. }
  430. if (value===undefined) {
  431. if (type == "radio" ) {
  432. // input.value="";
  433. value=saved_obj?saved_obj.v:"";
  434. }
  435. else {
  436. if (type=="checkbox" ) {
  437. if (input.checked)
  438. changed=true;
  439. input.checked=false;
  440. }
  441. log("Skip "+i);
  442. continue;
  443. }
  444. } //end if undefined
  445. //log("Check type, value: "+value+", type: "+type);
  446. if (value && typeof value == "string") value = value.replace(/\u200d/g, "\r\n");
  447. if (type=="checkbox") {
  448. if (value== tick)
  449. { setMember(input.checked, true); input.checked=true;}
  450. else
  451. {setMember(input.checked, false); input.checked=false;}
  452. }
  453. else if ((type == "radio") && elem_value == value)
  454. {setMember(input.checked, true); input.checked=true}
  455. else if (type != "radio") {
  456. if (finputs.tagName=="TEXTAREA") {
  457. if (value != elem_value)
  458. changed=true;
  459. }
  460. else
  461. if (type == "select-multiple" ) {
  462. options_list=value;
  463. for (var index=0; index < input.options.length; index++) {
  464. if (input.options.length <= index ) { result=false; continue }
  465. if ( RegExp ("\\b"+index+"\\b").test(options_list) )
  466. { setMember(input.options.item(index).selected, true); input.options.item(index).selected=true;}
  467. else
  468. { setMember(input.options.item(index).selected, false); input.options.item(index).selected=false;}
  469. }
  470. }
  471. else if ( type == "select-one" ) {
  472. log2(" Select, saved index "+value+". Current selected Index: "+input.selectedIndex+" of "+input.options.length+". .value "+input.value);
  473. if (input.options.length <= value) { result=false; continue }
  474. var index, set_select;
  475. try { if (input.selectedIndex!=value) {changed=true;input.selectedIndex=value; } } catch(e){}
  476. for ( index=0; index < input.options.length; index++ )
  477. if ( index == value) {
  478. setMember(input.options.item(index).selected, true);
  479. input.options.item(index).selected=true;
  480. option=input.options.item(index);
  481. //fakeClick(input.options.item(index));
  482. }
  483. }
  484. else {
  485. setMember(input.value, value);
  486. if (value.slice) input.value=value.slice(0,value.length/2);
  487. else input.value=value;
  488. //getAnonymousNodes The getAnonymousNodes method retrieves the anonymous children of the specified element.
  489. //only visible in dom inspector (red in color, beneath INPUTs at first as DIV with a BR then when filled as DIV with direct text.
  490. setTimeout(function(sinput, svalue) { //pass parameters to it from stack
  491. //log("setTimeout Filler set:"+sinput.name+"="+svalue+".");
  492. sinput.value=svalue;
  493. coaxInputs(sinput, svalue);
  494. }, 50, input, value); // input, value parameters passed to setTimeout function stack, 50 is timeout. To add to eventlistener extra param needed.
  495. //input.value=value;
  496. //log("last setMember "+value);
  497. }
  498. } //end if type != radio
  499. log("Set value of input["+i+"].type: "+type+", saved value is:"+value + ". Current value: "+input.value+ ". seloption: "+input.selectedIndex +". Changed: "+changed);
  500. if (changed) { // trick to simulate event if (changed && false) !!
  501. kick=changed;
  502. changed=false;
  503. var pseudo_event = window.document.createEvent("MouseEvents");// create event
  504. pseudo_event.initMouseEvent("change", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  505. pseudo_event = window.document.createEvent("MouseEvents");// create event
  506. //fakeClick(input);
  507. //if (option) { fakeClick(option);option=null;}
  508. }
  509. } // end for inputs
  510. var write_msg=(msg?msg+" ":"")+"Filled in forms"
  511. +(once_off ? " for dynamic element on "+page_key : " at "+page_key )+". "
  512. +( ! result? "Bounds error. " : "") +( kick ? "changed ( "+kick+"...)" : "No changes were necessary. ")
  513. +(fill_msg ? fill_msg : "");
  514. window.status=write_msg;
  515. GM_log(write_msg);
  516. old_elems=inputs.slice(0);
  517. if ( result && ! once_off && ! auto ) {
  518. log("Filled form, replay from filler as manual ");
  519. replayClick(true);
  520. }
  521. return result;
  522. } catch(e) { alert("Cannot fill-out form, reload and try again.\n\nError was: "+e+" "+e.lineNumber); throw(e);}
  523. }
  524.  
  525. function coaxInputs(input, value) {
  526. //console.log("coaxing "+input.name+" "+value);
  527. //Live dynamic inputs need coaxing, test in 1.5.0:
  528. var act_el=window.document.activeElement;
  529. input.focus(); try{
  530. moveCaretToEnd(input); }catch(e){}
  531. var evt = document.createEvent("KeyboardEvent"), evt2=document.createEvent("KeyboardEvent"), evt3=document.createEvent("KeyboardEvent"), evt4=document.createEvent("KeyboardEvent");
  532. //evt.initKeyEvent("keypress", true, true, null, "U+0008", 0, "");
  533. var keyval=98; // 98 'a', 8 is backspace
  534. if (FireFox) {
  535. jQuery.event.trigger({ type : "keypress", which : keyval});
  536. evt.initKeyEvent ("keydown", true, true, window, 0, true, false, false, keyval, 0);
  537. evt2.initKeyEvent ("keypress", true, true, window, 0, true, false, false, keyval, 0);
  538. evt3.initKeyEvent ("keyup", true, true, window, 0, true, false, false, keyval, 0);
  539. evt4.initKeyEvent ("textinput", true, true, window, 0, true, false, false, keyval, 0);
  540. }
  541. else {
  542. evt.initKeyboardEvent ("keydown", true, true, window, 0, true, false, false, keyval, 0);
  543. evt2.initKeyboardEvent ("keyup", true, true, window, 0, true, false, false, keyval, 0);
  544. evt3.initKeyboardEvent ("keypress", true, true, window, 0, true, false, false, keyval, 0);
  545. }
  546. input.dispatchEvent(evt);
  547. input.dispatchEvent(evt2);
  548. input.dispatchEvent(evt3);
  549. input.dispatchEvent(evt4);
  550.  
  551. act_el.focus();
  552. }
  553.  
  554. function moveCaretToEnd(el) {
  555. el.focus();
  556. if (typeof el.selectionStart == "number") {
  557. el.selectionStart = el.selectionEnd = el.value.length;
  558. } else if (typeof el.createTextRange != "undefined") {
  559. var range = el.createTextRange();
  560. range.collapse(false);
  561. range.select();
  562. }
  563. }
  564.  
  565.  
  566. function registerMenus(full_menus) {
  567. //
  568. // GM menus
  569. //
  570. switch(this.phaseMenus) {
  571. case undefined:
  572. if (this.done==2) return;
  573.  
  574. GM_registerMenuCommand( "========Simple Form Saver======", function(){});
  575. GM_registerMenuCommand( "Save Form",
  576. function() {
  577. readPersistentData();
  578. if ( inputs.length==0)
  579. return;
  580. useCorrect_page_object();
  581. saveFormData();
  582. if ( ! link )
  583. addIcon(true);
  584. setLive();
  585. inOutSet();
  586. }, "","","F" );
  587. GM_registerMenuCommand( "Record next mouse click on document...",
  588. function() {
  589. log("ask confirm "+FireFox);
  590. readPersistentData();
  591. if (click) {
  592. confirm3("A click has been previously recorded, hit OK to delete it and re-record, or Cancel to make addition clicks or abort", function(reply) {
  593. if (!reply) {
  594. log("Reply:"+reply+"."+typeof reply+" "+(!reply));
  595. prompt3("Please enter the path (XPath) for the additional item to be clicked, eg, /html/body/div[1]/div, for XPath see console while element highlighting is selected by you when recording a first click.","",
  596. function(reply){
  597. if (reply==null) { GM_log("No extra click recorded");return; }
  598. if (!click.further_clicks) click.further_clicks=[];
  599. click.further_clicks.push(reply.trim());
  600. var xpath=click.further_clicks[click.further_clicks.length-1];
  601. var celem=uwin.document.evaluate(xpath,uwin.document,null,6,null).snapshotItem(0);
  602. GM_log("Extra click is on:"+celem.tagName+", "+celem.textContent);
  603.  
  604. persistData();
  605. });
  606. return;
  607. }// !reply
  608. confirmClickRecording()
  609. }); // end confirm3 CB
  610.  
  611. }//end if click
  612. else
  613. confirmClickRecording();
  614. }, "", "", "M"); //end GM_registerMenu
  615. GM_registerMenuCommand( "Get Form Status & Settings",
  616. function() {
  617. readPersistentData();
  618. alert3(getStatusInfo());
  619. if (element_to_highlight) element_to_highlight.style.borderStyle="none";
  620. }, "","","G");
  621.  
  622. GM_registerMenuCommand( "Toggle Auto-Click-Replay function on all pages "
  623. +(suspend_replay ? "[disabled]": "[enabled]"),
  624. function() {
  625. readPersistentData();
  626. if (suspend_replay) suspend_replay=false;
  627. else suspend_replay=true;
  628. persistData();
  629. window.status="Auto click replay is now "+(suspend_replay ? "suspended. ": "on. ");
  630. alert3(window.status);
  631. }, "","","u");
  632.  
  633.  
  634. GM_registerMenuCommand( "Icon for Form Saver pages on/off, toggle "+(reddot?"[on]":"[off]"),
  635. function() {
  636. readPersistentData();
  637. toggleIcon();
  638. submenuModule.changeName("Icon for Form.*", "Icon for Form Saver pages on/off, toggle "+(reddot?"[on]":"[off]"));
  639. //submenuModule.positionAt("Icon for Form.*", 1);
  640. persistData();
  641. window.status="Form Saver Icon "+(reddot ? "on": "off");
  642. });
  643. /* if( ! FireFox) PROBlem on Chrome, only localstorage is available so imported data cannot be seen when visiting other websites*/
  644. // export also. Storing it as uneval or member in window.name fails when "back" is selected.
  645. /* GM_registerMenuCommand("Import Form Data", function(){ */
  646. /* var users_input=prompt(widener+"\nTo import raw forms' data paste here and click OK.\n\nTo get raw data on Firefox, look in about:config for the name 'hostFormsList', copy its value here." , ""); */
  647. /* if (users_input != null) GM_setValue('hostFormsList', users_input); */
  648. /* alert(GM_getValue('hostFormsList')); */
  649. /* readPersistentData(); */
  650. /* persistData(); */
  651. /* }) */
  652. GM_registerMenuCommand("Paste Data Into Form, Ctrl-v", pasteIntoForm, "", "", "P");
  653.  
  654.  
  655. GM_registerMenuCommand("Clear the Forms on the Page", function() {
  656. readPersistentData();
  657. for(var i=0; i < inputs.length; i++) {
  658. var input=inputs[i];
  659. if (input.selectedIndex != undefined) { input.selectedIndex=-1; input.value=-1; }
  660. if (input.checked !== undefined )
  661. input.checked=false
  662. input.value="";
  663. }
  664. }, "", "", "C");
  665. GM_registerMenuCommand( "List of pages with saved form information ",
  666. function() {
  667. readPersistentData();
  668. var roll="", len=0;
  669. var reply=RegExp( prompt("Give regexp to filter or leave blank for all:"), "i");
  670. for (var i in hash_host_list) {
  671. var fd=true;
  672. if (hash_host_list[i].form_data) {
  673. for (var j in hash_host_list[i].form_data)
  674. len++;
  675. var unevaled=uneval(hash_host_list[i].form_data);
  676. if ( ! reply.test(i) && ! reply.test(unevaled)) continue;
  677. var sizeof=unevaled.length;
  678. roll+="\n"+i+ "; "+len+" fields, "+sizeof+" bytes.\t\t"+prettyPrintUneval(unevaled, true);
  679. } else fd=false;
  680. if (hash_host_list[i].click) {
  681. if ( ! reply.test(i)) continue;
  682. roll+=( fd ? " (" : "\n" + i + ", " ) + " click path: "+hash_host_list[i].click.xpath.tail(30)+( fd ? ")" : "." ) ;
  683. }
  684. } //end for i in hash_host_list
  685. var sizeof=uneval(hash_host_list).length;
  686. //bigAlert(widener+(roll? roll: "none")+"\n \n"+"Size of store: "+Math.round(sizeof/1000)+"k.");
  687. var ar=roll.split("\n")
  688. ar.sort(function(a,b) { return a.toLowerCase().localeCompare(b.toLowerCase()) });
  689. roll=ar.join("\n");
  690. alert3(widener+(roll? roll: "none")+"\n \n"+"Size of store: "+Math.round(sizeof/1000)+"k.");
  691. });
  692. GM_registerMenuCommand( "Export/Import all form data", exportImport);
  693. GM_registerMenuCommand("Open Forms' Address/Notebook, alt-a", formsAddressBook, "", "", "A");
  694. GM_registerMenuCommand("Unhide passwords [once-off]", unhidePasswords, "", "", "A");
  695. GM_registerMenuCommand("Get/set Script Config Values", getSetConfigs, "", "", "C");
  696. addEventListener("keydown", function(e) {
  697. //GM_log("Keydown: charcode "+e.charCode +", keycode"+e.keyCode+", alt "+e.altKey+", ctrl "+e.ctrlKey);
  698. if (e.keyCode!=65 || ! e.altKey) return true;// alt-a. alt a
  699. if (!e.shiftKey)
  700. formsAddressBook();
  701. else
  702. fillFromAddressBook();
  703. }, 0);
  704. this.phaseMenus=1;
  705. break;
  706. case 1:
  707. if (full_menus) {
  708. registerPageMenus();// ------------>
  709. this.phaseMenus=2;
  710. }
  711. break;
  712. }
  713. function registerPageMenus() {
  714.  
  715. GM_registerMenuCommand( " --------------------------------------------------", function(){});
  716. GM_registerMenuCommand( "Fill-in Form",
  717. function() {
  718. readPersistentData();
  719. if (form_data) {
  720. fillForm();
  721. inOutSet();
  722. }
  723. else
  724. window.status="No form data to fill";
  725. // persistData(); !!
  726. }, "",0,"O");
  727. submenuModule.positionAt("Fill-in Form",0);
  728. GM_registerMenuCommand( "Delete stored Form info for this page",
  729. function() {
  730. readPersistentData();
  731. var request, page_str="\nPage: "+ page_key + (regexp_page_match ? " (regexp match)." : ".") ;
  732. if (click && form_data)
  733. confirm3(page_str+"\n\nTo delete click-replay & form data for this page, choose 'OK'. "
  734. +"\n\nTo delete only the click-replay or to abort, choose 'Cancel'",
  735. function(request){
  736. if (request) { page_object={}; wrapup(); }
  737. else
  738. confirm3(page_str+"\n\nOk to delete click recording? ",
  739. function(request) {
  740. if (request) { delete page_object.click; wrapup();}
  741. else return;
  742. });
  743. });
  744. else
  745. if (click || form_data)
  746. confirm3(page_str+"\n\nOk to delete form date for this page?",
  747. function(request) {
  748. if (request) {
  749. page_object={}
  750. wrapup();
  751. }
  752. else
  753. return;
  754. }); // end if clk||frm
  755. function wrapup(){
  756. inOutSet();
  757. window.status="Deleted "+(request?"all stored ":"only click-replay")+" info for: "+page_key;
  758. persistData();
  759. setLive();
  760. }
  761. });//end GM_registerMenuCommand()
  762.  
  763. GM_registerMenuCommand( "Toggle Automatic form fill & Click-replay on this page ["+(page_object.automatic?"on "+(page_object.automatic=="on" ? "[implicit]":""):"off")+"]", function () {
  764. readPersistentData();
  765. if (page_object.automatic) { //toggle
  766. page_object.automatic=false;
  767. persistData();
  768. window.status="Ending automatic fill-in of forms at page: "+page_key;
  769. alert3("Ending automatic fill-in of forms at page: "+page_key);
  770. }
  771. else {
  772. page_object.automatic=true;
  773. confirm3("Page: "+page_key+widener+"\n\nForm data saved by the user shall "
  774. +"be filled in for this page automatically from now explicitly on. Auto replay of Clicks on this page will override general setting."
  775. +"\n\nCancel to explicitly disable auto-fill/click-replay on this page"
  776. , function(request) {
  777. if ( ! request)
  778. page_object.automatic=false;
  779. else page_object.automatic=true;
  780. window.status="Automatic filling of forms on page "+page_key;
  781. alert3("Automatic filling of forms is explicitly on at page "+page_key);
  782. inOutSet();
  783. persistData();
  784. } );
  785. }
  786. }//end cb function
  787. , "","","" );
  788. GM_registerMenuCommand("Edit stored Form data and site", function() {
  789. editFormData();
  790. }, "", "", "E");
  791.  
  792. GM_registerMenuCommand("Edit Click Element data", function() {
  793. readPersistentData();
  794. editClickData();
  795. persistData();
  796. }, "", "", "E");
  797.  
  798.  
  799. GM_registerMenuCommand( "_____________________________________", function(){});
  800. }
  801. }
  802.  
  803. function convert_obj_to_array(obj) {
  804. var ar=[];
  805. for (var i in obj) if ( ! isNaN(i) ) ar[i]=obj[i];
  806. return ar;
  807. }
  808.  
  809. // function bigAlert(str) {
  810. // lines=str.split("\n");
  811. // var entries=lines.length-5;
  812. // var roll=entries+" entries. "+(entries>30?"Pages: "+( (entries/30^0) + 1 ) : "" ) + "\n", i=0;
  813. // while (lines.length) {
  814. // while ( i < 30) { roll+=(lines[0]?lines[0]+"\n":""); lines.shift(); i++ }
  815. // alert2(roll, 0.7);
  816. // roll=widener, i=0;
  817. // }
  818. // }
  819.  
  820. function fixMargins() { // see trickle function
  821. var results = document.evaluate("//*[contains(@style,'margin-left')]", document, null, 6, null);
  822. var item, len=results.snapshotLength;
  823. var margin, parent, pos;
  824. if (len > 0)
  825. for(var i=0; item=results.snapshotItem(i), i < len; i++ ) {
  826. margin=parseInt(item.style.marginLeft);
  827. //log("item margin "+margin);
  828. parent=item.offsetParent;
  829. pos=item.offsetLeft;
  830. //log(" item pos "+pos);
  831. if (pos < 200 && margin > 5) {
  832. item.style.setProperty("margin-left",(Math.max(0, margin - 35) )+"px","important");
  833. //log("Fixed margin for "+item+" "+item.id+", "+item.className +", from "+margin+". At: "+page_key);
  834. }
  835. }
  836. return;
  837. }
  838.  
  839. function toggleIcon() {
  840. if (reddot) {
  841. reddot=false
  842. removeIcon();
  843. }
  844. else {
  845. reddot=true
  846. addIcon(true);
  847. }
  848. }
  849.  
  850. function removeIcon() {
  851. if (link.parentNode == window.document.body) {
  852. window.document.body.removeChild(link);
  853. delete page_object.position;
  854. }
  855. }
  856.  
  857. function addIcon(make, override) {
  858. if (link && (reddot || override)) {
  859. if (window.document.body) window.document.body.insertBefore(link, window.document.body.firstElementChild);
  860. img.style.display="";
  861. setTimeout(fixMargins, 1000); //!!!!fixMargins
  862. // link.style.cssFloat="left";
  863. // img.style.cssFloat="left";
  864. }
  865. else if ( ! link && make)
  866. makeReddot();
  867. }
  868.  
  869. function makeReddot(parent) { // makes a "link" object as: <B><IMG></IMG></B> and registers mouse events on it.
  870. var lk;
  871. if (! parent)
  872. parent=window.document;
  873. if ( ! link)
  874. link=parent.createElement("b");
  875. var lk=link;
  876. img = parent.createElement("img");
  877. //img2 = parent.createElement("img");
  878. // img2.setAttribute("style", "float: left; z-index:999 ! important; position: relative; margin-right: 20px;margin-left: 20px; ");
  879. // img2.setAttribute("style", "left: -2px; top: 5px; z-index:999 ! important; position: fixed; margin-right: 20px;margin-left: 20px; ");
  880. //img2.setAttribute("style", "left: 3px; top: 5px; z-index:999 ! important; float: left; position: relative; visibility: hidden; margin-right: 12px;");
  881. img.id="SFSimg"; //img2.id="SFSimg2";
  882. //img2.height=10; img2.width=19;
  883. lk.appendChild(img);
  884. //lk.appendChild(img2);
  885. addIcon();
  886. //checkPageLayout(); //!!!checkPageLayout
  887. setLive();
  888. // var pos=getXY(img);
  889. // try {
  890. // var el = document.elementFromPoint(pos.x, pos.y);
  891. // trickleUp(el);
  892. // trickleUp ( document.elementFromPoint(pos.x, pos.y + 50)) } catch(e){};
  893. lk.id="hostFormsListButton";
  894. lk.className="SFSButton";
  895. var dragged=0;
  896. setTimeout(function(){
  897. $(img).draggable({ stop: function( event, ui ) { event.dragEv=true;
  898. event.ui=ui;
  899. dragged=0;
  900. mouseUpCB(event);},
  901. drag: function( event, ui ) { dragged++;}
  902. });//.draggable()
  903. },100);
  904. // $(lk).draggable();
  905. //$(reddot).draggable();
  906. lk.addEventListener("contextmenu", function(e) { // right click
  907. readPersistentData();
  908. inOutSet();
  909. e.preventDefault();
  910. e.stopPropagation();
  911. window.document.body.setAttribute("onClick", "return false;"); //!!!
  912. useCorrect_page_object();
  913. //if (form_data) {
  914. confirm3("Simple Form Saver, saving data at: \n\n\t"+page_key+" Ok/Cancel?",function(reply){
  915. if (!reply) return;
  916. saveFormData();
  917. setLive();
  918. });
  919. //return false;
  920. }, true);
  921. // lk.addEventListener("mouseup", function(e) {
  922. // e.preventDefault();
  923. // e.stopPropagation();
  924. // return false;
  925. // }, true); //mousedown->context->mouseup;
  926. lk.addEventListener("mouseup", function(e) { // click // was "mousedown"
  927. mouseUpCB(e)}, true);
  928. function mouseUpCB(e) {
  929. log2("mouse btn event, on lk "+e.button+", dragged?:"+dragged+", from drag:"+e.dragEv+" new pos: "+(e.ui?uneval(e.ui.position):""));
  930. if (dragged) return;
  931. if(e.dragEv) {
  932. readPersistentData();
  933. page_object.position=e.ui.position;
  934. persistData();
  935. log2("Mouse was dragged");
  936. // e.preventDefault();
  937. // e.stopPropagation();
  938. return;
  939. }
  940. // e.preventDefault();
  941. // e.stopPropagation();
  942. if (e.button==0 ) { //left button
  943. readPersistentData();
  944. inOutSet();
  945. // e.preventDefault();
  946. // e.stopPropagation();
  947. if (form_data)
  948. fillForm();
  949. else {
  950. useCorrect_page_object();
  951. if (click) replayClick(true);
  952. saveFormData();
  953. setLive();
  954. persistData();
  955. }
  956. //persistData(); //!!
  957. log("done but 0");
  958. }
  959. if (e.button==1) { // middle click
  960. readPersistentData();
  961. if ( ! iframe)
  962. alert3(getStatusInfo());
  963. else {
  964. //window.open(location.href);
  965. top.location.href = document.location.href ;
  966. top.window.name+="SFS:KeepFrame";
  967. // confirm2(getStatusInfo()+"\n\n"+"Click 'Cancel' to edit this subpage's stored data", function(reply) {
  968. // if ( ! reply ) {
  969. // editFormData();
  970. // persistData();
  971. //} } );
  972. } //end else
  973. // e.preventDefault();
  974. // e.stopPropagation();
  975. if (element_to_highlight) element_to_highlight.style.borderStyle="none";
  976. //return false;
  977. }
  978. // e.preventDefault();
  979. // e.stopPropagation();
  980. // return false;
  981. } //end mouseUpCB()
  982. }
  983.  
  984. function setLive() {
  985. if ( ! link)
  986. return;
  987. var redSquareRing="data:image/gif;base64,R0lGODlhCQAMAIABAP8zM////yH5BAEKAAEALAAAAAAJAAwAAAIPhI+pwdvJgpPwoTiz2rsAADs=";
  988. redSquareRing="data:image/gif;base64,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"
  989. var redDot_img="data:image/gif;base64,R0lGODlhBwAHAIABAP8AAP///yH5BAEAAAEALAAAAAAHAAcAQAILTICGy6351mlgygIAOw==";
  990. redDot_img="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH2QcREzYMParL2gAAAIhJREFUGJV10DEOglAQBNAXCg6lVsZTaaOlYq8H8AAqvfFIIDWuhd+ABCaZZJOdndldOixQ4pVYYmWADVrECHd9p3dOFMQr8UTkX2GLOdwRRyIGPHSuN2gQzYiw7oR1Nlx2ChkecBlpnrvyCUtp8SLFVcT+/5jZb2I78Z4W62HSIl1Xo8K17/QByVJNP9L1aWcAAAAASUVORK5CYII=";
  991. redDot_img="data:image/gif;base64,R0lGODlhrAC1AKU7AIkTFIkTFYsTFIoTF4wTE4kUEokUE4wTFooUE4oUFIsUEooUFosUFIoUF4gVFIsUFowUFIsUF4wUFYsUGIkVFI0UE4oVEokVFY0UFIwUF4oVE4kVFo0UFYwUGIoVFIkVF4oVFY0UF4sVE4sVFIoVF4sVFYoVGIgWFIwVE4wVFIsVF4kWE4kWFI0VE40VFIoWE40VFo0VF4oWFosWFIsWFowWFIsWF4wWFo0WFIwWF40WFf///////////////////yH5BAEKAD8ALAAAAACsALUAAAb+wJ9wSCwaj8ikcqmUgH6lzKhErVJFD6GEye16v+CweEz03CQlD2iUuDE8VutIwiixQRI1ec/v+/l5Igl2IwgSKU4jHh5TcVRqGikjDGgIFSAqjAkoLH+en6BgKj8rEjAICR5ti3AqCDQeKmiOJZU5DKcgCzmMNSUoaxwJUKHFxp40DIojIykJNBILNQwqj04QIiUAtCWGCAwsEQw1OrgMM1MpdiwqGrE/w8fy80klPyMcKSIeOSkg6CQmgbhRQoOEGwpAgECQgiAtDwhsMFC38JkHdSkWsLDTzcMDOCk4iKBHct6JH2raSZKAwM1ED5cWeICRQMQ5Kv4aOVIBQpn+IkQ3Bo3I4ItRs2q/DlAoQULFuJJQ//jKlMLCIDjMlIFIoEhQCgQlEqjAAKIWHG6SLtTBw6ZZKioYul1AQYfExQTKNDAgFrWvGJYjHlAhwa2w4cOIE1Pxy5gJA0oQaHBlNkix5cuYw1rY0rgzETQXEKxRQfNr5tOoH5Z44rlxmpYWOLYLyyy17dSIRGdg3ZqkWjYlWkR5MAJGCQ6MkN5ebnmjvgQ1CNBI0ftYnRIqUMwAcZEKm1QQNBBmTh7xGzu68uipHuqsIgMTzNRpxkwDjArl8xdepOKGKg0lMMABe5+koc4Uc5iCgAckJIDACCqQAIx+FMYBGIQiqKDISwT+9oHABSQsoFOFJNrmwTiMdEjGHCJwoMGIJcaIGXQWZJCGPSp6kYIUJexTgkMyBmnZiSTkoMAjOXIRkCIxZALDWUJGeZg63WDX4ChJItHdCB2IKB+QUobpiAcSiMCWOiKMQEGWRdxwgSQI5DCCCBnVQKWYeFrR3SILEJTCiWwKkQYDGbwYQUs1wZinmAtM5pODKaSAQ6A/sDBRQP5ANAJbiy7KTFmlhRVQGh4kmcCLv3SqqmXjEWdIjjcwtAEcYK1qKzeTsUEDBGiwlxUI+WB167BxMJQADBoupAF7b9QwwgIaqJBAZcQSS1lB1TCzQGst2cEAcQxo8Fa1xGroAQf+JcAQ2J8jdNYMegmA4EACOjxLLrEkaACBKlzZkYABroX1AAvMzCmJiPcOWwEML9JXwgI+chaVM0NlgwAJGGSiaMKLgoBCU4UkQMKfedgB1alToMDxyoUxMMgCKrdLUqwpzFolyzhTwR0rAfJ2jEDBcpQzzhIkMMMDf1JHjwfOQisttUNznEDRKdiVzbLHaPjtJOJCGXXC3BWizgNYH6NGvPPWu8DXKzNTNQN2BShzKNkUTOezG7N9KwKuoFCUCBI4UAw+tGKssd4ceyBTWWRSoWEoIGSI+ORVNE6CJxIQF8JGlE8uUx6eGMVI55PT8ScJCHgobQ3fkI64CpKX2gf+CJaakbfrLMNSSwV8fMXADZNAjXvONdMVDQx8bJotmMPjnM4iEO41RgaMZAhc80NvWgcCYJGB/fdhhQEg+M3P7YWN5A+PpRci1Jq+6z5zgfD7rsPxxfX0d25IDV0EiEP+9ZPdEmKxBgB2TgL14UJPvGZAtgGHCzCQRAM7xwA8MGFBEpkg4haRiiUUYAYNyoYGHciMDCwhGj253QgTRiYz5SAJqiDT1Fb4tVlAJAnNQAFXykLDqG2qJts6wgLK4grm9TBxXymZ0sqQB0Rw54g5mwKZVKGlGnigHFAc2kQQaL4h1IIBuptFFjmWB2/wzwhsyIEdxDXGlS3IDhlQwQz+jDCaKYixjfcCy6cUUQQ4KOAFaeAJHhPGDH5gQAQwkNgPKMHDON5pkMSSgLnaUrYyyoQZJYNktSI3kdU8Tggq1CTL4iE8UX5NCAw05dBS9wPBqJJtQxjfK6M2hFDOslrDaNwtV6mIXfpwFHf0Jce0Z0th2ooRwTQmuepQg1Iqk1hPkMkzE9eGuEwzYQ6S5TWJ9QYIbZNcc2CACL85LGZ8hJzW2tR40GkrZoyFnbdiRprg2c520bOeNRDAPVVFmWLuk0TMgMI/O/aDBSRzoEKaGkoQmidBMVRMMvPnQ8kjRVdONEogqIFFLyqjdqmBo0JijTNBmhqPgQwVAfEiScn+s7CGSTAeAl3pcvK1r2khSKUyvY250KUuwYAyp8u5VrRMhlOgosZYyNoUKx1qVNTk6hm8IkJTU9OqQijypxYgAQmIM1XEaChD+6Jem+pgkxlcp6uFcVaACLbEIbQDAXFJJVrjkAKkWPFyRhANAnzBw7nSggIcqEMKrIjGDCRALcrwKzeexaBJ4KgIp+BiXxVrBdIwIDbhQ2NXEEFZRxhkEatJAqFo1VlHvAumR4hCpkqrJxtZoK10fNEjWTsCN9gPhz8aHWupgIKsZmIJdtDlbm0gARbANrXMcB9rudoFJ8hVsYxYKnATu1sqZKELc6juIwqCXeR0YxoZGddK2UL+pg0s6Ibss4MkFDKCDYz0oQ1ZgA7gEIO6HneAm7qIBzBgL6N2Uyxd5EImavEwNcwWpIL5E1bGgDc0+OOsJE1FdhPwpzEgAAYKmEMvcioSODxAjXyAwdrWoFyQOiVs0h0DV4wCVNGoAw0CHENNTzRZkO6jDinqg4Ys0MucqmNqeO3D7xigspwW4k2eIAHc1knSgXzSE95qcTZAkQpfyNQZof3EA5KRUw+4IMaeyK5MqWgMJ2AALOcoID3xEps8XDUUJcDAB84BAX/A80VqcPBjj5ENM3HHyvB0AncgoAOoeCADv3vA/9j5p1rIsyQcmALrdPDeZ7LAAjEQC5jn0aP+Jz33mW1RwJvpsQDC0AGeRIZD2fpSFonOMjmegZAHDLKayEhAm5pkxHVSwIC9orYxF5IiCQA3S3RkAw4X+LVnZAGCF12MBq8886ZqgYEgs0dxh1AEBF75Cnyk4g0qkgRpAFRjSA4iXtAJcHWUMURX0zADsfEACthEMjGbkg2sG7WKRIRrSDqhA5QaAuqwkwoEWqA7GvzTAg5OhQygLuBFuMAIUPAhCQzA3bjDA98OwIq9QryPeClalDVYiLWtK8UfByUbZPCgFSKwBBaQBcBSbgQGIRABHcDsBHmoiE3TnAjKcADGSfcgN/8cuB6AtgZdcXQvDL1z0Gm6FyLSgBz+AG7FIHiADh6wBghXaxJBQYEsAqQXM7XrvlJfQg3WkNF+LeIGFDgwsWJhgY3cAUI5EIy60+6FVETwAjTJsAlWhghBbIoDyvI538GQAksBR7YeGCc4CXyiRcBt1Yvfw1t5MkSvW4uD/lB25v2wo1jIfVj6rYFMRv+zRpPRHqBj/TyeLiTZ++UsO3wNu0pWiLCUxRmGiM0aIjXtzaBiLQ9TRNGAtQaU2N4zJdCnOjT0LBHvyU5rIEENlp8HE5iATFuRQA2KxLTIQ4QRjED78/siMwTMgCtfQRUhoPeNJzbjMRJUQ68XgQgAi379BMIdwCMJ5lQFXDERN6ACLqAQLtAeH7zGBgzACwDYdLbzAwyxF1wUALGyCC0RAJgke0EAADs=";
  992. redDot_img="data:image/gif;base64,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";
  993. redDot_off="data:image/gif;base64,R0lGODdhCAAIAMIEAO9mZvBmZu9nZu9oZ////////////////ywAAAAACAAIAAADD0g00UotBiirvXjaB5pQCQA7";
  994. redDot_off="data:image/gif;base64,R0lGODlhCAAIAMIEAO9mZvBmZu9nZu9oZwAAAAAAAAAAAAAAACH5BAEKAAQALAAAAAAIAAgAAAMPSDTRSi0GKKu9eNoHmlAJADs=";
  995. link.addEventListener("mouseover",function(){
  996. //log("mo "+form_data); this.style.opacity=0.7;
  997. if ( ! (form_data||click)) img.src=redDot_img;
  998. //else img.style.height=img.style.width=20;
  999. }, false);
  1000. link.addEventListener("mouseout",function() {
  1001. this.style.opacity=1;
  1002. if ( ! (form_data||click) ) img.src=redDot_off;
  1003. //else img.style.height=img.style.width=12;
  1004. }, false);
  1005. // link.style.opacity=".6";
  1006. link.style.opacity="1";
  1007. link.style.rightMargin="10px";
  1008. with (img.style) {
  1009. cursor="pointer";
  1010. position="fixed";
  1011. top="5px";
  1012. left="18px";
  1013. if (page_object && page_object.position) {
  1014. top=page_object.position.top+"px";
  1015. left=page_object.position.left+"px";
  1016. var msg="Simple Form Saver, user set image position to left:"+left+", top:"+top;
  1017. GM_log(msg);
  1018. window.status=msg;
  1019. }
  1020. // cssFloat="left";
  1021. setProperty("display", "inline", "important");
  1022. setProperty("z-index", "2147483644", "important");
  1023. setProperty("min-width", "6px", "important");
  1024. //zIndex=9999;
  1025. }
  1026. /* img2.src="data:image/gif;base64,R0lGODlhBwAHAIABAP8AAP" */
  1027. /* img2.width=12; */
  1028. /* img2.height=12; */
  1029. //log("setlive");
  1030. if (form_data || click) {
  1031. registerMenus(true);
  1032. img.src=redSquareRing;
  1033. //log("setLive has Form data");
  1034. with (img.style) {
  1035. setProperty("border","", "important");
  1036. setProperty("border-style","outset", "important");
  1037. borderWidth=".23em";
  1038. opacity=1.0;
  1039. borderColor="rgba(200, 200, 200, .9)";
  1040. // img.height=12;
  1041. // img.width=12;
  1042. height="14.4px"; //".6em"
  1043. width="12px"; //".5em"
  1044. height = ! form_data ? "10px" : "12px"; //"1.0em" : "1.5em";
  1045. width=height; //!form_data ? "1.0em" : "1.5em";
  1046. link.title="Click to Fill in Form (&/or replay a recorded click). Right click to Save form. Drag n Drop to move icon."
  1047. +" Middle click "+(iframe?"to open iframe in own window/tab in order to allow access to GM settings for this part of the page."
  1048. :"to get forms' status info.")
  1049. + (iframe ? " (Within iframe: "+location.href+")." : "" ) + " Simple Form Saver, GM script.";
  1050. link.setAttribute("noautohide","true");
  1051. }
  1052. //img2.style.display="inline";
  1053. //img2.style.setProperty("border-style", "none", "important");
  1054. full_icon=true;
  1055. }
  1056. else { // no form data, nor click
  1057. log("setLive, no form data");
  1058. if (click) registerMenus(true);
  1059. img.src= redDot_off;
  1060. img.height=img.width="10px";
  1061. with (img.style) {
  1062. height="";
  1063. width="";
  1064. height=".5em"
  1065. width=".5em"
  1066. if (!click) {
  1067. borderStyle="none";
  1068. borderWidth="";
  1069. //if(!click)
  1070. setProperty("border","none", "important");
  1071. link.title="Click once to save form data "+(click?"or to replay a click":"")+"; middle-click to get forms status info."
  1072. + (iframe ? " (Within iframe)." : "" ) +" Can drag and drop to move icon. See options to remove. At: "+page_key+". Added by GM script, Simple Form Saver.";
  1073. }
  1074. }
  1075. //img2.style.display="none";
  1076. full_icon=false;
  1077. }
  1078. if (reddot==false)
  1079. img.style.display="none";
  1080. else
  1081. img.style.display="";
  1082. // $(img).draggable();
  1083. // $(reddot).draggable();
  1084. }// end setLive()
  1085.  
  1086. function inOutSet(set) {
  1087. if ( full_icon
  1088. || ( ! form_data && click )
  1089. || set
  1090. ) {
  1091. if ( ! img.style.borderWidth)
  1092. img.style.borderWidth=".1px";
  1093. if (!this.prev) {
  1094. img.style.setProperty("border-style", "inset", "important");
  1095. this.prev=true;
  1096. }
  1097. else {
  1098. img.style.setProperty("border-style", "outset", "important");
  1099. this.prev=false;
  1100. }
  1101. }
  1102. }
  1103.  
  1104. function checkPageLayout(){
  1105. var logo, p, gp; logos=getByIdOrClass("logo-img-2", "logo", "gh-log", "p-logo", "mw-panel", "logocont");
  1106. for( var i =0; logo=logos[i], i < logos.length; i++ ) {
  1107. logo.style.setProperty("margin-left", "0px", "important");
  1108. // if (p && p.parentNode && p.parentNode.style) p.parentNode.style.setProperty("margin-left", "0px", "important");
  1109. // if (p && p.style) p.style.setProperty("margin-left", "0px", "important");
  1110. logo.style.setProperty("z-index", "0", "important");
  1111. if ( ! checkPageLayout.count) { checkPageLayout.count=10;}
  1112. logo.addEventListener("DOMAttrModified", checkMargins, 0);
  1113. }
  1114. return;
  1115. }
  1116.  
  1117. function checkMargins(e){
  1118. log("checkMargins");
  1119. if (e.target != this) return;
  1120. if ( this.style.marginLeft[0] != 0) this.style.setProperty("margin-left", "0px", "important");
  1121. if ( ! -- checkPageLayout.count )
  1122. for( var j =0; logoj=logos[j], j < logos.length; j++ )
  1123. logoj.removeEventListener("DOMAttrModified", checkMargins, 0);
  1124. }
  1125.  
  1126. function findRenamedInput(name, i, saved_value)
  1127. {
  1128. var n, result, saved_data=page_object.form_data;
  1129. for (n in saved_data) {
  1130. if ( i == saved_data[n].i ) {
  1131. result=saved_data[n].v
  1132. break;
  1133. }
  1134. }
  1135. return [result, n, i ];
  1136. }
  1137.  
  1138. function getElementsByTagNames(list,obj, full) {
  1139. if (!obj || ! obj.getElementsByTagName) var obj = window.document;
  1140. var tagNames = list.split(',');
  1141. var resultArray = new Array();
  1142. if (obj.tagName && list.match(obj.tagName.toLowerCase())){
  1143. resultArray.push(obj);
  1144. }
  1145. for (var i=0;i<tagNames.length;i++) {
  1146. var tags = obj.getElementsByTagName(tagNames[i]);
  1147. for (var j=0;j<tags.length;j++) {
  1148. if ( tags[j].type
  1149. && ! /^(submit|hidden|image|reset|button)$/.test( tags[j].type.toLowerCase() )
  1150. && ! (tags[j].disabled == true) || full ) {
  1151. resultArray.push(tags[j]);
  1152. }
  1153. }
  1154. }
  1155. var testNode = resultArray[0];
  1156. if (!testNode) return [];
  1157. if (testNode.sourceIndex) {
  1158. resultArray.sort(function (a,b) {
  1159. return a.sourceIndex - b.sourceIndex;
  1160. });
  1161. }
  1162. else if (testNode.compareDocumentPosition) {
  1163. resultArray.sort(function (a,b) {
  1164. return 3 - (a.compareDocumentPosition(b) & 6);
  1165. });
  1166. }
  1167. return resultArray;
  1168. }
  1169.  
  1170. function chopLongString(form_string) {
  1171. if (form_string.length > 1700) form_string=form_string.replace(/[\t\n]/g," ").replace(/\u26ab/g,tab_bullet).replace(/\t/g,"")
  1172. return form_string;
  1173. }
  1174.  
  1175. function getStatusInfo() {
  1176. var data= form_data, n, list="";
  1177. for (n in data)
  1178. if (data[n].p)
  1179. list+=n+",\t";
  1180. if (list) list=list.substring(0, list.length-2)
  1181. var page_list=null, page_list2="";
  1182. // if ( ! form_data) { //////////////////////////////////////
  1183. page_list=bullet_tab;
  1184. // var tinputs = getElementsByTagNames('input,textarea,select', null, true);
  1185. var tinputs = getElementsByTagNames('input,textarea,select');//was showing submit buttons with no text.
  1186. for(var i=0; i<tinputs.length; i++) {
  1187. var inp=tinputs[i];
  1188. var type=0, option=0;
  1189. if (inp.tagName =="INPUT")
  1190. type=inp.type
  1191. else {
  1192. type=inp.tagName
  1193. option="";
  1194. if (inp.tagName=="SELECT" && Number(inp.value)) {
  1195. var options=inp.options;
  1196. option="\t\t(~"+options.item(inp.selectedIndex).textContent.replace(/[\n\t( )]/g,"")+")\t";
  1197. }
  1198. }
  1199. var val=inp.value;
  1200. var style=window.document.defaultView.getComputedStyle(inp, null)||{};
  1201. var visibility=( (style.display=="none" || style.visibility=="hidden") ? " (invisible)" : "");
  1202.  
  1203. page_list+=(inp.name?inp.name:"null")+"::"
  1204. +(val? ( inp.type=="password" && ! unhide ? val[0] + "..." + val[val.length-1]+"["+val.length+"]"
  1205. : val +(unhide?" (unhidden":"") )
  1206. :"null")
  1207. +(option?option:"\t\t\t\t")+"\t"+type+visibility+"\n"+bullet_tab;
  1208. if (inp.type=="password" && ! unhide)
  1209. page_list2+=val[0]+"..."+val[val.length-1]+"["+val.length+"]";
  1210. else
  1211. page_list2 += val +"\n";
  1212. } // end for inputs[].
  1213. page_list=page_list.substring(0,page_list.length-2);
  1214. // } // end if ! form_data ///////////////////////////////////////
  1215. if ( form_data) { page_list=null }
  1216. var ip_names="";
  1217. for (var i in input_names) ip_names+=i+", "; ip_names=ip_names.substr(0, ip_names.length-2);
  1218. var form_string=page_list || formDataToFromString(true);
  1219. form_string=chopLongString(form_string);
  1220. log("get xpath ");
  1221. click_elem=getXPathElem();
  1222. var forms_info =
  1223. ( form_data ? "Previously saved values and names are listed below"
  1224. : "Page Values and form names have not been saved for this page")
  1225. +" (format: name ["+mdash_char+"default]::value"+( form_data ? "":"\t\tType of (name::value)")+")."
  1226. + (form_data ? "\n\n"+bullet_tab+form_string : "\n\n"+form_string+"" )
  1227. + (list ? "\n\nThere are duplicates whose values may differ from the above for the input(s) named:\t "+list+"." : "")
  1228. var settings_info=""
  1229. +"Names of all inputs: "+ip_names
  1230. +"\n\nAutomatic form filling on this page "
  1231. + (page_object.automatic ? "is on. " : (page_object.automatic !== false ? "has not been turned on, and is off. " : "and click-replay, have been explicitly disabled. "))
  1232. +(click ? "\nClick auto-"+(click.ctrlShift?"delete":"replay")+", is set for"
  1233. +(click.own_href == "any" ? " any page with this path. " : " only this specific page. ")
  1234. +"Page's element (XPath) to be auto-"+(click.ctrlShift ? "deleted":"clicked")+" is:\n\n\t\t"+ ( ! (click_elem && click_elem.xx) ? page_object.click.xpath : page_object.click.xxpath + " as xpath" ) +""
  1235. : "Nothing to replay on this page. " )
  1236. + ( click && ! click_elem
  1237. ? " (the element is not currently on this page" + (click.href ? " but has href, " + click.href : "" ) + ")."
  1238. : ( click && (href == click.own_href || click.own_href == "any" )
  1239. ? (click?"\n\nThe element is present on the page.\nIts value and text is: "+click_elem.value+" "+click_elem.textContent+", it's href is:"+click.own_href : "none")
  1240. : (click?", the element is on page but it is for another webpage: \n\t\t"+click.own_href : "none" )))
  1241. +(click && click.onclick_js? "\n\t\tJava to replay: "+ click.onclick_js : "")
  1242. +(click && click.further_clicks? "\n\nFurther clicks are on elem(s) (XPath(s)): "+click.further_clicks.join(", "):"")
  1243. + "\n\n"+ (suspend_replay ? "Click auto-replay is suspended in general." : "Click auto-replay on all pages is not suspended.")
  1244. + (inputs.length ? "\n\nNumber of useable input areas on page (some may currently be hidden): "+(inputs_on_page) : "")
  1245. +"\nIcon status is: "+(reddot ? "show.": "don't show.")
  1246. +(iframe ? "\nHTML Frame." :"")
  1247. +"\nCurrent values:\n"+page_list2;
  1248. var status="Page "
  1249. + ( (form_data || click) ? "stored: " : " not stored: ")
  1250. + page_key + (regexp_page_match ? " (matches as regexp within, "+regexp_page_match+", specifically)." : ".") + widener;
  1251. if (form_data)
  1252. status +="\n\n"+forms_info+"\n\n"+settings_info;
  1253. else {
  1254. if (page_list)
  1255. status+="\n"+settings_info+"\n\n"+forms_info;
  1256. else
  1257. status+="\n"+settings_info;
  1258. }
  1259. if (click && click_elem) {
  1260. element_to_highlight=click_elem;
  1261. with (element_to_highlight.style) { borderColor= "red"; borderWidth= "10px" ; borderStyle= "solid" ; }
  1262. }
  1263. var flen=window.frames.length;
  1264. if (flen) status+="\n\n"+"Window contains "+flen+" iframe(s), please use icon to fill or save forms within iframes or invoke GM menu shortcut (alt-m) whilst focus is in iframe.";
  1265.  
  1266. return status;
  1267. }
  1268.  
  1269.  
  1270. function getXPath(elt, counting) {
  1271. function getElementIdx(elt) {
  1272. var count = 0; // zero meaning only tag of that type here.
  1273. for (var sib = elt.previousSibling; sib ; sib = sib.previousSibling) {
  1274. if(sib.nodeType == 1 && sib.tagName == elt.tagName) {
  1275. if (count==0) count=1;
  1276. count++;
  1277. }
  1278. }
  1279. if (count==0)
  1280. for (var sib = elt.nextSibling; sib ; sib = sib.nextSibling) {
  1281. if(sib.nodeType == 1 && sib.tagName == elt.tagName) {
  1282. count=1;break; //1 signalling 1 of many
  1283. }
  1284. }
  1285. return count;
  1286. }
  1287. var path = "";
  1288. for (; elt && elt.nodeType == 1; elt = elt.parentNode) {
  1289. idx = getElementIdx(elt);
  1290. xname = elt.tagName.toLowerCase();
  1291. if (idx > 0) {
  1292. if (elt.id && ! counting && ! /[0-9]{3,}/.test(elt.id) )
  1293. xname+="[@id='"+elt.id+"']";
  1294. else
  1295. xname += "[" + idx + "]";
  1296. }
  1297. path = "/" + xname + path;
  1298. }
  1299. return path;
  1300. }
  1301.  
  1302. function getXPathElem() {
  1303. if ( ! click) return;
  1304. var snap=uwin.document.evaluate(click.xpath,uwin.document,null,6,null), xx, xpresult;
  1305. log("Called evaluate on "+click.xpath+", results: "+snap.snapshotLength+" pagekey: "+page_key);
  1306. var elem=snap.snapshotItem(0);
  1307. if (snap.snapshotLength > 1 || snap.snapshotLength == 0) {
  1308. snap=uwin.document.evaluate(click.xxpath,uwin.document,null,6,null);
  1309. xx=true;
  1310. }
  1311. if (snap.snapshotLength > 0)
  1312. xpresult=snap.snapshotItem(0);
  1313. if (!xpresult) log("No xpath result, try id and class if singular, id: "+click.id+", class: "+click.className+".");
  1314. if (!xpresult && click.id) { xpresult=document.getElementById(click.id); if (xpresult) xpresult.classed=click.id;}
  1315. if (!xpresult && click.className) {
  1316. xpresult=document.getElementsByClassName(click.className);
  1317. if (xpresult.length==1) { xpresult=xpresult[0]; xpresult.classed=click.className;} else xpresult=undefined;
  1318. }
  1319. if (xpresult && xx) xpresult.xx=true;
  1320. log(" got xp: "+xpresult);
  1321. return xpresult;
  1322. }
  1323.  
  1324. function useCorrect_page_object() {
  1325. if ( ! regexp_page_match ) return;
  1326. log("cfm");
  1327. var reply=confirm( "This page's form data is already stored under "
  1328. + page_key + " (as regexp). It matches the current page, "+regexp_page_match+"." + widener
  1329. +"\n\nActual page matched a more general page on which form data was previously stored."
  1330. +"\n\nClick 'OK' to save under general page that is already stored."
  1331. +"\n\nClick 'Cancel' to save only for this specific page." )
  1332. if ( reply ) return;
  1333. reply=site+window.document.location.pathname;
  1334. var po=hash_host_list[reply]
  1335. if ( ! po) po=new Object();
  1336. hash_host_list[reply]=po;
  1337. //po.click=click; // copy old data?
  1338. persistData();
  1339. }
  1340.  
  1341. function persistData() {
  1342. hash_host_list["data_version"]=data_version;
  1343. hash_host_list["suspend_replay"]=suspend_replay;
  1344. hash_host_list["reddot"]=reddot;
  1345.  
  1346. if (page_object && countMembers(page_object))
  1347. hash_host_list[page_key]=page_object;
  1348. else
  1349. delete hash_host_list[page_key]
  1350. updateVars();
  1351. var stringed=uneval(hash_host_list);
  1352. GM_setValue('hostFormsList', stringed);
  1353. log("PerSIST end ");
  1354. if (page_object) log("poauto "+page_object.automatic);
  1355. write_once=true;
  1356. }
  1357.  
  1358. function readPersistentData(origin) {
  1359. // if ( write_once == false) return;
  1360. var data;
  1361. data=GM_getValue('hostFormsList');
  1362. //log("readPersistentData "+(!data)+" "+origin);
  1363. if (data) {
  1364. try { hash_host_list = eval(data) } catch(e) {
  1365. var etext=e+"";
  1366. if (etext.indexOf("CSP") == -1) {
  1367. alert("Simple Form Saver ERROR, "
  1368. +"dumping bad data, all form data cleared, data corrupted: "+e
  1369. +". Simple Form Saver DUMP "+data);
  1370. hash_host_list={};
  1371. }
  1372. else GM_log("CSP error");//security at some sites?
  1373. }
  1374. if (hash_host_list["data_version"] >= 1) { // have new data default to false or empty then no rev change needed.
  1375. suspend_replay=hash_host_list["suspend_replay"];
  1376. }
  1377. if (hash_host_list["data_version"] < data_version) {
  1378. window.status="Simple Form Saver database format increment "+data_version+". Consider saving some forms again or of reverting versions if problems are encountered";
  1379. GM_log("Simple Form Saver database changed. Consider saving forms again if problems encountered");
  1380. }
  1381. }
  1382. if (countMembers(hash_host_list) == 0) {
  1383. hash_host_list["suspend_replay"]=suspend_replay;
  1384. hash_host_list["reddot"]=reddot;
  1385. }
  1386. updateVars();
  1387. write_once=false;
  1388. //log("Read Persist key: "+ page_key + "\n\nPage data:\n "+uneval(page_object.form_data))
  1389. }
  1390.  
  1391. function updateVars() { //called after reading and before writing underlying data.
  1392. site=getSite();
  1393. page_key=site;
  1394. if (page_key) {
  1395. try { // pathname gives file after site name but before any additional stuff eg, after a '?' in long href.
  1396. page_key+=window.document.location.pathname; } catch(e) { page_key+=window.document.title.substring(0,10);}
  1397. }
  1398. else
  1399. page_key=window.document.title.substring(0,40);
  1400. checkForInputs();
  1401. getMatchingPageObject();
  1402. form_data=page_object.form_data
  1403. click=page_object.click;
  1404. suspend_replay=hash_host_list["suspend_replay"];
  1405. reddot=hash_host_list["reddot"];
  1406. //log("updated vars, form_data is: "+form_data+", click:"+click+", key: "+page_key);
  1407. }
  1408.  
  1409. function getMatchingPageObject(key_in) { //dual function updates global vars or returns value;
  1410. var key=key_in||page_key;
  1411. var po=hash_host_list[key];
  1412. var regexp_match=false;
  1413. if ( ! po) {
  1414. for(var i in hash_host_list) { // i as regexp in longer strings site,page_key
  1415. if (i.match("/")) continue;// only bare site name matches all.
  1416. if (site.match(i) || key.match(i) ) {
  1417. //log("Match found in hash for:"+i+", in "+site+", &/or in page key: "+key)
  1418. po=hash_host_list[i];
  1419. regexp_match=key;
  1420. key = i;
  1421. break;
  1422. }
  1423. }
  1424. if ( ! po)
  1425. po=new Object();
  1426. } // end if ! po
  1427. if (!key_in) { //update globals
  1428. page_key=key;
  1429. page_object=po;
  1430. regexp_page_match=regexp_match;
  1431. }
  1432. else return po;
  1433. }//end matchPageObject()
  1434.  
  1435. function getSite() {
  1436. var host;
  1437. var domain_regexp=/((\.\w+\.|^)\w+.\w*$)/;
  1438. try{
  1439. host=window.document.location.host;
  1440. href=window.document.location.href;
  1441. if ( ! host)
  1442. host="localfile";
  1443. }
  1444. catch(e){
  1445. host="";
  1446. if (window.document.title)
  1447. GM_log("can't get site for doc: "+uwin.document.title)
  1448. }
  1449. try { if ( ! href ) href = window.document.title } catch(e) { GM_log("Cant get doc href or title")}
  1450. return host;
  1451. }
  1452.  
  1453. function insertCode(code, win) {
  1454. if (code.length > 1) {
  1455. if (!win) win=window;
  1456. var script = win.document.createElement("script");
  1457. script.type = "application/javascript";
  1458. script.textContent = "(function() {" + code + "})();"; // for to exec anonymously, ie "(funcX)()" a function.
  1459. win.document.body.appendChild(script);
  1460. return true;
  1461. }
  1462. }
  1463.  
  1464. function replayClick(manual) {
  1465. setTimeout(function() { replayClickSwapped(manual); }, 100);
  1466. }
  1467. function replayFurtherClicks() {
  1468. if (!click.further_clicks) return;
  1469. for(var i=0, delay=0; i< click.further_clicks.length; i++, delay+=200) {
  1470. setTimeout(function(j) {
  1471. var elem, snap=uwin.document.evaluate(click.further_clicks[j],uwin.document,null,6,null);
  1472. GM_log("Replaying further clicks after 2 sec pause, on xpath, "+click.further_clicks[j]+", # matching elements: "+snap.snapshotLength);
  1473. elem=snap.snapshotItem(0);
  1474. if (!elem) return;
  1475. elem.relatedTarget=elem;
  1476. fakeClick(elem);
  1477. var tstamp=(new Date().getTime());
  1478. log(" tstamp "+tstamp);
  1479. }, 2000+delay, i);
  1480. }
  1481. }
  1482.  
  1483. //function replayClick(manual) {
  1484. function replayClickSwapped(manual) {
  1485. log("replayClickSwapped "+click+", "+suspend_replay);
  1486. if ( ! click || ( ( suspend_replay || page_object.automatic === false )
  1487. && ! ( manual || page_object.automatic === true) )) {
  1488. if (suspend_replay && click)
  1489. GM_log("Click auto-replay suspended from playing. ");
  1490. return;
  1491. }
  1492. log("ReplayClick(), click's href: "+click.own_href+". href: "+href+", manual "+manual);
  1493. try{
  1494. var prejudice={};
  1495. function execInPageContext(nohref) {
  1496. var code="";
  1497. if ( ! nohref && click.href)
  1498. code+=click.href.substring(11);
  1499. code+=";"+(click.onclick_js ? click.onclick_js : "");
  1500. return insertCode(code);
  1501. }
  1502. if ( checkIfIntervalLongEnough(prejudice) || manual) {
  1503. if (click.xpath && ( href == click.own_href || click.own_href == "any" ) ) {
  1504. elem=getXPathElem();
  1505. log("replay-- matches href or href=any, elem.innerHTML: "+(elem?elem.innerHTML:""));
  1506. var java_run;
  1507. if ( ! elem ) {
  1508. if ( regexp_page_match) return;
  1509. no_element_for_click=true;
  1510. log("replay-- doc, "+page_key+" changed. Recorded Xpath stale: "+click.xpath +(click.href ? ". Try href: " + click.href : "" ) + (click.onclick_js ? ". Try javascript: "+click.onclick_js : "" ) )
  1511. if (click.href && click.href.substring(0,11) != "javascript:" ) {
  1512. java_run=execInPageContext(true);
  1513. window.document.location = click.href;
  1514. }
  1515. else
  1516. java_run=execInPageContext();
  1517. if (java_run || click.href)
  1518. persistClickEvidence();
  1519. }
  1520. else { // else ! !elem
  1521. if ( click.ctrlShift) try {
  1522. elem.parentNode.removeChild(elem); var msg="Auto-Deleted element "+click.xpath; window.status=msg; }
  1523. catch(e) { var msg="Can't delete "+click.xpath; window.status=msg; GM_log(msg); }
  1524. else {
  1525. no_element_for_click=false;
  1526. log("replay-- Have element, path: "+click.xpath + (click.href ? ". Try href: " + click.href : "" ) +(elem.href ? ". Try href: " + elem.href : "" ) +(click.onclick_js ? ". Try javascript: "+click.onclick_js : "" )
  1527. + ", elem.click:"+ (elem.click?" true.":" none."));
  1528. if (elem.tagName=="A" && elem.href != "" && elem.href.substring(0,11) != "javascript:" ) {
  1529. if (elem.target == "_blank") unsafeWindow.open(elem.href, elem.target);
  1530. else window.document.location = elem.href;
  1531. }
  1532. else {
  1533. if (click.href && click.href.substring(0,11) == "javascript:")
  1534. java_run=execInPageContext()
  1535. else if (click.onclick_js)
  1536. java_run=execInPageContext()
  1537. }
  1538. if (elem.click ) {
  1539. // var scripts=window.document.getElementsByTagName("script");
  1540. // for (var i in scripts) if ( scripts[i] && ! /(^\s*$)/.test(scripts[i].textContent || " ") ) try {
  1541. // eval.call(uwin, scripts[i].textContent); } catch(e){ GM_log("\nParse Error: "+e); }
  1542. ///above needs remove scripts too.
  1543. //eval ("elem.click()"); // an input
  1544. //dblcheck triggering of event:
  1545. //elem.addEventListener("click", function() {alert("clicked"); }, 0);
  1546. elem.click();
  1547. } else {
  1548. var pseudo_event = window.document.createEvent("MouseEvents");
  1549. // type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)
  1550. pseudo_event.initMouseEvent("click", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
  1551. elem.dispatchEvent(pseudo_event);
  1552. }
  1553. } // else ! click.ctrlShift
  1554. persistClickEvidence();
  1555. } // end if/else have Xpath elem.
  1556. var msg="Info: Finished replaying click/delete, at: "+site
  1557. + ( elem ? ". Xpath ok "
  1558. + (!elem.xx ? click.xpath : click.xxpath+" as xpath")+". "
  1559. + (elem.classed ? elem.classed+" class used, Xpath ignored. ":"")
  1560. + ( click.ctrlShift ? "Success, removed element. "
  1561. : ( elem.tagName=="A" ? "Visited link " + (elem.target == "_blank" ? " in new window. ": ". " ) : "")
  1562. + (elem.click ? "Invoked input tagged: "+elem.tagName+"; text: "+elem.textContent+"; class, "+elem.className+"; value: "+elem.value+", with click() function. " : "Created a pseudo click event. ") )
  1563. : ". Failed, Xpath not on page. "
  1564. + (click.href ? "Visiting link "+click.href + ". " : "") )
  1565. + (java_run ? "Ran javascript code: "+click.onclick_js : "" );
  1566. hash_host_list.msg=msg;
  1567. GM_log(msg);
  1568. //persistData();
  1569. } // end if click.xpath
  1570. } // end if checkIfIntervalLongEnough
  1571. else { // last click replay was < 10 secs ago.
  1572. if ( ! prejudice.obj) { // prevent reload and auto replay on reload looping here.
  1573. persistClickEvidence(10000);// to prevent looping here, from reload(false) below.
  1574. GM_log("Simple Form Saver--reload page "+page_key);
  1575. uwin.document.location.reload(false);
  1576. }
  1577. return true;
  1578. }//end else
  1579. replayFurtherClicks();
  1580. } catch(e) {GM_log("Replay error, "+e.lineNumber+" "+e); throw(e); }
  1581. }
  1582.  
  1583. function persistClickEvidence(prejudice) { //if click causes reload ensures no looping.
  1584. click_evidence={};
  1585. click_evidence.tstamp=(new Date().getTime() + ( prejudice ? prejudice : 0));
  1586. if (prejudice)
  1587. click_evidence.prejudice=true;
  1588. page_object.click_evidence=click_evidence;
  1589. local_setValue("click_evidence", click_evidence);
  1590. //persistData();
  1591. //log("persistClickEvidence "+click_evidence.tstamp+", prej: "+prejudice);
  1592. }
  1593.  
  1594. function checkIfIntervalLongEnough(prejudice) {
  1595. var result=true, stamp=new Date().getTime();
  1596. var dc=local_getValue("click_evidence","");
  1597. page_object.click_evidence=dc;
  1598. //log("Check click_evidence: "+dc.tstamp+", prej: "+dc.prejudice);
  1599. //var dc=page_object.click_evidence;
  1600. if (dc && dc.sealed) {
  1601. var diff=stamp - dc.tstamp;
  1602. //log2("Interval since last auto-click: "+diff/1000+". If < 5 no auto re-click.");
  1603. if ( diff < 5000)
  1604. result=false;
  1605. if (diff < 0 && prejudice)
  1606. prejudice.obj=true;
  1607. if (prejudice) delete page_object.click_evidence;
  1608. //persistData();
  1609. local_setValue("click_evidence", page_object.click_evidence);
  1610. }
  1611. //log("checkIfIntervalLongEnough: "+result+" "+(dc? "s: "+dc.sealed+" t: "+dc.tstamp+" p:"+dc.prejudice:""));
  1612. return result;
  1613. }
  1614.  
  1615. function fakeClick(target) {
  1616. var e = window.document.createEvent("MouseEvents");// create event
  1617. var pseudo_event_md = window.document.createEvent("MouseEvents");// create event
  1618. var pseudo_event_mu = window.document.createEvent("MouseEvents");// create event
  1619. var pseudo_event_click = window.document.createEvent("MouseEvents");// create event
  1620. //type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget
  1621. pseudo_event_md.initMouseEvent("mousedown", true, e.cancelable, e.view, e.detail,
  1622. e.screenX, e.screenY, e.clientX, e.clientY,
  1623. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  1624. 0, target.relatedTarget);
  1625. pseudo_event_mu.initMouseEvent("mouseup", true, e.cancelable, e.view, e.detail,
  1626. e.screenX, e.screenY, e.clientX, e.clientY,
  1627. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  1628. 0, target.relatedTarget);
  1629. pseudo_event_click.initMouseEvent("click", true, e.cancelable, e.view, 318153143,
  1630. e.screenX, e.screenY, e.clientX, e.clientY,
  1631. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  1632. 0, target.relatedTarget);
  1633. pseudo_event_click.fakeClick=true;
  1634. target.dispatchEvent(pseudo_event_md);
  1635. target.dispatchEvent(pseudo_event_mu);
  1636. target.dispatchEvent(pseudo_event_click);
  1637. //log2("Dispatched fake click on: "+target.tagName+", "+target.id+", "+target.className+", "+target.textContent)
  1638. }
  1639.  
  1640. function ordinal(n) {
  1641. var sfx = ["th","st","nd","rd"];
  1642. var val = n%100;
  1643. return n + (sfx[(val-20)%10] || sfx[val] || sfx[0]);
  1644. }
  1645.  
  1646. function updateValueTitles() {
  1647. var just_inputs = getElementsByTagNames('input');
  1648. for(var i=0; i<just_inputs.length; i++) {
  1649. var title=just_inputs[i].title;
  1650. title=title.replace(/Value:.*(?=Type:)/,"Value: "+just_inputs[i].value+". "); //regexp "lookaround", not matching for replace /1
  1651. just_inputs[i].title=title
  1652. }
  1653. }
  1654. function checkForInputs() {
  1655. if (no_of_inputs != selects.length + rinputs.length + tareas.length) {
  1656. no_of_inputs = selects.length + rinputs.length + tareas.length;
  1657. var new_inputs = getElementsByTagNames('input,textarea,select');
  1658. //log("checkForInputs "+new_inputs.length);
  1659. var new_names={}, new_ones=[];
  1660. text_inputs = inputs_on_page = 0;
  1661. for(var i=0; i<new_inputs.length; i++) {
  1662. var input=new_inputs[i];
  1663. var name=input.name;
  1664. var type=input.type;
  1665. if (type=="checkbox")
  1666. name+=mdash_char+input.value;
  1667. if ( ! name) { name="yyNoName"+i; } //new_inputs[i].name=name; } ///RENAMEs
  1668. new_names[name]=input;
  1669. if ( input_names[input.name] )
  1670. continue;
  1671. new_ones.push(input);
  1672. var style=window.document.defaultView.getComputedStyle(input, null)||{};
  1673. if (style.display=="none" || style.visibility=="hidden") continue;
  1674. if (reddot) {
  1675. if (input.title.match(/Name:.*Value:.*Type/)) continue;
  1676. if ( ! ( /q|Gw/.test(name) && type=="text") && ! /textarea/i.test(input.nodeName)) {
  1677. if (type=="checkbox") input.title+= "Name"+mdash_char+"value: "+name;
  1678. else if (name) input.title+= "Name: "+name;
  1679. else if (input.id) input.title+="Id: "+input.id;
  1680. var val=input.value;
  1681. input.title+= ( /checkbox/.test(type) ? ""
  1682. : ". Value: " + ( val ?
  1683. ( input.type=="password" ? val[0] + "..." + val[val.length-1] : val)
  1684. : null)
  1685. );
  1686. input.title+=". Type: "+type+".";
  1687. if (/checkbox|radio/.test(type)) {
  1688. // var w=parseInt(style.width), h = parseInt(style.height), min_size=18, hi_size=20;
  1689. // //log("Size of tick "+h+"x"+w);
  1690. // var fixed_sizes=GM_getValue("fixed_sizes", false);
  1691. // if ( (w < min_size || h < min_size) && ! fixed_sizes) {
  1692. // //log("Too small, set to "+min_size);
  1693. // input.style.setProperty("height", min_size+"px", "important");
  1694. // input.style.setProperty("width", min_size+"px", "important");
  1695. // input.style.setProperty("-moz-appearance", "none", "important");
  1696. // // input.addEventListener("mouseover", function(){ this.style.setProperty("height", hi_size+"px", "important"); this.style.setProperty("width", hi_size+"px", "important"); }, false);
  1697. // // input.addEventListener("mouseout", function(){ this.style.setProperty("height", min_size+"px", "important"); this.style.setProperty("width", min_size+"px", "important"); }, false);
  1698. // }
  1699. //var roll=""; for(var j in style) roll+=j+":"+style[j]+"::"+style[style[j]]+", "
  1700. //log("Computed style "+roll);
  1701. input.style.MozAppearance="none";
  1702. input.style.height="1em";
  1703. input.style.width="1em";
  1704. }
  1705. } // if ! q|Gw
  1706. if (iframe) input.title+=" (Within iframe).";
  1707. if (/text/.test(type)) text_inputs++;
  1708. if ( ! /hidden|submit|image|reset/.test(type) ) inputs_on_page++;
  1709. }
  1710. }
  1711. inputs=new_inputs;
  1712. input_names=new_names;
  1713. return new_ones;
  1714. } //end if change in number of inputs
  1715. else {
  1716. var new_inputs = getElementsByTagNames('input,textarea,select');
  1717. for(var i=0; i<new_inputs.length; i++) {
  1718. var input=new_inputs[i];
  1719. var name=input.name;
  1720. var type=input.type;
  1721. var style=window.document.defaultView.getComputedStyle(input, null)||{};
  1722. if (style.display=="none" || style.visibility=="hidden") continue;
  1723. if (reddot) {
  1724. // if (input.title.match(/Name:.*Value:.*Type/)) //update title value if necessary
  1725. input.title="";
  1726. if (type=="checkbox") input.title+= "Name: "+mdash_char+"value: "+name;
  1727. else if (name) input.title+= "Name: "+name;
  1728. else if (input.id) input.title+="Id: "+input.id;
  1729. var val=input.value;
  1730. input.title+= ( /checkbox/.test(type) ? ""
  1731. : ". Value: " + ( val ? ( input.type=="password" ? val[0] + "..." + val[val.length-1] : val ) : null));
  1732. input.title+=". Type: "+type+".";
  1733. if (iframe) input.title+=" (Within iframe).";
  1734. }
  1735. }
  1736. }
  1737. } //end checkForInputs()
  1738.  
  1739. function prettyPrintUneval(str, oneline) {
  1740. return str.substring(2, str.length-2);
  1741. }
  1742.  
  1743. function editFormData() {
  1744. readPersistentData();
  1745. if ( ! form_data && ! click) return;
  1746. var pwin=prompt3(widener+'\nThe following is the form filling information previously saved, '
  1747. + '\nedit the values in the box at the bottom '
  1748. +'(you may need to scroll down).'
  1749. +'\nthen Click OK, Click Cancel/Next to edit sites to\nwhich data applies or to simply cancel.\n\nKeep to the punctuation format. '
  1750. +'Do not edit the special\ncharacters, "'+bullet+'<tab>", leave them in place,\nunless '
  1751. +'you want to remove or add an entire name/value pair.'
  1752. +'\n\nNote. Each input box or tick box on a webpage has an internal name,\nits value is what gets filled-in by the user. '
  1753. +'Pass the mouse over a form\nto see the internal\nnames of the fields.'
  1754. +'They are given in the form name:: value & must have a\nspecial character between pairs. Checkbox names also have their default\nvalue appearing with their name after a dash ('+mdash_char+').\n\n'
  1755. +'To just fill just the values without names, clear the input and type in\nthe values leaving a space between them.\nValues with spaces must be surrounded by double quotes.\n\n'
  1756. +chopLongString(bullet_tab+formDataToFromString(true)), formDataToFromString(false, undefined, "", true)
  1757. , function(users_input) {
  1758. log("Got users_input "+users_input);
  1759. if (users_input != null) {
  1760. log("ip "+users_input +" match: "+users_input.match(separator) );
  1761. if ( users_input && ! users_input.match(separator) )
  1762. res=formDataToFromString(false, users_input, "name_value_paste");
  1763. else
  1764. res=formDataToFromString(false, users_input);
  1765. if (res) {
  1766. log("Store "+uneval(res)+"\n\nAt page_key: "+page_key);
  1767. page_object.form_data=res;
  1768. } else {
  1769. delete page_object.form_data;
  1770. }
  1771. persistData();
  1772. }
  1773. else { //is null (ie, a cancel):
  1774. pwin.dontclose=true;
  1775. log("Next, prompt3 for window2");
  1776. prompt3(widener+"\nThe following is the page(s) for which the form data and click information is valid."
  1777. +"\nEdit the page name to indicate page name(s) where stored form data may be applied and click OK.\nClick CANCEL to leave as is."
  1778. +"\n\nFor example, remove all but the site name to make it valid for the entire site or for a number of sites (can use regexp or a part of the name but no slashes)"
  1779. , page_key, function(users_input) {
  1780. log("From prompt3 got: "+users_input);
  1781. if (users_input != null && page_key != users_input) {
  1782. log(1);
  1783. hash_host_list[users_input]=page_object;
  1784. log(1);
  1785. page_object=null;
  1786. }
  1787. persistData();
  1788. } ); //result_handler end of function
  1789. log("pwin "+pwin);
  1790. }//else
  1791. } ); //result_handler end function
  1792. }
  1793.  
  1794. function editClickData(){
  1795. prompt3(widener+"\nThe following is the XPath address of the auto-click element on this page."
  1796. +"\nEdit it and click OK.\nClick CANCEL to leave as is.", (click?click.xpath:""),
  1797. function(users_input) {
  1798. log("Got "+users_input+", click: "+click);
  1799. if ( users_input != null ) {
  1800. if ( ! click) click={};
  1801. click.xpath=users_input;
  1802. click.xxpath=users_input;
  1803. persistData();
  1804. GM_log("Set click xpath to:"+click.xxpath+".");
  1805. }
  1806. else GM_log("No input");
  1807. });
  1808. }
  1809.  
  1810. function formDataToFromString(breaks, str_in, value_paste, isedit, tmp_data) {
  1811. var data= form_data||{};
  1812. if (tmp_data) data=tmp_data;
  1813. var j=0, v, n, form_str="", extra_break="";
  1814. tab=" "+bullet+" ";
  1815. if (breaks) tab ="\n"+bullet_tab
  1816. if ( str_in == undefined ) { //toString
  1817. for (n in data) {
  1818. j++;
  1819. v=data[n].v;
  1820. if ( ! breaks ) {
  1821. if ( ! v && v != 0)
  1822. v="null";
  1823. if (v==tick || v==ex) {
  1824. data[n].checkbox=true;
  1825. (v==tick ? v = "on": v= "off");
  1826. }
  1827. }
  1828. else { // breaks.
  1829. if (typeof v == "object") {
  1830. v = v + "\t\t[ "+getOptionsNames(n, v)+" ]"
  1831. data[n].select_multi=true;
  1832. }
  1833. if (input_names[n] && input_names[n].type=="select-one")
  1834. v = v + "\t\t[ " + getOptionsNames(n , v) + " ]"
  1835. if ( j % 3 == 0 ) extra_break="\n"
  1836. else extra_break="";
  1837. } // end ! breaks
  1838. if ( ! (data[n].pw) || unhide || isedit) {
  1839. form_str+=n+separator;
  1840. form_str+=(breaks ? "\t" : " ") +v+(unhide?"(unhidden)":"")+extra_break+tab;
  1841. }
  1842. else {
  1843. form_str+=n+separator
  1844. this.pw=v;
  1845. var i=0
  1846. form_str += (breaks ? "\t" : " ") + (v[0]?v[0]:"");
  1847. while (i++ < v.length-2)
  1848. form_str+="*";
  1849. form_str+=(v[i]?v[i]:"");
  1850. form_str+=tab;
  1851. }
  1852. } //end for n in data
  1853. return form_str.substring(0,form_str.length-tab.length);
  1854. }
  1855. //
  1856. //endif str_in==undefined, hence it is fromString:
  1857. if (value_paste) return doValuePaste(str_in, data);
  1858. name_value=str_in.split(bullet);
  1859. var val;
  1860. for(var i=0; i < name_value.length; i++) {
  1861. log("for loop, str_in, nm pair:"+name_value[i]);
  1862. if ( ! name_value[i])
  1863. continue;
  1864. words=name_value[i].split(separator);
  1865. if (words.length != 2)
  1866. continue;
  1867. var name=words[0].replace(/^\s+|\s+$/g,"");
  1868. val = words[1].replace(bullet_regexp, "").replace(/^\s+|\s+$/g,"");
  1869. if (val =="null")
  1870. val=null;
  1871. if ( ! data[name] ) data[name]=new Object();
  1872. //if (data[name].pw) continue;
  1873. data[name].v=val; ////
  1874. log("not pw, data[name] val: "+data[name].v);
  1875. data[name].user_set=true;
  1876. if (data[name].checkbox) {
  1877. val=val.replace(/\s*/g,"");
  1878. if (/^on$/i.test(val))
  1879. data[name].v=tick;
  1880. else
  1881. data[name].v=ex;
  1882. }
  1883. if (input_names[name] && input_names[name].type=="select-one")
  1884. if (isNaN(val) )
  1885. data[name].v = getOptionsIndex(input_names[name], val, data[name]);
  1886. else
  1887. data[name].v = val;
  1888. log("Set val: "+data[name].v+", type "+(typeof data[name].v));
  1889.  
  1890. if (data[name].select_multi) {
  1891. data[name].v=val.split(/\s*,\s*/);
  1892. }
  1893. }
  1894. for(var n in data) {
  1895. if ( ! data[n].user_set) {
  1896. delete data[n]
  1897. }
  1898. else {
  1899. delete data[n].user_set
  1900. delete data[n].checkbox;
  1901. delete data[n].select_multi;
  1902. }
  1903. }
  1904. if (countMembers(data) == 0)
  1905. return 0;
  1906. return data;
  1907. }
  1908.  
  1909. function doValuePaste(str_in, data) {
  1910. str_in=stuffQuotedSpaces(str_in);
  1911. log("Do paste str_in: "+str_in);
  1912. var values=str_in.split(/\s+/);
  1913. var val, name;
  1914. for(var i=0; i < values.length; i++) {
  1915. log("str in: "+values[i]);
  1916. if ( ! values[i] )
  1917. continue;
  1918. val = values[i].replace(bullet_regexp, " ").replace(/^\s+|\s+$/g,"").replace(/[""]/g,"");//2 doubles for indent
  1919. if (val =="null")
  1920. val=null;
  1921. name=nameAtI(data, i);
  1922. log2(i+"th, val "+val+" name:" +name+ " "+(input_names[name]?input_names[name].type:"no ip"));
  1923. if (name==false) continue;
  1924. if ( ! data[name] ) data[name]=new Object();
  1925. if (data[name].pw) continue;
  1926. data[name].v=val; ////
  1927. data[name].user_set=true;
  1928. if (input_names[name] && input_names[name].type=="select-one") {
  1929. if (isNaN(val))
  1930. data[name].v = getOptionsIndex(input_names[name], val, data[name]);
  1931. else
  1932. data[name].v = Number(val);
  1933. }
  1934. log2( "paste val: "+data[name].v+", typeof val "+(typeof data[name].v) );
  1935. if (data[name].checkbox) {
  1936. val=val.replace(/\s*/g,"");
  1937. if (/^on$/i.test(val))
  1938. data[name].v=tick;
  1939. else
  1940. data[name].v=ex;
  1941. }
  1942. if (data[name].select_multi)
  1943. data[name].v=val.split(/\s*,\s*/);
  1944. } //matches end of for loop over values
  1945. for(var n in data) {
  1946. if ( ! data[n].user_set) {
  1947. delete data[n]
  1948. }
  1949. else {
  1950. delete data[n].user_set
  1951. delete data[n].checkbox;
  1952. delete data[n].select_multi;
  1953. }
  1954. }
  1955. if (countMembers(data) == 0)
  1956. return 0;
  1957. return data;
  1958. } //actually matches end of doValuePaste()
  1959.  
  1960. function doNameValuePaste(str_in, data){
  1961. name_value=str_in.split("\n");
  1962. var val;
  1963. for(var i=0; i < name_value.length; i++) {
  1964. if ( ! name_value[i])
  1965. continue;
  1966. var words=name_value[i].split(":");
  1967. if (words.length != 2)
  1968. continue;
  1969. var name=words[0].trim();
  1970. val = words[1];
  1971. if (val =="null")
  1972. val=null;
  1973. name=matchInputName(name, inputs);
  1974. //log("Got name match of "+name+" data[name] :" +data[name]);
  1975. if ( ! name ) continue;
  1976. if ( ! data[name] ) data[name]=new Object();
  1977. if (data[name].pw) continue;
  1978. data[name].v=val; ////
  1979. data[name].user_set=true;
  1980. if (data[name].checkbox) {
  1981. val=val.replace(/\s*/g,"");
  1982. if (/^on$/i.test(val))
  1983. data[name].v=tick;
  1984. else
  1985. data[name].v=ex;
  1986. }
  1987.  
  1988. if (input_names[name].input_names[name].type=="select-one")
  1989. if (isNaN(val) )
  1990. data[name].v = getOptionsIndex(input_names[name], val, data[name]);
  1991. else
  1992. data[name].v = val;
  1993. log("Paste set val: "+data[name].v+", type "+(typeof data[name].v));
  1994.  
  1995.  
  1996. if (data[name].select_multi) {
  1997. data[name].v=val.split(/\s*,\s*/);
  1998. }
  1999. }
  2000. for(var n in data) {
  2001. if ( ! data[n].user_set) {
  2002. delete data[n]
  2003. }
  2004. else {
  2005. delete data[n].user_set
  2006. delete data[n].checkbox;
  2007. delete data[n].select_multi;
  2008. }
  2009. }
  2010. if (countMembers(data) == 0)
  2011. return 0;
  2012. return data;
  2013. }
  2014.  
  2015. function countMembers(obj) { var cnt=0; for(var i in obj) if ( ! obj.hasOwnProperty || obj.hasOwnProperty(i)) cnt++; return cnt; }
  2016.  
  2017. function matchInputName(name, data) {
  2018. var res, ratio=0, high_ratio=0;
  2019. for(var i in data) {
  2020. ratio=ratioOfMatchedWords(name, data[i].name);
  2021. //log("got ratio of "+ratio+" for "+name+" in data[i].name: "+data[i].name);
  2022. if ( ratio > .5) {
  2023. if (ratio > high_ratio) {
  2024. high_ratio=ratio;
  2025. res=data[i].name;
  2026. }
  2027. }
  2028. if (ratio == 1)
  2029. return data[i].name;
  2030. }
  2031. return res;
  2032. }
  2033.  
  2034. function nameAtI(obj, i) {
  2035. var index=i;
  2036. for(var j in obj) {
  2037. if ( i == 0 ) return j || "yyNoName"+index;
  2038. i--;
  2039. }
  2040. return false;
  2041. }
  2042.  
  2043. function stuffQuotedSpaces(str) {
  2044. var phrase_pos=0;
  2045. var i=str.indexOf('"', phrase_pos);
  2046. while (i !== -1) {
  2047. var j=str.indexOf('"', i+1);
  2048. var phrase=str.substring(i, j);
  2049. str=stringCutAndPaste(str, phrase, phrase.replace(/\s/g, bullet), i);
  2050. i=str.indexOf('"', j+1);
  2051. }
  2052. return str;
  2053. }
  2054.  
  2055. function stringCutAndPaste(str, tocutout, topaste, start) {
  2056. var n=str.indexOf(tocutout, start||0);
  2057. if (n != -1)
  2058. return str.substring(0,n) + ( topaste || "") + str.substring(n+tocutout.length);
  2059. return str;
  2060. }
  2061.  
  2062. function getOptionsNames(name, value_s) {
  2063. var result="";
  2064. var option_names=[], options;
  2065. var ip=input_names[name];
  2066. if (ip) options=ip.options;
  2067. if (typeof value_s == "object") {
  2068. if (ip && ip.type == "select-multiple" )
  2069. for(var i in value_s)
  2070. option_names[i]=getOptionsNames(name, value_s[i]);
  2071. result =String(option_names).replace(/,\b/g,", ");
  2072. }
  2073. else {
  2074. for (var i =0; i < options.length; i++) {
  2075. //log("opt.inx: "+options.item(i).index +", look for: "+value_s +". Opt.value: "+options.item(i).value );
  2076. var option=options.item(i);
  2077. if (option.index== value_s ) {
  2078. if (option.textContent)
  2079. result = option.textContent;
  2080. else
  2081. result = option.value;
  2082. }
  2083. }// end for.
  2084. }
  2085. return result.replace(/[\r\n]/g, "").replace(/^\s+|\s+$/g,"");
  2086. }
  2087.  
  2088. function getOptionsIndex(ip , in_val, name) {
  2089. var index = 0, best_match = 0;
  2090. for (var i =0; i < ip.options.length; i++) {
  2091. var option=ip.options.item(i);
  2092. var opts_val=option.value;
  2093. if ( option.textContent && option.textContent.trim() )
  2094. opts_val=option.textContent;
  2095. opts_val=opts_val.toLowerCase().trim();
  2096. in_val=in_val.toLowerCase().trim();
  2097. var match_ratio=ratioOfMatchedWords(in_val, opts_val);
  2098. if (match_ratio > best_match ) {
  2099. best_match=match_ratio;
  2100. name.str=opts_val;
  2101. //index=String(option.index);
  2102. index=option.index;
  2103. if (match_ratio > 0.9)
  2104. // return String(option.index);
  2105. return option.index;
  2106. }
  2107. }
  2108. return index;
  2109. }
  2110.  
  2111. function ratioOfMatchedWords(source_words, target_words) {
  2112. source_words=source_words.replace(/[^a-zA-Z0-9_ \t]+/g," ").split(/\s+/);
  2113. var matches=0, ratio=0, targ_ratio=0;
  2114. for(var i in source_words)
  2115. if (target_words.match( RegExp(source_words[i],"i") ))
  2116. matches++;
  2117. ratio = matches/source_words.length;
  2118. targ_ratio = matches/target_words.replace(/[^a-zA-Z0-9_ \t]+/g," ").split(/\s+/).length;
  2119. ratio = (ratio + targ_ratio) / 2;
  2120. //log("opt match "+source_words+", in targ "+target_words+". # Matches "+matches+", in "+source_words.length+" , targ ratio:, "+targ_ratio+" ratio "+ratio)
  2121. return ratio;
  2122. }
  2123.  
  2124. function parseTextarea(ta) {
  2125. var result="";
  2126. var lines=ta.value.trim();
  2127. //lines=lines.replace(/[\r\n]\s*[\n\r]/g, "\n"); //removes empty lines
  2128. log("lines:\n "+uneval(lines));
  2129. lines=lines.split(/\n/); // check MSDOz!!
  2130. lines=reorderLines(lines);
  2131. for(var i=0; i < lines.length; i++) {
  2132. var val, res;
  2133. var line=lines[i].trim();
  2134. if (line && ! /:/.test(line))
  2135. line=": "+line;
  2136. if (/::/.test(line) || multiMarked(i) ) {
  2137. val=line.splitOnce(/\s*:+\s*/)[1];
  2138. if (val) result += " " + parseOptionalValues(val, i);
  2139. }
  2140. else {
  2141. val=line.splitOnce(/:\s*/)[1];
  2142. if (res=parseDateTime(val, i)) result += " " + res;
  2143. // else if (val) result += ' "'+val.replace(/[\x27\x22]/g,"")+'"';
  2144. else if (val) result += ' "'+val.replace(/[\x22]/g,"").replace(/[\x27]/g,"\\\x27")+'"';
  2145. else result += " null ";
  2146. }
  2147. }
  2148. return result.trim();
  2149. }
  2150.  
  2151. // GM_setValue("line_order", "3*, 1, 2DsT, 4");
  2152. // GM_setValue("optionals", "line1word2of3");
  2153.  
  2154. function parseDateTime(val, i) {
  2155. var datetime=GM_getValue('line_order', ""), date="", time=""; //eg, "3*, 1, 2DsT, 4"
  2156. if ( ! /[DT]/i.test(datetime))
  2157. return null;
  2158. datetime=datetime.split(/,/)[i];
  2159. if ( ! /[DT]/i.test(datetime))
  2160. return null;
  2161. if (/Ds/i.test(datetime))
  2162. date=new Date(val).slashFormat();
  2163. if (/T/i.test(datetime)) {
  2164. time=new Date(val).ampmFormat();
  2165. time += " " + getTimeZone();
  2166. }
  2167. result = date+" "+time;
  2168. if (/TD/.test(datetime)) result = time+" "+date;
  2169. //log("ret date/time "+result);
  2170. return result;
  2171. }
  2172.  
  2173. function parseOptionalValues(val, i) {
  2174. var opts=GM_getValue("optionals", ""), opt_word, max_words;
  2175. var pos=opts.indexOf( "line" + (i+1) ); //line number not zero counted.
  2176. if (pos != -1) {
  2177. pos= pos + ("line"+i).length + 4;//eg, "line1word2of3"
  2178. opt_word=parseInt ( opts.substr (pos) );
  2179. pos += (opt_word+"").length + 2;
  2180. max_words=parseInt ( opts.substr(pos) );
  2181. //log(pos+ "<pos. opt_word "+opt_word+", max "+max_words);
  2182. var words=val.split(/\s+/);
  2183. if (words.length < max_words) {
  2184. words.splice(opt_word-1, 0, "null"); //insert 'null' if optional is not given.
  2185. }
  2186. val=words.join(" ");
  2187. }
  2188. return val;
  2189. }
  2190.  
  2191. function multiMarked(i) {
  2192. var marked=GM_getValue('line_order', "1, 2, 3, 4, 5");
  2193. marked=marked.split(/,/);
  2194. return /\*/.test(marked[i]);
  2195. }
  2196.  
  2197. //greasemonkey.scriptvals.userscripts.org/Simple Form Saver.line_order
  2198. function reorderLines(lines){
  2199. order=GM_getValue('line_order', "1, 2, 3, 4, 5").replace(/[^\d,]/g,"");
  2200. order=eval("["+order+"]");
  2201. var newlines=[];
  2202. for( var i in lines)
  2203. if (i < order.length && order[i] != 0) {
  2204. var old_pos=order[i]-1;
  2205. if (old_pos < lines.length)
  2206. newlines[i]=lines[old_pos];
  2207. else
  2208. newlines[i]="null";
  2209. }
  2210. else
  2211. newlines[i]=lines[i];
  2212. return newlines;
  2213. }
  2214.  
  2215. function pasteIntoForm() {
  2216. // 1. Open a window containing help info with a list of form field names and into which user can paste form values, then
  2217. // 2. Label all form elements in page with a visible sequence number, then
  2218. // 3. Set up a click handler for user's 'OK', which
  2219. // 4. Calls parseTextarea() and doValuePaste() to set up values given by user, then
  2220. // 5. Ask user to confirm the values to be set, finally call fillForm() with ordered array tmp_data.
  2221.  
  2222. log2("pasteIntoForm() "+typeof pasteWindow);
  2223. //readPersistentData();
  2224. if (special_ta) log("special_ta tc "+special_ta.value+" id "+special_ta.id);
  2225. if (getById("SimpleFormSave")) return;
  2226. var ta_val="";
  2227. //var pasteWindow=uwin.open("ex_form.html#bizarreoKeyer","_blank", "foreground");
  2228. try { if (pasteWindow && !pasteWindow.closed) { // in order to focus on pasteWindow
  2229. var texta = pasteWindow.div.getElementsByTagName("textarea")[0];
  2230. ta_val=texta.value;
  2231. log2("ta_val "+ta_val);
  2232. pasteWindow.close();
  2233. }} catch(e) { pasteWindow=null; } //catch occurs if window was closed, cant acess dead object.
  2234.  
  2235. if (Chrome) pasteWindow=window;
  2236. else pasteWindow=unsafeWindow.open("","pasteIntoForm","menubar=no,scrollbars,location=no,toolbar=no, status=no,left="+screen.width+"px,top="+(screen.height/2-300)+"px,width=600em");
  2237. log("pasteWindow "+pasteWindow);
  2238. //pasteWindow.document.addEventListener("keydown", function(e) { if (e.keyCode == 27) pasteWindow.sfspasteCANCEL=true;}, 0);
  2239. var div=pasteWindow.document.createElement("div"); div.id="SimpleFormSave";
  2240. div.setAttribute("onkeydown","if (event.keyCode == 27) window.sfspasteCANCEL=true;");
  2241. pasteWindow.div=div;
  2242. var body=pasteWindow.document.body;
  2243. function listFields() { var skip, roll="field(s) named, ";
  2244. for (var i=0; i < inputs.length; i++) {
  2245. var ip=inputs[i];
  2246. if(skip==ip.name) continue;
  2247. if (ip.type=="radio") skip=ip.name;
  2248. var style=window.document.defaultView.getComputedStyle(ip, null)||{};
  2249. var visibility=( (style.display=="none" || style.visibility=="hidden") ? " (invisible)" : "");
  2250. ip.visibility=visibility;
  2251. roll+= ip.name+visibility+", ";
  2252. }
  2253. return roll; }
  2254. var tatitle="For an example form with three fields: name, last-name, sex. Paste-in as follows in three lines, with the first line, John, second line, Webster, third line, male. "
  2255. +"To do it in just one line use two colons, type or paste, ::John Webster male. If there was also a field for the middle name to set it as null put: ::John null Webster male."
  2256. +"If one of the values after the double colon (::) had spaces in it, it would need to be surrounded by double quotes, eg, ::John "+'"Boyle OConnor Fitzmaurice Tisdall OFarell"'+" male. "
  2257. +"If you know the names of the inputs or can guess or check them by looking at the pages HTML then you could use, "+" name: Peter "+", on any line and it would fill it in the right field"
  2258. div.innerHTML="<p>Enter or Paste form field values into the box directly below this text; do it for each field of the form. Click OK when complete, or leave blank and click OK to see current values. You can put a colon "
  2259. +"before the value if desired. You may need to maintain the same order as the page's form so fields are now numbered with '#' on the webpage, watch out for invisible (red) fields. "
  2260. +"<br><br>Use a new line for each field, or use double colons (::) to put more than one field's values on a single line. "
  2261. +"Put the mouse over this and below for instructions and examples. You can put the mouse over the fields of the form to see details of form.<br><br>This page has "+listFields()+"</p>"
  2262. +"<textarea style='width:100%;height:20em; border: double;background-color:#fffff4;color:midnightblue;' title='"+tatitle+"';>"
  2263. +(special_ta?special_ta.value:ta_val)+"</textarea>";
  2264. var ta=div.getElementsByTagName("textarea")[0];
  2265. div.innerHTML+="<form>"
  2266. +"<input type=button value='Cancel/Next' style='width:100; height:50;font-size: 12' "
  2267. +"onclick='window.sfspasteCANCEL=true' " //workaround for window perms problem.
  2268. +">"
  2269. +"<input type=button value='OK' style='width:100; height:50;font-size: 24;' "
  2270. +"onclick='window.sfspasteOK=true' "
  2271. +"></form>";
  2272. div.firstElementChild.title='Give one value on each line eg, "Pepsi" or put them in the form name \u2039colon\u203a value, eg, "Company: Pepsi" or indeed just \u2039colon\u203a value, eg, ":Pepsi". For fields not to be filled-in at all just put a single colon on that line. '
  2273. +"If names are given the name need not match the real name; each value is copied into the approriate field based not on the name but on the field ordering"
  2274. +" unless the config value for pasting names is set in "
  2275. +"about:config. The first line is copied to the first form field, etc.. One field value per line or,"
  2276. +" to allow more values on one line, use double colons for contiguous fields, eg, 'time:: 12:23 Eastern' will fill-in two fields, the time and the"
  2277. +" time zone. To skip a field use the value 'null' or leave the part after the colon empty. Hover over below to see help text.";
  2278. //ta.value="\n ";
  2279. div.innerHTML+=""
  2280. +"<input title='When above text box is empty click Prev./Save to paste values that were previously saved here in this dialog. Or, when not empty, "
  2281. +"click it to save the values currently in above text box of this dialog. To get current values, click OK when empty, to see current values that are set in the form and choice values/names middle click on red dot.' type=button value='Prev./Save' style='width:100; height:50;font-size: 12' "
  2282. +"onmouseup='window.sfspastePrev=event.which' "
  2283. +">";
  2284. log2("prevPasteVals:"+page_object.prevPasteVals+".");
  2285. if (page_object.prevPasteVals)
  2286. div.innerHTML+="<div id=prevsave>Previously saved at this dialog:<br><pre>"+page_object.prevPasteVals+"</pre></div>"
  2287. else
  2288. div.innerHTML+="<div id=prevsave></div>"
  2289. ta.value=ta_val; //set from innerHTML not here!
  2290. log2("set ta_val "+ta.value);
  2291. var title=div.firstElementChild;
  2292. body.appendChild(div);
  2293. if (Chrome) {
  2294. div.style.cssText=" z-index: 99; top: 40px; height: 50%; opacity: 0.85; background-color: white; font-family: Helvetica; font-weight: bold; font-size: small; padding: 10px";
  2295. ta.style.cssText="width: 50%; margin: 0; border: none;"
  2296. +" -moz-appearance: none; border-left: double; min-height: 300px; ";
  2297. title.style.cssText= "background-color: -moz-field; margin:0; border: double;";
  2298. log("set cssTExt "+ta.style.cssText);
  2299. }
  2300. else {
  2301. div.style.cssText=" top: 40px; height: 50%; font-family: Helvetica; font-weight: bold; font-size: small; padding: 10px";
  2302. // ta.style.cssText="width: 50%; height: 150px; margin: 0; border: none;"
  2303. // +" -moz-appearance: none; border-left: double;"
  2304. title.style.cssText= "background-color: -moz-field; color: -moz-FieldText; margin:0; border: double;";
  2305. }
  2306. //title.scrollIntoView();
  2307. //pasteWindow.resizeTo(body.offsetWidth+50,body.offsetHeight+100);
  2308. var labels=[], radio_names={}, detract=0;
  2309. for(i=0; i < inputs.length; i++) {
  2310. var input=inputs[i];
  2311. var l=document.createElement("label");
  2312. var prev=input.previousSibling;
  2313. if ( prev && prev.tagName=="LABEL" && /#\d$/.test (prev.textContent) ) continue;
  2314. input.parentNode.insertBefore(l, input);
  2315. if (input.visibility) { l.title="Invisible input, provide a value or leave line blank, name="+input.name; l.style.color="red";l.style.backgroundColor="black";}
  2316. if (input.type=="radio") {
  2317. if (radio_names[input.name]==undefined)
  2318. radio_names[input.name]=(i+1-detract);
  2319. else detract++;
  2320. l.textContent=" #"+(radio_names[input.name]);
  2321. }
  2322. else l.textContent=" #"+(i+1-detract);
  2323. labels.push(l);
  2324. }
  2325. var processTextarea=function() {
  2326. var ta = div.getElementsByTagName("textarea")[0];
  2327. log("ta css "+ta.style.cssText+" tag "+ta.tagName);
  2328. log("processing val:"+ta.value);
  2329. log("processing ");
  2330. var str_in, parse_method=GM_getValue("parse_method", "values"); //can be set to "names", default is "values" which uses the order to know which value to paste.
  2331. if (parse_method[0] != "n") parse_method=true; else parse_method=false;
  2332. if (ta.value=="") { fillWithCurrentValues(ta); return; }
  2333. if (parse_method)
  2334. str_in=parseTextarea(ta);
  2335. else str_in=ta.value.trim();
  2336. log("parse_method "+parse_method+", after parse, str_in: "+str_in);
  2337. var data={};
  2338. for(i=0; i < inputs.length; i++) {
  2339. var input=inputs[i], name=input.name;
  2340. data[name]={};
  2341. data[name].i=i;
  2342. if (input.type=="checkbox") {
  2343. data[name].checkbox=true;
  2344. var cbname=name+mdash_char+input.value;
  2345. data[cbname]=data[name];
  2346. delete data[name];
  2347. }
  2348. }
  2349. var res;
  2350. if (parse_method)
  2351. res=doValuePaste(str_in, data), list=widener, j=0;
  2352. else
  2353. res=doNameValuePaste(str_in, data), list=widener, j=0;
  2354. log(" res "+res+" data.len: "+data.length);
  2355. for (var i in data) {
  2356. if (typeof data[i] === 'function') continue;
  2357. //log2("for i in data. i:"+i+"="+data[i]+", type: "+typeof data[i]);
  2358. if (parse_method)
  2359. list += "" //"field #" + ( ( j++ ) + 1)
  2360. +"To be filled-in: "+data[i].v+" \t\t ("+i+")\n";
  2361. // +"To be filled-in: "+(data[i].str ? data[i].str : (data[i].v||"") )+" \t\t ("+(inputs[j-1].name||"")+")\n";
  2362. else
  2363. list += "input name: "+i
  2364. +", to be filled-in with: "+(data[i].str ? data[i].str : (data[i].v||"") )+"\n";
  2365. }
  2366. list=list.replace(/\\\x27/g,"\x27");
  2367. // var go_ahead=
  2368. var w=pasteWindow.outerWidth, h=pasteWindow.outerHeight;
  2369. //pasteWindow.resizeTo(1,1);
  2370. //window.focus();
  2371. //log2("w h "+pasteWindow.outerWidth+" "+pasteWindow.outerHeight);
  2372. confirm3(list+"\n\nConfirm or Cancel", function(go_ahead) {
  2373. if (go_ahead) {
  2374. if (res) fillForm(false, false, res);
  2375. inOutSet();
  2376. }
  2377. if (Chrome) {
  2378. div.style.display="none";
  2379. div.parentNode.removeChild(div);
  2380. delete div;
  2381. }
  2382. for (var i in labels) {
  2383. var parent=labels[i].parentNode;
  2384. if (parent) parent.removeChild(labels[i]);
  2385. }
  2386. //pasteWindow.resizeTo(w, h);
  2387. //pasteWindow.focus();
  2388. }); //confirm3()
  2389. }; // end processTextarea() ///////////////////////////////
  2390. var form_inputs=div.getElementsByTagName("input");
  2391. form_inputs[0].do_onclick=function() { // was "onclick" workaround window perms problem.
  2392. pasteWindow.close(); log("cancel");
  2393. for (var i in labels)
  2394. if (labels[i].parentNode) labels[i].parentNode.removeChild(labels[i]);
  2395. if (Chrome) {
  2396. div.style.display="none";
  2397. div.parentNode.removeChild(div);
  2398. delete div;
  2399. }
  2400. };
  2401. form_inputs[0].style.cssFloat="left";
  2402. form_inputs[1].do_onclick=function (){
  2403. processTextarea(); ///////////////////////
  2404. //log2("processTextarea "+ta.value+".");
  2405. };
  2406. //ta.focus();
  2407. if (form_inputs[2])
  2408. form_inputs[2].do_onclick=function (which_button) {
  2409. log2("Which_button "+which_button)
  2410. var ta = div.getElementsByTagName("textarea")[0];
  2411. var taval=ta.value.trim();
  2412. if (taval=="") { ta.value=page_object.prevPasteVals; }
  2413. else {
  2414. readPersistentData();
  2415. page_object.prevPasteVals=taval;
  2416. var paradiv=$(div).find("#prevsave");
  2417. paradiv.html("Previous saved at this dialog:<br><pre>"+page_object.prevPasteVals+"</pre>");
  2418. persistData();
  2419. //alert("Saved "+page_object.prevPasteVals)
  2420. }
  2421. };
  2422. log2("was set ta_val "+ta.value);
  2423. pasteWindow.ta=ta;
  2424. pasteWindow.setTimeout(function() { ta.focus(); }, 3000);
  2425.  
  2426. var siid=setInterval(function() { //Added in cos of new window's lack of perms to run code in this file.
  2427. try { if (pasteWindow.closed) clearInterval(siid); var tmp=pasteWindow.sfspasteOK; } catch(e) { clearInterval(siid);}
  2428. if (pasteWindow.sfspasteOK) {
  2429. pasteWindow.sfspasteOK=false;
  2430. form_inputs[1].do_onclick();
  2431. }
  2432. if (pasteWindow.sfspasteCANCEL) {
  2433. clearInterval(siid);
  2434. pasteWindow.sfspasteCANCEL=false;
  2435. form_inputs[0].do_onclick();
  2436. pasteWindow.close(); //cancel
  2437. }
  2438. if (pasteWindow.sfspastePrev) {
  2439. // tbd, paste prev values or save existing ones.
  2440. form_inputs[2].do_onclick(pasteWindow.sfspastePrev); //see page_object.prevPasteVals
  2441. pasteWindow.sfspastePrev=false;
  2442. }
  2443. }, 200);
  2444. } // end pasteIntoForm()
  2445.  
  2446.  
  2447. function fillWithCurrentValues(ta) { //clicking ok when pasting values and empty fills with current values.
  2448. var tmp_data=saveFormData(true);
  2449. var str_in=formDataToFromString(false, undefined, false, false, tmp_data);
  2450. var vals=str_in.split(separator); //separator is "::" w/ ctrl char betwixt.
  2451. var roll="";
  2452. for (var i=1; i< vals.length;i++) {
  2453. roll+=vals[i].split(bullet)[0].trim()+"\n"; //bullet is ⚫
  2454. }
  2455. ta.value=roll; //+"\n\n";
  2456. }
  2457.  
  2458. function confirmClickRecording() {
  2459. confirm3("The next part of the webpage on which you click shall be recorded and\ncan be clicked again on subsequent visits to this page, automatically or manually."
  2460. +"\n\nTo ensure you click on the correct element all elements can be\nhighlighted with a red outline & show XPath (it also appears in the console,\nsee status bar (if allowed by about:config, see userscript webpage) for element tag."
  2461. +"\n\nClick OK to proceed\n\nHit Cancel/Next to hightlight elements.\nNote, highlighted elements may move position, hit 'Esc' key to clear highlighting."
  2462. +"\n\nThen click on chosen element to have a click replayed on this page when visited in future.\nShift-click searches for nearest input, form, link, or onclick element. "
  2463. //+"\n\nInstead, to have an element permanently deleted from the page, do, control-shift-click, on the element.\nRecording a click will not involve a real click on the page."
  2464. , function(reply) { //result handler function
  2465. recording=reply;
  2466. if ( recording == false) {
  2467. GM_addStyle("* { border-color: red ! important ; border-width: 1px ! important ; border-style: solid ! important ; } ");
  2468. window.document.body.addEventListener("mouseover", mouseOver, false);
  2469. addEventListener("keyup", function(e) {
  2470. if (e.keyCode!=27) return; //esc
  2471. var headNode=window.document.getElementsByTagName("head")[0];
  2472. headNode.removeChild(headNode.lastElementChild);
  2473. window.document.body.removeEventListener("mouseover", mouseOver, false);
  2474. window.document.body.removeEventListener("keyup", arguments.callee, false);
  2475. }, 0);
  2476. }
  2477. //window.document.body.addEventListener("click", recordClick, false);
  2478. window.document.body.addEventListener("click", recordClick, true); //capture mode
  2479. window.status="Shall save next click on this page: "+page_key;
  2480. await_click=true;
  2481. })
  2482. }
  2483.  
  2484. function recordClick(e) {
  2485. log("recordClick "+e.target.className+" "+e.target.tagName+" "+recording);
  2486. if ( $(e.target).hasClass("sfsdiax") ) return true;
  2487. if ( recording === undefined) return true;
  2488. // if ( ! FireFox && ! window.counted_confirm_click) {
  2489. // window.counted_confirm_click=true;
  2490. // return true;
  2491. // }
  2492. await_click=false;
  2493. if (e.preventDefault) {
  2494. e.preventDefault();
  2495. e.stopPropagation();
  2496. e.stopImmediatePropagation();
  2497. }
  2498. if ( recording == false) {
  2499. var headNode=window.document.getElementsByTagName("head")[0];
  2500. headNode.removeChild(headNode.lastElementChild);
  2501. window.document.body.removeEventListener("mouseover", mouseOver, false);
  2502. }
  2503. this.removeEventListener("click", arguments.callee, true);
  2504. //img.inOutSet(); might be on different page, so cant access this img.style w/o security error
  2505. //readPersistentData();
  2506. var click={};
  2507. var elem=e.target;
  2508. var cond=false, onclick;
  2509. var orig_onclick=elem.getAttribute("onclick") // or onmousedown, up
  2510. if (e.ctrlKey && e.shiftKey)
  2511. click.ctrlShift=true;
  2512. else {
  2513. if (e.shiftKey) {
  2514. while ( elem && elem.tagName ) {
  2515. onclick=elem.getAttribute("onclick");
  2516. cond= /^(input|form|a)$/.test ( elem.tagName.toLowerCase() );
  2517. cond = cond || onclick;
  2518. if (cond) break;
  2519. elem=elem.parentNode;
  2520. }
  2521. if ( ! elem || ! elem.tagName)
  2522. elem=e.target;
  2523. }
  2524. click.onclick_js=elem.getAttribute("onclick")
  2525. click.href=elem.href;
  2526. }
  2527. log("rem click listnr");
  2528. log("store click info");
  2529. click.own_href=href;
  2530. click.xpath=getXPath(elem, true);
  2531. click.id=elem.id;
  2532. click.className=elem.className;
  2533. click.xxpath=getXPath(elem);
  2534. log("Click was on element, innerHTML: "+elem.innerHTML);
  2535. log(" onc "+orig_onclick+" "+elem.onclick);
  2536. elem.id=null;
  2537. //elem.parentNode.removeChild(elem);
  2538. page_object.click=click;
  2539. var click_msg="Mouse click to save was on element: "+elem.tagName+", text, "+elem.textContent+", class, "+elem.className+" path: "+click.xpath;
  2540. GM_log(click_msg+" for page "+page_key);
  2541. var subpage=href.substring ( href.lastIndexOf ( page_key ) + page_key.length)
  2542. log("confirm again on subpage: "+subpage);
  2543. if (click.ctrlShift) // asych calls, events can intervene (eg, unload and clobber data)
  2544. confirm3( "Page name: "+page_key+"\n" +"Element xpath: " + click.xpath+(click.xpath!=click.xxpath ? " ("+click.xxpath+")":"")+widener
  2545. +"\n\nThe element on which the mouse click was just made shall be automatically deleted from this page from now on, "
  2546. +"unless automatic form-fill & replay are explicitly disabled for the page."
  2547. +"\n\n"
  2548. + "Click 'OK' to auto-delete this element in future on this specific page name, even when the page name is suffixed."
  2549. +"\n\nClick 'Cancel ' to abort or to specify on which pages the element is to be deleted"
  2550. +(subpage ? ", ie, on entire site or only on pages ending with the suffix:\n\t\t" + subpage : "" ) + "."
  2551. +"\n\n"
  2552. +(suspend_replay ? "\n\nNOTE: auto/replay-delete is currently suspended, to enable it go to the toggle in the GM menu." : ""),
  2553. reply_handler
  2554. );
  2555. else
  2556. confirm3( "Page name: "+page_key+"\n" + "Element xpaths: " + click.xpath+(click.xpath!=click.xxpath ? "\n\n("+click.xxpath+")":"")+"\n"+elem.textContent.replace(/\n/g,"")+widener
  2557. +"\n\nThe mouse click just made shall be replayed automatically on this page from now on "
  2558. +"unless automatic form-fill & replay are explicitly disabled for the page or in general. "
  2559. +"\nAny form data for the page will also be filled in prior to replaying the click unless auto-fill has been explicitly disabled for this page."
  2560. +"\n\nTo access this page in future it may be necessary to first select GM icon 'User Script Commands' menu option 'Suspend the Auto-Replay' or explicitly disable auto-replay for the page so click will only replay when the form is manually filled in via the red icon at top left or the GM icon"
  2561. +"\n\n"
  2562. + "Click 'OK' to auto-replay this click in future on this specific page name, even when the page name is suffixed."
  2563. +"\n\nClick 'Cancel/Next ' to specify on which page(s) to replay it, "
  2564. +(subpage ? ", ie, on entire site or only on this page ending with the suffix:\n\t\t" + subpage : "" ) + ", or to abort."
  2565. +""
  2566. +(suspend_replay ? "\n\nNOTE: auto-replay is currently suspended, to enable it go to the toggle in the GM menu." : ""),
  2567. reply_handler
  2568. );
  2569. function reply_handler(reply) {
  2570. log("reply_handler for record "+reply);
  2571. if (reply) {
  2572. page_object.click=click;
  2573. if(page_object.automatic!=false) page_object.automatic="on";
  2574. click.own_href="any";
  2575. recording=undefined;
  2576. hash_host_list.msg=click_msg
  2577. persistData();
  2578. window.status=": "+hash_host_list.msg;
  2579. }
  2580. else { //cancel
  2581. prompt3("Will replay click (or element deletion) on the following pages. "
  2582. +"\nThe site is, "+site+", edit the details as required or delete all but "
  2583. +"the site name for it to operate on entire site."
  2584. +"\nForward slashes[/] after the site name specifies"
  2585. +"\nthat it only applies to that page not whole site. "
  2586. +"\n\nClick 'Cancel' to abort entire recording.", decodeURIComponent(page_key+subpage),
  2587. function(subpage_reply) {
  2588. if (subpage_reply) {
  2589. log("page_key "+page_key+", == reply?: "+subpage_reply);
  2590. if (page_key != subpage_reply) { //move all to new page object, ie, & del this one.
  2591. click.own_href="any";
  2592. var po=hash_host_list[subpage_reply];
  2593. GM_log("New page object for, "+subpage_reply+", previous, "+page_key);
  2594. if ( ! po) {
  2595. po=new Object();
  2596. function copy(destination, source) {
  2597. for (var property in source) {
  2598. if (typeof source[property] === "object" &&
  2599. source[property] !== null ) {
  2600. destination[property] = destination[property] || {};
  2601. arguments.callee(destination[property], source[property]);
  2602. } else {
  2603. destination[property] = source[property];
  2604. }
  2605. }
  2606. return destination;
  2607. };
  2608. copy(po, page_object);
  2609. log("no page for it, copied page_object "+page_object+" to po");
  2610. }// end if !po
  2611. hash_host_list[subpage_reply]=po;
  2612. po.click=click;
  2613. if(po.automatic!=false) po.automatic=true;
  2614. //page_object={};
  2615. log("cleared page obj and set hash_host_list of given to "+hash_host_list[subpage_reply]);
  2616. GM_log("Operates on different pages: "+subpage_reply);
  2617. } //end if != subpage_reply
  2618. else {
  2619. page_object.click=click;
  2620. if(page_object.automatic!=false) page_object.automatic=true;
  2621. click.own_href="any";
  2622. log(" == to page_key, own_href: "+click.own_href)
  2623. }
  2624. }
  2625. else { window.status="Click recording aborted"; GM_log("Recoding aborted"); return;}
  2626. persistData();
  2627. recording=undefined;
  2628. });
  2629. }// end else //cancel
  2630. } // end reply handler
  2631. } // end recordClick()
  2632.  
  2633. function mouseOver(e) {
  2634. var msg="Mouse is over: "+e.target.nodeName+". Xpath: "+getXPath(e.target);
  2635. window.status=msg;
  2636. GM_log( msg+"\n"+e.target.textContent.substr(0,40).replace(/\n+/g," ") );
  2637. }
  2638.  
  2639. function handlePopups(e) { try { //node was inserted
  2640. if (!e.target.tagName) return;
  2641. if (recording !== undefined) return;
  2642. setTimeout(function() {
  2643. //log("handlePopups elem: "+e.target.tagName+", tc: "+e.target.textContent.substr(0,10).replace(/\s+/g,"")+", class: "+e.target.className);
  2644. readPersistentData("popups"); }, 0);
  2645. var target=e.target;
  2646. if (page_object.automatic) {
  2647. var opt_flag;
  2648. var new_elems=getElementsByTagNames("input,textarea,select"
  2649. +(awaiting_an_option ? ",option" : "") , target);
  2650. var news=new_elems.slice(0);
  2651. while ( target=new_elems.pop() ) {
  2652. if ( old_elems.some(function(e) {return target==e; }) ) continue;
  2653. if (target.tagName.toLowerCase()=="option") { opt_flag=target; target=target.parentNode;}
  2654. if (/^(input|textarea|select)$/.test(target.tagName.toLowerCase()))
  2655. if (form_data && form_data[target.name] ) {
  2656. var once_off=[];
  2657. once_off[0]=target;
  2658. if (target.options) {
  2659. var index=form_data[target.name].v;
  2660. if (target.options.length <= index) { awaiting_an_option = target; continue }
  2661. if (opt_flag && opt_flag.index!=index) continue;
  2662. if (awaiting_an_option == target) awaiting_an_option=false;
  2663. }
  2664. setTimeout(function() { fillForm(once_off) }, 0);
  2665. target.title += ( target.name ? "Name: " + target.name : (target.id ? "Id: " + target.id : "" ) ) + ". Value: " + target.value + ". Type: " + target.type
  2666. }
  2667. } // end while
  2668. old_elems=news.length? news : old_elems;
  2669. }
  2670. if (click && ! awaiting_an_option && no_element_for_click && ! suspend_replay) {
  2671. if (getXPathElem()) {
  2672. ws(function() { if (no_element_for_click) { if (fillForm(false, true)) no_element_for_click=false; } }, 0);
  2673. }
  2674. }
  2675. }catch(e){GM_log(e); throw(e);}
  2676. }
  2677.  
  2678. function log(str) {
  2679. //GM_log(str); return;
  2680. if ( typeof dcount == "undefined" ) { dcount=2; }
  2681. var d=new Date(); d=d.getMinutes()+"m:"+d.getSeconds()+"s:"+d.getMilliseconds()+"ms ";
  2682. if ( ! log.win) {
  2683. log.win=unsafeWindow.open(""); // need to allow site in noscript & be online for this.
  2684. log.doc=log.win.document;
  2685. str=d+" i-0: Doc: "+window.document.location.href+".<br>"+d+" i-1: "+str;
  2686. }
  2687. var style="style='margin-left : 100px; border-bottom: solid 1px; font-size: 14pt;line-height: 2em ' ondblclick='document.body.innerHTML=null'";
  2688. try{ log.doc.writeln("<div "+style+">"+d+"i-"+dcount+": "+str+" "+(iframe?"(iframe msg)":"")+"</div>"); log.doc.title=dcount; dcount++; }
  2689. catch(e){ window.setTimeout(function() {log(str)}, 0); }
  2690. }
  2691.  
  2692. function getXY(obj) {
  2693. var curleft = 0; var curtop = obj.offsetHeight + 5; var border;
  2694. function getStyle(obj, prop) { return document.defaultView.getComputedStyle(obj,null).getPropertyValue(prop); }
  2695. if (obj.offsetParent) {
  2696. do {
  2697. // If the element is position: relative we have to add borderWidth
  2698. if ( /^rel/.test ( getStyle (obj, 'position') ) ) {
  2699. if (border = getStyle(obj, 'border-top-width')) curtop += parseInt(border);
  2700. if (border = getStyle(obj, 'border-left-width')) curleft += parseInt(border);
  2701. }
  2702. curleft += obj.offsetLeft;
  2703. curtop += obj.offsetTop;
  2704. }
  2705. while (obj = obj.offsetParent)
  2706. }
  2707. else if (obj.x) {
  2708. curleft += obj.x;
  2709. curtop += obj.y;
  2710. }
  2711. return {'x': curleft, 'y': curtop};
  2712. }
  2713.  
  2714. function trickleUp(el) { try {
  2715. var style=window.document.defaultView.getComputedStyle(el, null);
  2716. //if (/^rel/.test(style.position)) el.style.setProperty("position","static", "important");
  2717. if (/^abs/.test(style.position)) { el.style.setProperty("margin-left","0px", "important");
  2718. el.style.setProperty("margin-right","0px", "important");
  2719. log("trickleUp unset abs, margins, on "+el.tagName+" "+el.id);
  2720. }
  2721. if (el.parentNode) trickleUp(el.parentNode); } catch(e){}
  2722. }
  2723.  
  2724. function getTimeZone() {
  2725. var right_now = new Date();
  2726. var jan_here = new Date(right_now.getFullYear(), 0, 1, 0, 0, 0, 0);
  2727. var temp = jan_here.toGMTString();
  2728. var jan_GMT = new Date(temp.substring(0, temp.lastIndexOf(" ")-1));
  2729. var offset = (jan_GMT - jan_here) / (1000 * 60 * 60);
  2730. offset|=0;
  2731. //log("off set "+offset);
  2732. switch(offset) {
  2733. case 0: return "GMT";
  2734. case 5: return "eastern";
  2735. case 6: return "central";
  2736. case 7: return "mountain";
  2737. case 8: return "pacific";
  2738. default: return "GMT+"+-offset;
  2739. }
  2740. }
  2741.  
  2742. function getById(id) {
  2743. var el=window.document.getElementById(id);
  2744. return el;
  2745. }
  2746.  
  2747. function getByIdOrClass() {
  2748. var res=[];
  2749. for(var i=0; i<arguments.length; i++) {
  2750. var els=[];
  2751. els[0]=window.document.getElementById( arguments[i] );
  2752. if ( ! els[0] ) els=window.document.getElementsByClassName( arguments[i] );
  2753. for (var j=0; j < els.length; j++) {
  2754. log(arguments[i]+"<len: "+els.length+" push "+els[j].tagName+", id: "+els[j].id+", class "+els[j].className);
  2755. res.push(els[j]);
  2756. }
  2757. }
  2758. return res;
  2759. }
  2760.  
  2761. function fillFromAddressBook() { //alt shift a
  2762. var addressbook="\n"+GM_getValue("formsAddressBook","");
  2763. var act_el=window.document.activeElement;
  2764. var key=act_el.value;
  2765. var line=addressbook.match( RegExp("\\n\\s*"+key+".*", "i") );
  2766. if (!line) line=addressbook.match( RegExp(".*"+key+".*", "i") );
  2767. if (line) line=line[0];
  2768. else {
  2769. alert("Alt-shift-a was hit. There is no line in addressbook with:"+key+", open address book with alt-a to check."+line);
  2770. return;
  2771. }
  2772. var sel=line.match( RegExp("\\n\\s*"+key+"\\S*\\s\(.*\)", "i") ); // Brackets splits to give sel[1] below
  2773. if (!sel) {
  2774. sel=line.match( RegExp("\(\\S*"+key+"\\S*\\s*.*\)", "i") );
  2775. log2("not keyed, sel "+sel+", line:"+line);
  2776. }
  2777. if (sel) {
  2778. sel=sel[1];
  2779. if (sel=="") sel=line;
  2780. act_el.title="From:"+line.replace(/^\n/,"")+", line in address book (alt-shift-a)";
  2781. $(act_el).trigger("mouseout");//over
  2782. $(act_el).trigger("mouseover");//over
  2783. }
  2784. else sel=line;
  2785.  
  2786. act_el.value=sel;
  2787. var tip=$("<p><strong>"+act_el.title+"</strong></p>");
  2788. $(act_el).after(tip);
  2789. tip.fadeOut(20000);
  2790. }
  2791.  
  2792. function esc(str) {
  2793. return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  2794. }
  2795.  
  2796. function formsAddressBook() { //alt a
  2797. var addressbook=GM_getValue("formsAddressBook","");
  2798. var last_elem, old_style={}, flag;
  2799. var pwin=prompt3("", addressbook,
  2800. function(edited_text) {
  2801. log("reply callback txt:"+edited_text+", type: "+typeof edited_text);
  2802. restoreStyle();
  2803. if (edited_text != null) GM_setValue("formsAddressBook", edited_text );
  2804. });
  2805. pwin.moveTo(screen.width,screen.height/2-300);
  2806. var textarea=pwin.document.getElementById("p2reply");
  2807. $(textarea).attr("ondblclick", "window.fsfdblclick=this; ");
  2808. textarea.style.height=Math.round(pwin.innerHeight*0.66)+"px";
  2809. var hfl=pwin.document.getElementById("hostFormsListButton");
  2810. var heading=pwin.document.createElement("div");
  2811. heading.id="sfshead";
  2812. // on https, secure sites, cannot set tags in innerHTML nor access window.document.
  2813. heading.innerHTML="Simple Form Saver Address/Note Book. Shortcuts: Alt-a for this window. Alt-Shift-a "
  2814. +"to fill value in using name typed in form input. <BR/>Select or Edit text below. "
  2815. +"Hit OK to save. Put one item on each line, eg, Steve 056 703 4567."
  2816. +"<br/>Double click on a word or number below to select it and the text up to the end of "
  2817. +"line (numbers may be automatically parsed-in on certain sites); "
  2818. +"this selection is then automatically entered into the field of form on the main webpage.<br/>"
  2819. +"<input type='checkbox'>Tick to keep this window open; after each selection the next field on "
  2820. +"the form is selected.</input>";
  2821. heading.title="Use \\n in text in order to copy a newline into a textarea. "
  2822. +"Double click on the words 'on' or 'off' to change tickboxes. "
  2823. +"Double click on a word that is the same as the name from a drop-down selection box.";
  2824. heading.style.cssText="font-size: small;";
  2825. textarea.parentNode.insertBefore(heading, textarea);
  2826. saveAndSetStyle(window.document.activeElement);
  2827. var intid=setInterval(function() {
  2828. try{ var h=prompt_win.innerHeight; } catch(e){ log("clear interv"); clearInterval(intid); }
  2829. if (pwin.fsfdblclick) {
  2830. pwin.fsfdblclick=false;
  2831. var textarea=pwin.document.getElementById("p2reply");
  2832. var s=textarea.selectionStart;
  2833. var text=textarea.value;
  2834. var sel=text.substring(s).split("\n")[0];
  2835. textarea.selectionEnd = s + sel.length;
  2836. setTimeout(function(){textarea.selectionStart=textarea.selectionEnd=0;}, 750);
  2837. sel=sel.trim();
  2838. sel=sel.replace(/\\n/g,"\n");// convert \n to a real newline
  2839. var heading=pwin.document.getElementById("sfshead");
  2840. var keep_open_tick=heading.lastElementChild.checked;
  2841. var act_el=window.document.activeElement;
  2842. log("dblclick:"+sel+". active:"+act_el.tagName+", type: "+act_el.type+", name "
  2843. +act_el.name+" "+keep_open_tick);
  2844. if (/SELECT/i.test(act_el.tagName)) {
  2845. sel=sel.split(" ")[0];/*first word*/
  2846. for (var i=0; act_el[i]; i++)
  2847. if (act_el[i].textContent.match(sel)) {
  2848. act_el.value=act_el[i].value;break; }
  2849. }
  2850. else if (act_el.type=="checkbox") {
  2851. sel=sel.split(" ")[0];/*first word*/
  2852. if (sel=="on") act_el.checked=true;
  2853. else if (sel=="off") act_el.checked=false;
  2854. }
  2855. else if (act_el.type=="radio") {
  2856. sel=sel.split(" ")[0];/*first word*/
  2857. var radios = window.document.getElementsByName( act_el.name );
  2858. log("radio, tc "+act_el.textContent+", val "+act_el.value+" name "+act_el.name
  2859. +" #of name:"+radios.length);
  2860. for( i = 0; i < radios.length; i++ ) {
  2861. var rad=radios[i];
  2862. log("radio value:"+rad.value+"=="+sel+".");
  2863. if (rad.value.match(sel)) { rad.checked=true;log("checkd it!");}
  2864. log("radio "+i+" "+rad.checked+" "+rad.textContent);
  2865. rad.focus(); }
  2866. }
  2867. else /* ordinary input/textarea: */
  2868. act_el.value=sel.substr(sel.indexOfRegExp(/[0-9]/,0)); // set to 1st number on, or all if none.
  2869. if (!keep_open_tick) {
  2870. GM_setValue("formsAddressBook", text.replace(/^\s*|\s*$/g,""));//trim and save
  2871. restoreStyle();
  2872. pwin.close();
  2873. } //no tick
  2874. else { // document.activeElement focused element;
  2875. act_el=window.document.activeElement;
  2876. var tinputs = getElementsByTagNames('input,textarea,select'), firstOne, found;
  2877. log("inputs "+tinputs.length);
  2878. for(var i=0; i<tinputs.length; i++) {
  2879. var inp=tinputs[i];
  2880. var style=window.document.defaultView.getComputedStyle(inp, null)||{};
  2881. var invisible=style.display=="none" || style.visibility=="hidden" || inp.offsetHeight==0, seek;
  2882. if (seek && !found && !invisible) { log("seek now true, n: "+inp.name);found=inp;}
  2883. if (act_el==inp) { log("This inp is active: "); seek=true;}
  2884. log("tinputs "+inp.id+" "+style.display+" "+style.visibility+", h:"+inp.offsetHeight+" "+seek+" "+inp.tagName+" act: "+act_el.tagName+", n "+inp.name);
  2885. if (!invisible && !firstOne) firstOne=inp;
  2886. }
  2887. if (!found) { found=firstOne;}
  2888. // log2("found, id:"+found.id+", tag "+found.tagName+", type "+found.type+", name "+found.name+", title "+found.title
  2889. // +" dim "+found.offsetWidth+"x"+found.offsetHeight
  2890. // +"\n style "+found.style.cssText);
  2891. found.focus();
  2892. //found.select();
  2893. scrollToMid(found);
  2894. restoreStyle();
  2895. //save
  2896. saveAndSetStyle(found);
  2897. }
  2898. }//end if fsfdblclick
  2899. },500); //end textarea.addEventListener()
  2900. function saveAndSetStyle(el) {
  2901. var bcolor;
  2902. if (el.type=="radio"||el.type=="checkbox") { el=el.parentNode; bcolor="orange"; }
  2903. //save
  2904. var style=getComputedStyle(el, null);
  2905. with (style) { //log("get settings "+borderWidth+" "+borderStyle+" "+borderColor);
  2906. old_style.bw=borderWidth; old_style.bs=borderStyle ; old_style.bc=borderColor }
  2907. //set
  2908. with (el.style) {
  2909. borderColor= bcolor||"red"; borderWidth= "2px" ; margin="0px"; padding="2px"; borderStyle= "dashed" ; }
  2910. last_elem=el;
  2911. }
  2912. function restoreStyle() {
  2913. if (last_elem) with (last_elem.style) { borderColor= old_style.bc; borderWidth= old_style.bw;
  2914. borderStyle= old_style.bs; }
  2915. }
  2916. } //end formsAddressBook()
  2917.  
  2918. scrollToMid=function(elem) {
  2919. var pos=getXY(elem);
  2920. var midY=window.innerHeight/2|0, midX=window.innerWidth/2|0;
  2921. //log2("mid "+midX+" "+midY+", scrollPos:"+window.scrollX +" "+window.scrollY+". Elem pos: "+uneval(pos));
  2922. pos.x -= window.scrollX+midX; pos.y -= window.scrollY+midY
  2923. scrollBy(pos.x, pos.y)
  2924. }
  2925.  
  2926. function exportImport() {
  2927. log("Export/Import called at "+location.href);
  2928. var data;
  2929. data=GM_getValue('hostFormsList');
  2930. log("export "+typeof data);
  2931. if (data && data.substr) log("10 "+data.substr(0,10));
  2932. else data=JSON.stringify(data);
  2933. if ( data && data[0] != "(" ) data = "(" + data + ")";
  2934. var pwin=prompt3("Warning, clicking OK will set the entire form "
  2935. +"data to the values below. The existing form data so far stored by this script is "
  2936. +"given below and can be copied and pasted to the same export/import window on "
  2937. +"another browser, or backed up to a text file and restored later on in this same browser. "
  2938. +"Or, if you risk it, you can even edit the form data and click OK to save it, for example if "
  2939. +"the host/website name has changed"
  2940. +"GM script Simple Form Saver.\n\n",
  2941. data?data:"",
  2942. function(reply) {
  2943. if (reply==null) { log("rnull"); return;}
  2944. log("reply "+typeof reply);
  2945. if (reply) log("10 "+reply.substr(0,10)+" size "+reply.length);
  2946. //if (reply[2]!='"') {
  2947. //translate 2xUnevals (an FF write) to 2xJSON.stringifys (a chrome write)
  2948. //on chrome, read is JSON parse followed by an eval, FF is 2xeval. One of each is in callees other is in GM_*Value wrappers below. Redef of uneval on chrome.
  2949. log("eval "+eval);
  2950. try {
  2951. if (Chrome) {
  2952. reply=JSON.parse(reply);
  2953. log(" JSON res:"+reply);
  2954. }
  2955. else reply=eval(reply);
  2956. }catch(e) {GM_log("Caught error: "+e);}
  2957. log("Ex/In Got object "+reply);
  2958. //}
  2959. GM_setValue('hostFormsList', reply);
  2960. readPersistentData("export");
  2961. });
  2962. var textarea=pwin.document.getElementById("p2reply");
  2963. textarea.style.setProperty("height", "80%", "");
  2964. setTimeout(function(){textarea.scrollTop=9999999;}, 400);
  2965. }
  2966.  
  2967. function unhidePasswords() {
  2968. //showHiddenElements();
  2969. var pws = document.querySelectorAll('input[type="password"]');
  2970. unhide="onceoff";
  2971. for (var i=0; i < pws.length; i++) {
  2972. var inp=pws[i];
  2973. inp.form.autocomplete="off";
  2974. inp.type="text";
  2975. moveCaretToEnd(inp);
  2976. if (inp.form) {
  2977. inp.form.addEventListener("mousedown", function(e) {
  2978. inp.type="password";
  2979. setTimeout(function(){ inp.type="text";inp.focus();moveCaretToEnd(inp);},500);
  2980. }, true);
  2981. }
  2982. }
  2983. }
  2984. //prompt3("abc","", function(v){alert("value:"+v+". type:"+typeof v)});
  2985.  
  2986. function getSetConfigs() {
  2987. prompt3("Enter config value name to get or set its value,\nsee userscripts/openuserjs.org websites for meaning. "
  2988. +"\nValue names/value types are:\npaste_shortcut (ctrl-v), use value 'true' or leave blank."
  2989. +"\nline_order"
  2990. +"\noptionals"
  2991. +"\nparse_method"
  2992. ,"",function(configName){
  2993. if (configName != null){
  2994. var val=GM_getValue(configName);
  2995. prompt3("Current value is given here, enter new value to change it:", val, function(configValue){
  2996. if (configValue==null) return;
  2997. GM_setValue(configName, configValue);
  2998. alert("Set "+configName+" for "+scriptName+" to:"+configValue)
  2999. });
  3000. }
  3001. });
  3002. }
  3003. function showHiddenAncestors(el) {
  3004. var ancestors=el.parents();
  3005. ancestors=ancestors.add(el);
  3006. ancestors.each(function(){this.type="";});
  3007. ancestors.each(function(){$(this).css("visibility","visible"); $(this).css("opacity","1"); });
  3008. ancestors.each(function(){
  3009. if (this.style.display=="none") this.style.display="inline";
  3010. if ($(this).width()==0) $(this).width("100%");
  3011. if ($(this).height()==0) $(this).height("100%");
  3012. });
  3013. ancestors.each(function(){$(this).css({display: "inline", zIndex:"300"}); });
  3014. }
  3015.  
  3016. function showHiddenElements_full() {
  3017. var hiddeninputs, op0orvishidden, jqhidden, dispnone;
  3018. gethiddens(1); //fills vars hiddeninputs and op0orvishidden, jqhidden and dispnone.
  3019. hiddeninputs.each(function(){this.type="";});
  3020. op0orvishidden.each(function(){$(this).css("visibility","visible"); $(this).css("opacity","1"); });
  3021. jqhidden.each(function(){
  3022. if (this.style.display=="none") this.style.display="inline";
  3023. if ($(this).width()==0) $(this).width("100%");
  3024. if ($(this).height()==0) $(this).height("100%");
  3025. });
  3026. dispnone.each(function(){$(this).css({display: "inline", zIndex:"-300"}); });
  3027. gethiddens(2);
  3028. if (op0orvishidden.length)
  3029. op0orvishidden.each(function(){
  3030. GM_log("style.vis "+this.style.visibility+", style.op "+ this.style.opacity
  3031. +" it was "+$(this).data("opandviswas"));
  3032. });
  3033. function gethiddens(run) {
  3034. hiddeninputs=$("input[type=hidden]");
  3035. op0orvishidden=$("*").filter(function() {
  3036. $(this).data("opandviswas", run +"# op: "+$(this).css("opacity") + ", vis: " + $(this).css("visibility"));
  3037. return ( $(this).css("opacity") < 0.25 || $(this).css("visibility") == "hidden" );
  3038. } );
  3039. jqhidden=$(":hidden");
  3040. jqhidden=jqhidden.filter(function() { return ! /script|style/i.test(this.tagName); });
  3041. dispnone=$("*").filter(function() {
  3042. return $(this).css("display")=="none" && ! /script|style/i.test(this.tagName);;
  3043. });
  3044. GM_log("Hidden inputs "+hiddeninputs.length+", invisible or low opacity elements "+op0orvishidden.length+", JQ :hidden elements "+jqhidden.length+", dispnone "+dispnone.length);
  3045. }
  3046. }
  3047. /////////////////
  3048. /////////////////// ////////////WRAPPER for Google Chrome etc.///////////////////////////////////////////
  3049. ///////////////////Wrapper version 4.0.3
  3050. // Notes: the this pointer on chrome may differ from ff.
  3051. // keypress does not pass on altKey setting (charCode is not set for keypress but for keydown for both).
  3052. //
  3053. // Functions: 1. Provides platform independence, certain functions on Firefox but missing on Chrome are provided.
  3054. // 2. Alert and prompt windows of reasonable size compared to restricted native ones.
  3055. // 3. GMsetValue etc. overridden to write/read json values.
  3056. // 4. On Chrome GM menu is provided as an GM icon top right, it overrides GM_registerMenuCommand(). Now using GM_registerMenuCommand JS module.
  3057. // 5. On Chrome enables dynamic loading of jquery and jqueryui.
  3058. // 6. On Chrome provides access to own version of status bar.
  3059.  
  3060. function GM_platform_wrapper(title, use_jquery, loadedCB) { //use_jquery =[1,2,4,6], bit 2 for UI, eg for prompt3 function, bit 4 for new GM_menu registration.
  3061. var csname=title.replace(/\W*/g,""), uwin=unsafeWindow, bg_color="rgb(173,216,239, 0.8)",depends=[],tmp_store={}; //"#add8e6"
  3062. String.prototype.parse = function (r, limit_str) { var i=this.lastIndexOf(r);var end=this.lastIndexOf(limit_str);if (end==-1) end=this.length; if(i!=-1) return this.substring(i+r.length, end); }; //return string after "r" and before "limit_str" or end of string.
  3063. String.prototype.trim = function (charset) { if (!charset) return this.replace(/^\s*|\s*$/g,""); else return this.replace( RegExp("^["+charset+"]*|["+charset+"]*$", "g" ) , "");}; //trim spaces or any set of characters.
  3064. window.outerHTML = function (obj) { return new XMLSerializer().serializeToString(obj); };
  3065. window.FireFox=false; window.Chrome=false; window.uscript_name=title;
  3066. window.confirm3=confirm3; window.prompt3=prompt3; window.alert3=alert3;
  3067. window.stringify=JSON.stringify;
  3068. window.local_getValue=local_getValue; window.local_setValue=local_setValue;
  3069. //problem with localStorage is that webpage has full access to it and may delete it all, as bitlee dotcom does at very end, after beforeunload & unload events.
  3070. function local_setValue(name, value) { name="GMxs_"+name; if ( ! value && value !=0 ) { try{ localStorage.removeItem(name); } catch(e){}; return; }
  3071. var str=JSON.stringify(value); try {localStorage.setItem(name, str );}catch(e){}
  3072. }
  3073. function local_getValue(name, defaultValue) { name="GMxs_"+name; var value = null; try{value = localStorage.getItem(name);}catch(e){}; if (value==null) return defaultValue;
  3074. value=JSON.parse(value); return value;
  3075. } //on FF it's in webappsstore.sqlite
  3076. ///
  3077. ///Split, first firefox only, then chrome only exception for function definitions which of course apply to both:
  3078. ///
  3079. if ( ! /^Goo/.test (navigator.vendor) ) { /////////Firefox:
  3080. window.FireFox=true;
  3081. window.brversion=parseInt(navigator.userAgent.parse("Firefox/"));
  3082. var old_set=GM_setValue, old_get=GM_getValue;
  3083. GM_setValue=function(name, value) { return old_set( name, JSON.stringify(value)); };
  3084. GM_getValue=function(name, defaulT) { var res=old_get ( name, JSON.stringify (defaulT) ); if (res!="") try { return JSON.parse(res); } catch(e) { console.log(uscript_name+" during parse, JSON Error: "+name+", will return result anyway, type:"+(typeof res)+", value:"+res+", error:"+e); } return res; }; //rmed eval less backcompat
  3085. if (use_jquery&2 && ! document.getElementById("jqueryuiCss") ) {
  3086. var src=GM_getResourceText("jqueryuiCss");
  3087. $('head').append("<style id=jqueryuiCss>"+src+"</style>");
  3088. // $("head").append ("<link id=jqueryuiCss href="+url+"rel=stylesheet type=text/css>" ); //would also load ui images but extra load.
  3089. }
  3090. return;
  3091. } //end ua==Firefox
  3092. /////////////
  3093. ///////////////////// Only Google Chrome from here, except for functions used above defined below :
  3094. ///////////
  3095. window.Chrome=true;
  3096. window.brversion=parseInt(navigator.userAgent.parse("Chrome/"));
  3097. GM_setValue = function(name, value) { name=title+":"+name; local_setValue(name, value);};
  3098. GM_getValue = function(name, defval) { name=title+":"+name; return local_getValue(name, defval); };
  3099. GM_deleteValue = function(name) { localStorage.removeItem(title+":"+name); };
  3100. // Only Chromium not able for @require...
  3101. if (use_jquery) { //Can be 1/true (default jquery), 2: jquery-ui, 4:GM_registerMenuCommand, or 6 both.
  3102. var cb_countdown=1;
  3103. switch(use_jquery) { case 2: cb_countdown+=2;break; case 4: cb_countdown+=1;break; case 6: cb_countdown+=3; }
  3104. if(!window.jQuery)
  3105. loadScript("https://code.jquery.com/jquery-latest.js");
  3106. else if (use_jquery==1) sourceIn("");
  3107. if (use_jquery & 2) { //010 // eg, parseInt("010", 2) ==> 2
  3108. loadScript("https://code.jquery.com/ui/1.10.3/jquery-ui.js");
  3109. loadScript("https://code.jquery.com/ui/1.10.3/themes/vader/jquery-ui.css");
  3110. }
  3111. if (use_jquery & 4) { //100, eg parseInt("100", 2) => 4, 110 => 6
  3112. loadScript("https://openuserjs.org/src/libs/slow!/GM_registerMenuCommand_Submenu_JS_Module.js");
  3113. }
  3114. }//if use_jquery
  3115. function sourceIn(filename, filetext) {
  3116. if (/\.css$/.test(filename)) GM_addStyle(filetext);
  3117. else tmp_store[filename]=filetext;
  3118. cb_countdown--;
  3119. if (cb_countdown==0) {
  3120. while(depends.length)
  3121. try { window.unsafeWindow=unsafeWindow;eval.call(window,tmp_store[depends.shift()]); } catch(e){ console.log("Platform Wrapper userscript, js eval err"+e); }
  3122. setTimeout(loadedCB,0);
  3123. }
  3124. }
  3125. function loadScript(url) {
  3126. var filen=url.split("/").splice(-1);
  3127. var file=GM_getValue(filen,"");
  3128. depends.push(filen);
  3129. if (file) {
  3130. //console.info("Using cached "+filen+". To delete, close chromium and find sqlite file in chromium main dir under dir 'Local Storage' as a file with site name suffixed with .locastorage");
  3131. sourceIn(filen,file); return;
  3132. }
  3133. GM_xmlhttpRequest( { method: "GET", url: url, onload:function(r) { //asynch
  3134. GM_setValue(filen, r.responseText);
  3135. sourceIn(filen, r.responseText);
  3136. } });
  3137. }
  3138. uneval=function(x) {
  3139. return "("+JSON.stringify(x)+")";
  3140. };
  3141. GM_addStyle = function(css, doc) {
  3142. if (!doc) doc=window.document;
  3143. var style = doc.createElement('style');
  3144. style.textContent = css;
  3145. doc.getElementsByTagName('head')[0].appendChild(style);
  3146. };
  3147. function setStatus(s) {
  3148. //if (s) s = s.toLowerCase ? s.toLowerCase() : s;
  3149. setStatus.value=s;
  3150. var div=document.getElementById("GMstatus");
  3151. if (div) {
  3152. if (s) { div.textContent=s; div.style.display="block"; setDivStyle(); }
  3153. else { setDivStyle(); div.style.display="none"; }
  3154. }
  3155. else if (s) {
  3156. div=document.createElement('div');
  3157. div.textContent=s;
  3158. div.setAttribute('id','GMstatus');
  3159. if (document.body) document.body.appendChild(div);
  3160. setDivStyle();
  3161. div.addEventListener('mouseout', function(e){ setStatus(); },false);
  3162. }
  3163. if (s) setTimeout( function() { if (s==setStatus.value) setStatus(); }, 10000);
  3164. setTimeout(setDivStyle, 100);
  3165. function setDivStyle() {
  3166. var div=document.getElementById("GMstatus");
  3167. if ( ! div ) return;
  3168. var display=div.style.display;
  3169. div.style.cssText="border-top-right-radius: 6px; "
  3170. +"background: linear-gradient(#fff,#ddd);"
  3171. +"color: rgba(0,0,0,0.8) ! important; font-weight:500;" //text-shadow: 0 1px 0 rgba(0,0,0,.4);"
  3172. +"font-family: Helvetica; font-size: 15px;line-height:20px; z-index: 9909099; "
  3173. +"padding: 2px; padding-top:0px; border: 1px solid #82a2ad; "//Lucida Sans Unicode;
  3174. +"position: fixed ! important; bottom: 0px; " + (FireFox && brversion >= 4 ? "left: -1px" : "" );
  3175. div.style.display=display;
  3176. }
  3177. }//setStatus()
  3178. initStatus();
  3179. function initStatus() {
  3180. window.__defineSetter__("status", function(val){ setStatus(val); });
  3181. window.__defineGetter__("status", function(){ return setStatus.value; });
  3182. }
  3183. function prompt3(text, init_value, handler, following_text, title){
  3184. if (!init_value) init_value="";
  3185. var box=confirm3(text, handler, "", "", title, init_value, following_text);
  3186. return box;
  3187. } //prompt3()
  3188. function alert3(text){
  3189. confirm3(text, "", null);
  3190. }
  3191. function confirm3(text, handler, btn1, btn2, title, init_answer, following_text ) {
  3192. if(btn1 !== null) btn1=btn1||"Cancel";
  3193. if (!confirm3.cnt) confirm3.cnt=1; else confirm3.cnt++;
  3194. if (!handler) handler=function(){};
  3195. btn2=btn2||"OK";
  3196. title=title||"";
  3197. var topleft_btn="\u2573"; //"x";
  3198. text=text.replace(/</g,"&lt;").replace(/\n/g,"<br>").replace(/\t/g,"&nbsp;&nbsp;&nbsp;&nbsp;");
  3199. following_text=following_text||"";
  3200. following_text=following_text.replace(/</g,"&lt;").replace(/\n/g,"<br>").replace(/\t/g,"&nbsp;&nbsp;&nbsp;&nbsp;");
  3201. var res, buttons={}, box, dinput="";
  3202. if (init_answer!==undefined) {
  3203. dinput="<input id=fsfdinput style='width:100%; background:white;color:black'></input>";
  3204. if (!text) dinput="<textarea id=fsfdinput style='width:100%; height:98%; white-space:pre-wrap;'></textarea>";
  3205. }
  3206. box=$("<div id=sfsconfirm3 title=x><div id=sfsmsgtxt></div>"
  3207. +dinput+"<div id=sfsfollowing></div></div>");
  3208. var ip=box.find("input, textarea");ip.val(init_answer);
  3209. ip.css({bordertyle:"indent", borderWidth:"4px",padding:0,margin:0});
  3210. var msgtxt=box.find("#sfsmsgtxt"); msgtxt.html(text);
  3211. var followtxt=box.find("#sfsfollowing"); followtxt.html(following_text);
  3212. //GM_addStyle("pre { font-family: Sans-Serif;}"); // if wrap text in <pre>
  3213. $("body").append(box);
  3214. buttons[btn2]=function(e) { //OK
  3215. var ip=$(this).find("input, textarea");//can't use id since may be duplicated, queueing of dialoges?
  3216. handler(ip.length?ip[0].value:1);
  3217. $(this).dialog("close");
  3218. e.stopPropagation(); e.preventDefault();
  3219. };
  3220. if(btn1 !== null) buttons[btn1]=function(e) { //cancel
  3221. handler(null); $(this).dialog("close");
  3222. e.stopPropagation(); e.preventDefault();
  3223. };
  3224. box.dialog({ position: "center", modal: true, buttons: buttons, resizable: true,height:"auto", width:"auto", overflow:"auto" }); //wraps box in dialog elems.
  3225. if (!ip[0] || ip[0].tagName=="INPUT") box.keydown(function (event) {
  3226. if (event.keyCode == 13) { $(this).parent().find("button:eq(1)").trigger("click"); } });
  3227. //
  3228. // Dialog element has three component elements: titlebar, box and buttonpane.
  3229. var dialog=box.closest(".ui-dialog"), titlebar=$(".ui-dialog-titlebar",dialog), buttonpane=$(".ui-dialog-buttonpane",dialog), limit=window.innerHeight*0.66*0.85, longD;
  3230. if (box.height()>limit) { longD=true;log("too long "+limit); box.height(limit); dialog.css("top", window.innerHeight*(1/7)+"px"); }
  3231. limit=window.innerWidth*.66;
  3232. if (box.width()>limit) { box.width(limit); dialog.css("left", window.innerWidth*(1/6)+"px"); }
  3233. dialog.css({ zIndex:2147483642, textAlign:"left", position: "fixed",
  3234. left:"15%", width:"50%",height:"70%",top:"10%", fontSize:"medium" });
  3235. $(dialog).add(titlebar).css({background:"#002400", boxSizing: "content-box"});
  3236. buttonpane.css({background: "#001800",marginTop:0, boxSizing: "content-box"});
  3237. GM_addStyle(".sfsdiax {white-space:nowrap}");
  3238. $(".ui-button").css("font-size", "small");
  3239.  
  3240. buttonpane.css({width:"-moz-available",position:"absolute",bottom:0});
  3241. box.css({width:"-moz-available",position: "absolute"});
  3242. box.css({background:"#002000", color:"#eeeeee", overflow:"auto", fontSize:"medium" });
  3243. box.children().css({background:"#002000", color:"#eeeeee"});
  3244. dialog.find("*").addClass("sfsdiax");
  3245.  
  3246. with($(".ui-dialog-title")) {
  3247. children(":eq(0)").title="close"; width("5%"); css({cursor:"pointer",marginTop:"-4px",marginLeft:"-4px"});
  3248. click(function() { box.dialog("close"); } );
  3249. hover(function() { log("ft "); $(this).toggleClass("whitebtn");});
  3250. }
  3251. with(titlebar.find("button")) { text("x"); css({top:7,right:0}); }
  3252. jQuery.fn.reverse = [].reverse;
  3253. if(btn1===null) { var btn=$(".ui-dialog-buttonset .ui-button");btn.css({left:"40%"}); ;
  3254. $(".ui-button")[0].focus();$(".ui-dialog-buttonset").css({float:"none"});}
  3255. else $(".ui-dialog-buttonset button").reverse().each(function() {this.focus(); return false;}); //return false, break from each(), return true, equiv to continue
  3256. if (longD) box.focus();
  3257. if (ip[0]) { ip.focus();} //else dialog.focus();
  3258. //console.log("outerHeights:",buttonpane,buttonpane.outerHeight(), titlebar,titlebar.outerHeight());
  3259. titlebar.height("7%");box.height("65%");buttonpane.height("10%");
  3260. box.css({bottom: buttonpane.outerHeight(), top: titlebar.outerHeight(), boxSizing: "content-box" });
  3261. box.scrollTop(0);
  3262. // var wrapper="<div id=iwrapper style='position:fixed !important; width:60%; height:80%;'></div>";
  3263. // dialog.wrap(wrapper);
  3264. // wrapper.draggable();
  3265. return box;
  3266. }//end fun confirm3();
  3267. } //end platform_wrapper()