f18.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. "use strict";
  2. FW_version["f18.js"] = "$Id: f18.js 17478 2018-10-07 16:45:18Z rudolfkoenig $";
  3. // TODO: hierMenu+Pin,SVGcolors,floorplan
  4. // Known bugs: AbsSize is wrong for ColorSlider
  5. var f18_attr={}, f18_sd, f18_icon={}, f18_room, f18_grid=20, f18_margin=10;
  6. var f18_small = (screen.width < 480 || screen.height < 480);
  7. $(window).resize(f18_resize);
  8. $(document).ready(function(){
  9. f18_room = $("div#content").attr("room");
  10. f18_sd = $("body").attr("data-styleData");
  11. if(f18_sd) {
  12. eval("f18_sd="+f18_sd);
  13. if(!f18_sd)
  14. f18_sd = {};
  15. f18_attr = f18_sd.f18;
  16. if(f18_attr)
  17. delete(f18_attr.cols); // fix the past
  18. } else {
  19. f18_sd = {};
  20. }
  21. if(!f18_sd.f18) {
  22. f18_attr = { "Pinned.menu":"true", "hidePin":"true" };
  23. f18_resetCol();
  24. f18_sd.f18 = f18_attr;
  25. }
  26. if(typeof f18_attr.savePinChanges == "undefined")
  27. f18_attr.savePinChanges = true;
  28. f18_setCss('init');
  29. var icon = FW_root+"/images/default/fhemicon_ios.png";
  30. $('head').append(
  31. '<meta name="viewport" content="initial-scale=1.0,user-scalable=1">'+
  32. '<meta name= "mobile-web-app-capable" content="yes">'+
  33. '<meta name="apple-mobile-web-app-capable" content="yes">'+
  34. '<link rel="apple-touch-icon" href="'+icon+'">');
  35. if('ontouchstart' in window)
  36. $("body").addClass('touch');
  37. if(f18_small) {
  38. $("body").addClass('small');
  39. f18_attr["Pinned.menu"] = false;
  40. }
  41. var f18_aCol = getComputedStyle($("a").get(0),null).getPropertyValue('color');
  42. f18_loadIcons();
  43. f18_loadTouch();
  44. for(var i in f18_icon)
  45. f18_icon[i] = f18_icon[i].replace('gray', f18_aCol);
  46. f18_icon.pinOut = f18_icon.pinIn
  47. .replace('/>',' transform="rotate(90,896,896)"/>');
  48. // Needed for moving this label
  49. var szc = $("[data-name=svgZoomControl]");
  50. if($(szc).length)
  51. $(szc).before("<div class='SVGplot'></div>");
  52. $(".SVGlabel[data-name]").each(function(){
  53. $(this).attr("data-name", "Room_"+f18_room+"_"+$(this).attr("data-name"));
  54. });
  55. f18_menu();
  56. f18_tables();
  57. f18_svgSetCols();
  58. if(typeof svgCallback != "undefined")
  59. svgCallback.f18 = f18_svgSetCols;
  60. $("[data-name]").each(function(){ f18_setPos(this) });
  61. f18_setWrapColumns();
  62. f18_setFixedInput();
  63. });
  64. function
  65. f18_menu()
  66. {
  67. $("#menu").toggleClass("hidden", f18_small || !f18_getAttr("Pinned.menu"));
  68. if($("#menuScrollArea #menuBtn").length)
  69. return fixMenu();
  70. $("<div id='menuBtn'></div>").prependTo("div#menuScrollArea")
  71. .css( {"background-image":"url('"+f18_icon.bars+"')", "cursor":"pointer" })
  72. .click(function(){ $("#menu").toggleClass("hidden") });
  73. $("div#menu").prepend("<div></div>");
  74. f18_addPin("div#menu > div:first", "menu", true, fixMenu, f18_small);
  75. setTimeout(function(){ $("#menu,#content,#logo,#hdr").addClass("animated"); },
  76. 10);
  77. function
  78. fixMenu()
  79. {
  80. $("#menuScrollArea #logo").css("display",
  81. f18_getAttr("hideLogo") ? "none" : "block");
  82. if(f18_getAttr("Pinned.menu")) {
  83. $("body").addClass("pinnedMenu");
  84. $("#menu").removeClass("hidden");
  85. $("#content").css("left",
  86. (parseInt($("div#menu").width())+2*f18_margin)+"px");
  87. } else {
  88. $("body").removeClass("pinnedMenu");
  89. $("#content").css("left", f18_margin);
  90. }
  91. f18_resize();
  92. }
  93. }
  94. function
  95. f18_tables()
  96. {
  97. $("table.roomoverview > tbody > tr > td > .devType:not(:first)")
  98. .css("margin-top", "20px");
  99. $("table.column tbody tr:not(:first-child) .devType")
  100. .css("margin-top", "20px");
  101. $("#content .devType").each(function(){
  102. var el = this, grp = $(el).text();
  103. f18_addPin(el, "Room."+FW_urlParams.room+".grp."+grp, true,
  104. function(isFixed){
  105. var ntr = $(el).closest("tr").next("tr");
  106. isFixed ? $(ntr).show() : $(ntr).hide();
  107. });
  108. });
  109. if(FW_urlParams.detail) {
  110. $("div.makeTable > span").each(function(){
  111. var el = this, grp = $(el).text();
  112. var nel = $("<div>"+grp+"</div>");
  113. $(el).replaceWith(nel);
  114. f18_addPin(nel, "detail."+grp, true,
  115. function(isFixed){
  116. var ntr = $(nel).next("table");
  117. isFixed ? $(ntr).show() : $(ntr).hide();
  118. });
  119. });
  120. }
  121. if(FW_urlParams.cmd == "style%20list" ||
  122. FW_urlParams.cmd == "style%20select")
  123. $("div.fileList").each(function(){ f18_addPinToStyleDiv(this) });
  124. if(FW_urlParams.cmd == "style%20select")
  125. f18_special();
  126. else if(f18_getAttr("showDragger"))
  127. $("[data-name]").each(function(){ f18_addDragger(this) });
  128. }
  129. function
  130. f18_special()
  131. {
  132. var row, room='all', appendTo;
  133. var attr = function(attrName, inRoom)
  134. {
  135. if(inRoom && room != "all") {
  136. var val = f18_attr["Room."+room+"."+attrName];
  137. if(val != undefined)
  138. return val;
  139. }
  140. return f18_attr[attrName];
  141. };
  142. var setAttr = function(attrName, attrVal, inRoom)
  143. {
  144. if(inRoom && room != "all")
  145. attrName = "Room."+room+"."+attrName;
  146. f18_setAttr(attrName, attrVal);
  147. };
  148. var addRow = function(name, desc, val)
  149. {
  150. $(appendTo)
  151. .append("<tr class='ar_"+name+" "+(++row%2 ? "even":"odd")+"'>"+
  152. "<td "+(val ? "" : "colspan='2'")+">"+
  153. "<div class='col1'>"+desc+"</div></td>"+
  154. (val ? "<td><div class='col2'>"+val+"</div></div></td>" : '')+
  155. "</tr>");
  156. };
  157. var addHider = function(name, inRoom, desc, fn)
  158. {
  159. addRow(name, desc, "<input type='checkbox'>");
  160. $(appendTo+" tr.ar_"+name+" input")
  161. .prop("checked", attr(name, inRoom))
  162. .click(function(){
  163. var c = $(this).is(":checked");
  164. setAttr(name, c, inRoom);
  165. if(fn)
  166. fn(c);
  167. });
  168. };
  169. var addColorChooser = function(name, desc)
  170. {
  171. addRow(name, desc, "<div class='cp'></div>");
  172. FW_replaceWidget(appendTo+" tr.ar_"+name+" div.col2 div.cp", name,
  173. ["colorpicker","RGB"], attr("cols."+name, true), name, "rgb", undefined,
  174. function(value) {
  175. setAttr("cols."+name, value, true);
  176. f18_setCss(name);
  177. });
  178. };
  179. // call drawspecial after got the roomlist...
  180. var f18_drawSpecial = function()
  181. {
  182. var roomHash={};
  183. var cleanRoom = function(){
  184. for(var k in f18_attr) {
  185. var m = k.match(/^room\.([^.]*)\..*/);
  186. if(m && !roomHash[m[1]])
  187. delete f18_attr[k];
  188. }
  189. };
  190. row = 0;
  191. $("div#content tr.f18").remove();
  192. $("div#content > table").append("<tr id='f18rs' class='f18'></tr>");
  193. $("tr#f18rs").append("<div class='fileList f18colors'>f18 special</div>");
  194. $("tr#f18rs").append("<table id='f18ts' class='block wide'></table>");
  195. appendTo = "table#f18ts";
  196. addHider("rightMenu", false, "MenuBtn right<br>on small screen",f18_resize);
  197. addHider("savePinChanges", false, "Save pin changes");
  198. addHider("showDragger", false, "Dragging active", function(c){
  199. if(c) {
  200. if($(".ui-draggable").length) {
  201. $(".ui-draggable").draggable("enable");
  202. $(".dragMove,.dragSize,.dragReset").show();
  203. } else {
  204. $("div.fileList").each(function(){ f18_addDragger(this) });
  205. }
  206. } else {
  207. $(".dragMove,.dragSize,.dragReset").hide();
  208. $(".ui-draggable").draggable("disable");
  209. }
  210. });
  211. addHider("snapToGrid", false, "Snap to grid", function(c){
  212. $(".ui-draggable").draggable("option", "grid",
  213. c ? [f18_grid,f18_grid] : [1,1]);
  214. });
  215. addRow("editStyle", "<a href='#'>Additional CSS</a>");
  216. $(appendTo+" tr.ar_editStyle a").click(function(){
  217. $('body').append(
  218. '<div id="editdlg" style="display:none">'+
  219. '<textarea id="f18_cssEd" rows="25" cols="60" style="width:99%"/>'+
  220. '</div>');
  221. $("#f18_cssEd").val($("head #fhemweb_css").html());
  222. $('#editdlg').dialog(
  223. { modal:true, closeOnEscape:true, width:$(window).width()*3/4,
  224. height:$(window).height()*3/4, title:$(this).text(),
  225. close:function(){ $('#editdlg').remove(); },
  226. buttons:[
  227. { text: "Cancel",click:function(){$(this).dialog('close')}},
  228. { text: "OK", click:function(){
  229. if(!$("head #fhemweb_css"))
  230. $("head").append("<style id='fhemweb_css'>\n</style>");
  231. var txt = $("#f18_cssEd").val();
  232. $("head #fhemweb_css").html(txt);
  233. var wn = $("body").attr("data-webName");
  234. FW_cmd(FW_root+"?cmd=attr "+wn+" Css "+
  235. encodeURIComponent(txt.replace(/;/g,";;"))+"&XHR=1");
  236. $(this).dialog('close');
  237. }}]
  238. });
  239. });
  240. $("div#content > table").append("<tr id='f18rr' class='f18'></tr>");
  241. $("tr#f18rr").append("<div class='fileList f18colors'>"+
  242. "f18: Room specific</div>");
  243. $("tr#f18rr").append("<table id='f18tr' class='block wide'></table>");
  244. appendTo = "table#f18tr";
  245. addRow("room", "Target <select><option>all</option></select>");
  246. FW_cmd(FW_root+"?cmd=jsonlist2 .* room&XHR=1", function(data) {
  247. var d;
  248. try { d=JSON.parse(data); } catch(e){ log(data); return FW_okDialog(e); }
  249. for(var i1=0; i1<d.Results.length; i1++) {
  250. var rname = d.Results[i1].Attributes.room;
  251. if(!rname || rname == "hidden")
  252. continue;
  253. var rl = rname.split(",")
  254. for(var i2=0; i2<rl.length; i2++)
  255. roomHash[rl[i2]] = true;
  256. }
  257. cleanRoom();
  258. var rArr = Object.keys(roomHash); rArr.sort();
  259. $(appendTo+" tr.ar_room select")
  260. .html("<option>all</option><option>"+
  261. rArr.join("</option><option>")+
  262. "</option>")
  263. .change(function(e){
  264. room = $(e.target).val();
  265. f18_drawSpecial();
  266. });
  267. $("tr.ar_room select").val(room);
  268. });
  269. addRow("reset", "Preset colors: "+
  270. "<a href='#'>default</a> "+
  271. "<a href='#'>light</a> "+
  272. "<a href='#'>dark</a> "+
  273. (room=='all' ? '': "<a href='#'>like:all</a>"));
  274. $(appendTo+" tr.ar_reset a").click(function(){
  275. var txt = $(this).text();
  276. if(txt == "like:all") {
  277. delete(roomHash[room]);
  278. cleanRoom();
  279. } else {
  280. f18_resetCol(txt, room);
  281. if(room == "all")
  282. f18_setCss('preset');
  283. }
  284. f18_setAttr();
  285. f18_drawSpecial();
  286. });
  287. addColorChooser("bg", "Background");
  288. addColorChooser("fg", "Foreground");
  289. addColorChooser("link", "Link");
  290. addColorChooser("evenrow", "Even row");
  291. addColorChooser("oddrow", "Odd row");
  292. addColorChooser("header", "Header row");
  293. addColorChooser("menu", "Menu");
  294. addColorChooser("sel", "Menu:Selected");
  295. addColorChooser("inpBack", "Input bg");
  296. $("table.f18colors input").attr("size", 8);
  297. var bgImg = attr("bgImg", true);
  298. addRow("bgImg", "<a href='#'>Background: <span>"+
  299. (bgImg ? bgImg : "none")+"</span></a>");
  300. $(appendTo+" tr.ar_bgImg a").click(function(){
  301. FW_cmd(FW_root+'?cmd='+
  302. '{join("\\n",FW_fileList("$FW_icondir/background/.*.(jpg|png)"))}&XHR=1',
  303. function(data) {
  304. if(data)
  305. data += "none";
  306. var imgList = data.split(/\n/);
  307. FW_okDialog("List of files in www/images/background:<br><ul>"+
  308. "<a href='#'>"+imgList.join("</a><br><a href='#'>")+'</a></ul>');
  309. $("#FW_okDialog a").click(function(){
  310. var txt = $(this).text();
  311. setAttr("bgImg", txt == 'none' ? undefined : txt, true);
  312. $(appendTo+" tr.ar_bgImg span").html(txt);
  313. f18_setCss("bgImg");
  314. });
  315. });
  316. });
  317. addHider("hideLogo", true, "Hide logo", f18_menu);
  318. addHider("hideInput", true, "Hide input", f18_menu);
  319. addHider("hidePin", true, "Hide pin", function(c){
  320. $("div.pinHeader div.pin").css("display", c ? "none":"block");
  321. });
  322. addHider("fixedInput", false, "Fixed input and menu", f18_setFixedInput);
  323. addHider("wrapcolumns",false,"Wrap columns<br>on small screen",
  324. f18_setWrapColumns);
  325. $("div.f18colors").css("margin-top", "20px");
  326. $("tr.f18 div.fileList").each(function(e){ f18_addPinToStyleDiv(this) });
  327. if(f18_getAttr("showDragger"))
  328. $("div.fileList").each(function(){ f18_addDragger(this) });
  329. $("[data-name]").each(function(){ f18_setPos(this) });
  330. f18_setWrapColumns();
  331. };
  332. loadScript("pgm2/fhemweb_colorpicker.js", f18_drawSpecial);
  333. }
  334. function
  335. f18_setFixedInput()
  336. {
  337. // togleClass is true for undefined
  338. $("body").toggleClass("fixedInput", f18_getAttr("fixedInput") ? true:false);
  339. f18_resize();
  340. }
  341. function
  342. f18_setWrapColumns()
  343. {
  344. $("table.block").toggleClass("wrapcolumns", f18_getAttr("wrapcolumns"));
  345. }
  346. function
  347. f18_addPinToStyleDiv(el)
  348. {
  349. var grp = $(el).text();
  350. f18_addPin(el, "style.list."+grp, true,
  351. function(isFixed){
  352. var ntr = $(el).next("table");
  353. isFixed ? $(ntr).show() : $(ntr).hide();
  354. });
  355. }
  356. function
  357. f18_resize()
  358. {
  359. var w=$(window).width();
  360. log("f18.js resize W:"+w+" S:"+screen.width);
  361. var hl = f18_getAttr("hideLogo"),
  362. hi = f18_getAttr("hideInput"),
  363. pm = f18_getAttr("Pinned.menu"),
  364. rm = (f18_getAttr("rightMenu") && f18_small);
  365. var left = 0;
  366. left += hl ? 0 : 40;
  367. left += pm ? 0 : 44;
  368. var lleft = (pm ? 10 : 52);
  369. $("input.maininput").css({ width:(w-left-(FW_isiOS ? 36 : 24))+'px',
  370. "margin-left":(rm ? "0px" : "10px"),
  371. display: hi ? "none":"block"});
  372. $("#menu,#content").css("top", (hi && pm && hl) ? "10px" : "50px");
  373. $("#hdr").css({ left:(rm ? 10 : left)+'px' });
  374. $("#menuBtn").toggle(!pm || f18_small);
  375. $("#menuBtn").css({ left:(rm ? "auto":"10px"), right:(rm ? "10px":"auto") });
  376. $("#logo") .css({ left:(rm ? "auto":lleft ), right:(rm ? "48px":"auto") });
  377. if(FW_isiOS)
  378. $("#logo,#menuBtn").css({ top:'12px'});
  379. }
  380. function
  381. f18_addPin(el, name, defVal, fn, hidePin)
  382. {
  383. var init = f18_getAttr("Pinned."+name);
  384. if(init == undefined)
  385. init = defVal;
  386. $("<div class='pin'></div>")
  387. .appendTo(el)
  388. .css("background-image", "url('"+
  389. (init ? f18_icon.pinIn : f18_icon.pinOut)+"')");
  390. var f18_name = name.replace(/[^A-Z0-9]/ig,'_');
  391. $(el)
  392. .addClass("col_header pinHeader "+f18_name)
  393. .attr("data-name", f18_name);
  394. el = $(el).find("div.pin");
  395. $(el)
  396. .addClass(init ? "pinIn" : "")
  397. .css("cursor", "pointer")
  398. .css("display", (f18_getAttr("hidePin") || hidePin) ? "none" : "block")
  399. .click(function(){
  400. var nextVal = !$(el).hasClass("pinIn");
  401. $(el).toggleClass("pinIn");
  402. $(el).css("background-image","url('"+
  403. (nextVal ? f18_icon.pinIn : f18_icon.pinOut)+"')")
  404. f18_setAttr("Pinned."+name, nextVal);
  405. fn(nextVal);
  406. });
  407. fn(init);
  408. }
  409. // el is the drag-handle, return the corresponding SVG/table etc
  410. function
  411. f18_compEl(el)
  412. {
  413. return $(el).hasClass("fileList") ? $(el).next("table") :
  414. $(el).hasClass("SVGlabel") ? $(el).prev(".SVGplot") :
  415. $(el).closest("tr").next().find(">td>table").first();
  416. }
  417. function
  418. f18_addDragger(el)
  419. {
  420. if(f18_small || FW_urlParams.detail)
  421. return;
  422. if($(el).find(".dragger").length)
  423. return;
  424. var comp = f18_compEl(el);
  425. if($(comp).length == 0)
  426. return;
  427. f18_convertToAbs();
  428. var ep = $(el).position();
  429. var cp = $(comp).position();
  430. var pl = parseInt($(el).css("padding-left").replace("px",""));
  431. var grid = [1,1];
  432. if(f18_getAttr("snapToGrid"))
  433. grid = [f18_grid, f18_grid];
  434. function
  435. save()
  436. {
  437. var nep = $(el).position();
  438. f18_setAttr("Pos."+$(el).attr("data-name"), {
  439. left:nep.left, top:nep.top,
  440. width:$(comp).width(), height:$(comp).height(),
  441. oTop:cp.top-ep.top, oLeft:cp.left-ep.left
  442. });
  443. }
  444. /////////////////////////////////////
  445. // Position
  446. $("<div class='dragger dragMove'></div>")
  447. .appendTo(el)
  448. .css({"cursor":"pointer",
  449. "background-image":"url('"+f18_icon.arrows+"')"})
  450. $(el).draggable({
  451. drag:function(evt,ui){
  452. $(comp).css({ left:ui.position.left+cp.left-ep.left,
  453. top: ui.position.top +cp.top -ep.top });
  454. },
  455. stop:save, grid:grid
  456. });
  457. /////////////////////////////////////
  458. // Size
  459. var off = 20;
  460. if(!$(el).hasClass("SVGlabel")) {
  461. $("<div class='dragSize'></div>")
  462. .appendTo(el)
  463. .css({ cursor:"pointer", "background-image":"url('"+f18_icon.arrows+"')",
  464. position:"absolute", width:"16px", height:"16px",
  465. top:$(comp).height()+2, left:$(comp).width()-off, "z-index":1 })
  466. .draggable({
  467. drag:function(evt,ui){
  468. $(el).css( { width:ui.position.left+off });
  469. $(comp).css({ width:ui.position.left+off,
  470. height:ui.position.top });
  471. },
  472. stop:save, grid:grid
  473. });
  474. }
  475. /////////////////////////////////////
  476. // Reset _all_ elements on this page
  477. $("<div class='dragger dragReset'></div>")
  478. .appendTo(el)
  479. .css({"cursor":"pointer",
  480. "background-image":"url('"+f18_icon.ban+"')"})
  481. .click(function(){
  482. function
  483. delStyle(e)
  484. {
  485. var style = $(e).attr("style");
  486. $(e).attr("style", style.replace(/position:.*;/,"")); // hack
  487. }
  488. $("[data-name]").each(function(){
  489. var el = this;
  490. var name = $(el).attr("data-name");
  491. if(!f18_getAttr("Pos."+name))
  492. return;
  493. delete(f18_attr["Pos."+$(el).attr("data-name")]);
  494. delStyle(el);
  495. delStyle(f18_compEl(el));
  496. $(el).draggable('disable');
  497. $(el).find(".dragMove,.dragSize,.dragReset").hide();
  498. });
  499. f18_setAttr();
  500. });
  501. }
  502. function
  503. f18_applyGrid(pos)
  504. {
  505. if(!f18_getAttr("snapToGrid"))
  506. return;
  507. pos.left = Math.floor((pos.left + f18_grid-1)/f18_grid)*f18_grid;
  508. pos.top = Math.floor((pos.top + f18_grid-1)/f18_grid)*f18_grid;
  509. pos.width = Math.floor((pos.width + f18_grid-1)/f18_grid)*f18_grid;
  510. pos.height = Math.floor((pos.height+ f18_grid-1)/f18_grid)*f18_grid;
  511. }
  512. //////////////////////////
  513. // We use absolute positioning for all elements, if a user positioned
  514. // an item, relative (the default one) else.
  515. function
  516. f18_convertToAbs()
  517. {
  518. // Need two loops, else the sizes/positions are wrong
  519. var sz = {};
  520. $("[data-name]").each(function(){
  521. var el = this;
  522. var name = $(el).attr("data-name");
  523. if(f18_getAttr("Pos."+name))
  524. return;
  525. var comp = f18_compEl(el);
  526. if($(comp).length == 0)
  527. return;
  528. sz[name] = { ep:$(el).position(), cp:$(comp).position(),
  529. w:$(comp).width(), h:$(comp).height() };
  530. });
  531. var needSave=false;
  532. $("[data-name]").each(function(){
  533. var el = this;
  534. var name = $(el).attr("data-name");
  535. if(!name || !sz[name])
  536. return;
  537. needSave = true;
  538. var comp = f18_compEl(el);
  539. var ep=sz[name].ep, cp=sz[name].cp, w=sz[name].w, h=sz[name].h;
  540. var pos = {
  541. left:ep.left, top:ep.top, width:w, height:h,
  542. oTop:cp.top-ep.top, oLeft:cp.left-ep.left
  543. };
  544. f18_doSetPos(el, comp, pos);
  545. f18_setAttr("Pos."+name, pos, true);
  546. });
  547. if(needSave)
  548. f18_setAttr();
  549. }
  550. function
  551. f18_setPos(el)
  552. {
  553. if(f18_small || FW_urlParams.detail)
  554. return;
  555. var comp = f18_compEl(el);
  556. if($(comp).length == 0)
  557. return;
  558. var name = $(el).attr("data-name");
  559. var pos = f18_getAttr("Pos."+name);
  560. if(!pos || !pos.width)
  561. return;
  562. f18_doSetPos(el, comp, pos);
  563. // correct position
  564. var ds = $(el).find(".dragSize");
  565. if($(ds).length)
  566. $(ds).css({ top:pos.height+2, left:pos.width-20 });
  567. }
  568. function
  569. f18_doSetPos(el, comp, pos)
  570. {
  571. f18_applyGrid(pos);
  572. $(el).css({ position:"absolute", left:pos.left, top:pos.top });
  573. if(!$(el).hasClass("SVGlabel")) {
  574. var padding = parseInt($(el).css("padding-left").replace("px",""));
  575. $(el).css({ width:pos.width-padding });
  576. }
  577. $(comp).css({ position:"absolute",
  578. left:pos.left+pos.oLeft, top:pos.top+pos.oTop,
  579. width:pos.width, height:pos.height });
  580. }
  581. function
  582. f18_getAttr(attrName)
  583. {
  584. if(f18_room != undefined) {
  585. var val = f18_attr["Room."+f18_room+"."+attrName];
  586. if(val != undefined)
  587. return val
  588. }
  589. return f18_attr[attrName];
  590. }
  591. function
  592. f18_setAttr(name, value, dontSave)
  593. {
  594. if(name)
  595. f18_attr[name]=value;
  596. if(name && value == undefined)
  597. delete f18_attr[name];
  598. if(name && name.indexOf("Pinned.") == 0 && !f18_attr.savePinChanges)
  599. return;
  600. if(dontSave)
  601. return;
  602. var wn = $("body").attr("data-webName");
  603. FW_cmd(FW_root+"?cmd=attr "+wn+" styleData "+
  604. encodeURIComponent(JSON.stringify(f18_sd, undefined, 1))+"&XHR=1");
  605. }
  606. function
  607. f18_resetCol(name, room)
  608. {
  609. var cols = {
  610. "default":{ bg: "FFFFE7", fg: "000000", link: "278727",
  611. evenrow:"F8F8E0", oddrow:"F0F0D8", header: "E0E0C8",
  612. menu: "D7FFFF", sel: "A0FFFF", inpBack:"FFFFFF" },
  613. light: { bg: "F8F8F8", fg: "465666", link: "4C9ED9",
  614. evenrow:"E8E8E8", oddrow:"F0F0F0", header: "DDDDDD",
  615. menu: "EEEEEE", sel: "CAC8CF", inpBack:"FFFFFF" },
  616. dark: { bg: "444444", fg: "CCCCCC", link: "FF9900",
  617. evenrow:"333333", oddrow:"111111", header: "222222",
  618. menu: "111111", sel: "333333", inpBack:"444444" }
  619. };
  620. var col = (name ? cols[name] : cols["default"]);
  621. var prefix = (room && room != 'all' ? "Room."+room+".cols." : "cols.");
  622. for(var c in col)
  623. f18_attr[prefix+c] = col[c];
  624. }
  625. // Put all the colors into a head style tag, send background changes to FHEM
  626. function
  627. f18_setCss(why)
  628. {
  629. var style = "";
  630. function col(n) { return f18_getAttr("cols."+n, true) };
  631. function bg(c) { return "{ background:#"+c+"; fill:#"+c+"; }\n" }
  632. function fg(c) { return "{ color:#"+c+"; }\n" }
  633. style += ".col_fg, body, input, textarea "+fg(col("fg"));
  634. style += ".col_bg, textarea, input, option "+bg(col("bg"));
  635. style += ".col_link,a:not(.changed),.handle,.fhemlog,input[type=submit],"+
  636. "select,div.ui-widget-content a "+
  637. "{color:#"+col("link")+"!important; stroke:#"+col("link")+";}\n";
  638. style += "svg:not([fill]):not(.jssvg) { fill:#"+col("link")+"; }\n";
  639. style += ".col_evenrow, table.block,div.block "+bg(col("evenrow"));
  640. style += ".col_oddrow,table.block tr.odd,table.block tr.sel "+
  641. bg(col("oddrow"));
  642. style += ".col_header "+bg(col("header"));
  643. style += ".col_menu, table.room "+bg(col("menu"));
  644. style += ".col_sel, table.room tr.sel "+bg(col("sel"));
  645. style += ".col_inpBack, input "+bg(col("inpBack"));
  646. if(col("bg") == "FFFFE7") // default
  647. style += "div.pinHeader.menu {background:#"+col("sel")+";}\n";
  648. style += "div.ui-dialog-titlebar "+bg(col("header"));
  649. style += "div.ui-widget-content "+bg(col("bg"));
  650. style += "div.ui-widget-content, .ui-button-text "+fg(col("fg")+"!important");
  651. style += "div.ui-dialog { border:1px solid #"+col("link")+"; }";
  652. style += "button.ui-button { background:#"+col("oddrow")+"!important; "+
  653. "border:1px solid #"+col("link")+"!important; }\n";
  654. if(typeof DashboardDraggable != "undefined") {
  655. var db = "#dashboard ";
  656. style += db+".dashboard_widgetheader "+bg(col("header"));
  657. style += db+".dashboard_tabnav "+bg(col("menu")+"!important");
  658. style += db+".ui-widget-header .ui-state-default "+bg(col("menu"));
  659. style += db+".ui-widget-header .ui-state-active "+bg(col("sel"));
  660. style += db+".ui-widget-header "+fg(col("fg")+"!important;");
  661. style += db+".ui-widget-header li { border:none!important; }";
  662. style += db+".ui-widget-content a "+fg(col("link")+"!important" );
  663. }
  664. var bgImg = f18_getAttr("bgImg", true);
  665. if(bgImg) {
  666. style += 'body { background-image: url('+FW_root+
  667. '/images/background/'+bgImg+');}';
  668. } else {
  669. style += "body "+bg(col("bg"));
  670. }
  671. $("head style#f18_css").remove();
  672. style = "<style id='f18_css'>"+style+"</style>";
  673. if($("head style#fhemweb_css").length)
  674. $("head style#fhemweb_css").before(style);
  675. else
  676. $("head").append(style);
  677. $("head meta[name=theme-color]").remove();
  678. $("head").append('<meta name="theme-color" content="#'+col("bg")+'">');
  679. // Recolor the menu arrows. CSS does not apply to such SVGs :(
  680. if(why=='init' || why=='preset' || why=='link') {
  681. var a = $("a").get(0);
  682. if(window.getComputedStyle && a) {
  683. var col = getComputedStyle(a,null).getPropertyValue('color');
  684. FW_arrowRight = FW_arrowRight.replace(/rgb[^)]*\)/,col);
  685. FW_arrowDown = FW_arrowDown.replace(/rgb[^)]*\)/,col);
  686. $("div#menu table.room tr.menuTree > td > div > div")
  687. .css("background-image", "url('"+FW_arrowRight+"')");
  688. $("div#menu table.room tr.menuTree.open > td > div > div")
  689. .css("background-image", "url('"+FW_arrowDown+"')");
  690. }
  691. }
  692. }
  693. // SVG color tuning
  694. function
  695. f18_svgSetCols(svg)
  696. {
  697. function col(n) { return f18_getAttr("cols."+n, true) };
  698. if(!svg || !svg.getAttribute("data-origin"))
  699. return;
  700. var style = $(svg).find("> style").first();
  701. var sTxt = $(style).text();
  702. sTxt = sTxt.replace(/font-family:Times/, "fill:#"+col("fg"));
  703. $(style).text(sTxt);
  704. function
  705. addCol(c, d)
  706. {
  707. var r="";
  708. for(var i1=0; i1<6; i1+=2) {
  709. var n = parseInt(c.substr(i1,2), 16);
  710. n += d;
  711. if(n>255) n = 255;
  712. if(n< 0) n = 0;
  713. n = n.toString(16);
  714. if(n.length < 2)
  715. n = "0"+n;
  716. r += n;
  717. }
  718. return r;
  719. }
  720. // SVG background gradient: .css does not work in Firefox, has to use .attr
  721. var stA = $(svg).find("> defs > #gr_bg").children();
  722. var so = "; stop-opacity:1;";
  723. $(stA[0]).attr("style", "stop-color:#"+addCol(col("bg"),10)+so);
  724. $(stA[1]).attr("style", "stop-color:#"+addCol(col("bg"),-10)+so);
  725. }
  726. function
  727. f18_loadIcons()
  728. {
  729. // font-awesome
  730. var f18_svgPrefix='data:image/svg+xml;utf8,<svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="gray" ';
  731. f18_icon.pinIn=f18_svgPrefix+'d="M896 1088q66 0 128-15v655q0 26-19 45t-45 19h-128q-26 0-45-19t-19-45v-655q62 15 128 15zm0-1088q212 0 362 150t150 362-150 362-362 150-362-150-150-362 150-362 362-150zm0 224q14 0 23-9t9-23-9-23-23-9q-146 0-249 103t-103 249q0 14 9 23t23 9 23-9 9-23q0-119 84.5-203.5t203.5-84.5z"/></svg>';
  732. f18_icon.bars=f18_svgPrefix+'d="M1664 1344v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45z"/></svg>';
  733. f18_icon.arrows=f18_svgPrefix+'d="M1792 896q0 26-19 45l-256 256q-19 19-45 19t-45-19-19-45v-128h-384v384h128q26 0 45 19t19 45-19 45l-256 256q-19 19-45 19t-45-19l-256-256q-19-19-19-45t19-45 45-19h128v-384h-384v128q0 26-19 45t-45 19-45-19l-256-256q-19-19-19-45t19-45l256-256q19-19 45-19t45 19 19 45v128h384v-384h-128q-26 0-45-19t-19-45 19-45l256-256q19-19 45-19t45 19l256 256q19 19 19 45t-19 45-45 19h-128v384h384v-128q0-26 19-45t45-19 45 19l256 256q19 19 19 45z"/></svg>';
  734. f18_icon.ban=f18_svgPrefix+'d="M1440 893q0-161-87-295l-754 753q137 89 297 89 111 0 211.5-43.5t173.5-116.5 116-174.5 43-212.5zm-999 299l755-754q-135-91-300-91-148 0-273 73t-198 199-73 274q0 162 89 299zm1223-299q0 157-61 300t-163.5 246-245 164-298.5 61-298.5-61-245-164-163.5-246-61-300 61-299.5 163.5-245.5 245-164 298.5-61 298.5 61 245 164 163.5 245.5 61 299.5z"/></svg>';
  735. }
  736. function
  737. f18_loadTouch()
  738. {
  739. /*!
  740. * jQuery UI Touch Punch 0.2.3
  741. *
  742. * Copyright 2011-2014, Dave Furfero
  743. * Dual licensed under the MIT or GPL Version 2 licenses.
  744. *
  745. * Depends:
  746. * jquery.ui.widget.js
  747. * jquery.ui.mouse.js
  748. */
  749. !function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
  750. }