UNPKG

casbah

Version:

Contract Administration Site * Be Architecural Heroes *

668 lines (580 loc) 21.9 kB
<!doctype html> <style> .picBox{ max-height:350px; max-width:100%;} caption{ font-size:10pt; font-family:'segoe UI'; } figure{ font-size:12pt; font-family:'segoe UI'; font-weight:bold; } .border-not {border-color:black; border-style:solid; border-width:1px;} .photo-frame-not{ padding-left:5px; padding-top:15px; border-color:black; border-style:solid; border-width:1px; } .photo-frame{ padding-left:5px; padding-top:15px;} .photo-text{ padding-left:5px; padding-top:15px;} .photo-image {max-width:100%; padding-left:5px; float:left;} .break-line {margins:auto;} @media print { .row {page-break-inside:avoid; !important;} .control-joint {page-break-inside:auto; !important;} } </style> <script type='text/javascript'> /////////////////////// // Site visit report // main object var svr={}; // cache svr.data={}; svr.change=function(field, valu, callback){ //console.log("SVR_ID", localStorage.getItem("svr_id")) $.ajax({ contentType: "application/x-www-form-urlencoded; charset=UTF-8", data: $.param({ action:"SVR-CHANGE", project_id:localStorage.getItem("project_id"), svr_id:localStorage.getItem("svr_id"), field:field, valu:valu }), error: function(err){ console.log(err.message);}, success: function(result){ if (typeof callback =="function"){callback();}}, type:"POST", url:"/uploads" }); }; // text editor svr.ed=new casbah.Editor(); svr.refresh=function(result, delta){ //console.log("SVR_ID", localStorage.getItem("svr_id")) $.ajax({ contentType: "application/x-www-form-urlencoded; charset=UTF-8", data: $.param({ action:"SVR-SELECT", project_id:localStorage.getItem("project_id"), svr_id:localStorage.getItem("svr_id") }), error: function(err){ console.log(err.message);}, success: function(result){ console.log("SVR-SUCCESS"); svr.notes.render(result.svrs[0]); svr.photos.render(result.svrs[0]); }, type:"POST", url:"/uploads" }); }; svr.notes={}; svr.notes.insert=function(){ //this function is meant to be called from a context menu and without arguments var caller=svr.notes.menu.menu("option","caller"); var sn=$(caller).attr("section_name"); var si=$(caller).attr("section_index"); var copy=$(caller).text(); console.log("INSERT:", sn); //insert copy of text into section svr.data[sn].splice(si, 0, copy); svr.change(sn, svr.data[sn], svr.notes.render); }; svr.notes.menu=$("#svr-notes-menu").menu(); svr.notes.menu.css("position","absolute", "width", "200px").hide(); svr.notes.edit=function(el){ svr.ed.text(el, function(){ var field=svr.ed.target_attr("section_name"); //eg. 'comments' var index=svr.ed.target_attr("section_index"); var text=svr.ed.val(); svr.data[field][index]=text; //svr.data[field][index]=; //eg. ['first comment','revised comment 2'...] console.log("FIELD",field, " UPDATED TEXT:", text); svr.change(field, svr.data[field], function(){ svr.ed.hide(); //refresh (server request and render) or just render cache for now... svr.notes.render(svr.data); }); }); }; svr.notes.ondragstart=function(el, ev){ //note that "Text" argument is required by iexplorer, "text/plain" won't work... ev.dataTransfer.setData("Text", "section_index:"+$(el).attr("section_index") + " " + "section_name:"+$(el).attr("section_name") ); }; svr.notes.ondrop=function(el, ev){ console.log("DROP..."); //var txt=ev.dataTransfer.getData("text/plain"); //note that "Text" argument is required by iexplorer... var tx=ev.dataTransfer.getData("Text"); var a1=tx.indexOf("section_index:"); var a2=tx.indexOf("section_name:"); if ( a1 > -1 && a2 > -1){ //from row index var fsi=tx.slice(a1+"section_index:".length).split(/\s+/)[0]; var fsn=tx.slice(a2+"section_name:".length).split(/\s+/)[0]; //to row index var tsi=$(el).attr("section_index"); var tsn=$(el).attr("section_name"); //console.log("drag from:", fsi, fsn, " drop to:", tsi, tsn); if (fsn == tsn){ //same section casbah.array_fromindex_toindex(svr.data[tsn], fsi, tsi); //save changes then callback render svr.change(tsn, svr.data[tsn], svr.notes.render ); } else if (fsn != tsn) { //different sections //insert item at 'to' section svr.data[tsn].splice(tsi, 0, svr.data[fsn][fsi]); //remove item at 'from' section svr.data[fsn].splice(fsi, 1); //save change for 'to' sections, then save change for 'from' section then callback renderer svr.change(tsn, svr.data[tsn], function(){ svr.change(fsn, svr.data[fsn], svr.notes.render);}); } } }; svr.notes.render=function(r){ //result from server or undefined //r={generals:[...], comments:["comment 1", "comment 2",...],...} // svr.notes.render called without argument means cache changed and server updated, no need to get result from server just use cache, otherwise update local cache with server results if(typeof r == "undefined"){ r=svr.data;} else { svr.data=r;} //reformat result to suit comments template svr.data.rows=[].concat( svr.notes.reformat(r.generals, "generals", 1, "General Notes"), svr.notes.reformat(r.comments, "comments", 2, "Comments & Observations"), svr.notes.reformat(r.issues_closed, "issues_closed", 3, "Closed Issues"), svr.notes.reformat(r.issues, "issues", 4, "New and Ongoing Issues") ); console.log("SVR data:", svr.data); $("#svr-header-placeholder").html(svr.header.template({svr:r})); $("#svr-titleblock-right-placeholder").html(svr.titleblock_right.template({svr:r})); $("#svr-notes-placeholder").html(svr.notes.template({rows:svr.data.rows})); } svr.notes.reformat=function(section, section_name, section_num, section_title){ //console.log("reformat_notes:", section); var rows=[{section_heading:true, section_name:section_name, txt:section_title, section_num:section_num, section_index:0}]; for (i in section){rows.push({ txt:section[i], section_item:true, section_name:section_name, section_num:section_num, section_index:Number(i) });} return rows; }; svr.notes.remove=function(el){ //this function is meant to be called from a context menu and without arguments var caller=svr.notes.menu.menu("option","caller"); var sn=$(caller).attr("section_name"); var si=$(caller).attr("section_index"); //delete note from section svr.data[sn].splice(si, 1); //save change for 'to' sections, then save change for 'from' section then callback renderer svr.change(sn, svr.data[sn], svr.notes.render); }; svr.notes.template=Handlebars.compile($("#svr-notes-template").html()); svr.notes.update=function(row, rowid){ console.log("comments update ROWID:\n", rowid, "ROW:\n",row) }; ///////////////// // PHOTOS svr.photos={} svr.photos.ondrop=function(ev){ /*** https://msdn.microsoft.com/en-us/ie/ms536929(v=vs.94) You must cancel the default action for ondragenter and ondragover in order for ondrop to fire. In the case of a div, the default action is not to drop. This can be contrasted with the case of an input type=text element, where the default action is to drop. In order to allow a drag-and-drop action on a div, you must cancel the default action by specifying window.event.returnValue=false in both the ondragenter and ondragover event handlers. Only then will ondrop fire. https://stackoverflow.com/questions/2320069/jquery-ajax-file-upload Re. processData:false solves 'append called on an object that does not implement interface FormData' ***/ //ev.preventDefault(); console.log("ONDROP..."); var fd=new FormData(); //load up form data with dropped files... for (var i = 0; i < ev.dataTransfer.files.length; i++) { //console.log("name:", ev.dataTransfer.files[i].name); //console.log("filetype:", ev.dataTransfer.files[i].type); fd.append(ev.dataTransfer.files[i].name, ev.dataTransfer.files[i]); } fd.append("action","SVR-UPLOAD"); fd.append("project_id", localStorage.getItem("project_id")); fd.append("svr_id",localStorage.getItem("svr_id")); fd.append("upload_file",true); $.ajax({ data:fd, contentType:false, error:function(err){console.log("Error uploading:",err);}, processData:false, success:function(result){ console.log("Success uploading, refresh everything..."); svr.refresh(); }, type:"POST", url:"/uploads" }); }; svr.photos.formats={}; svr.photos.formats.filler=function(rx, i, row){ var keys=Object.keys(rx); var vals=Object.values(rx); var j=keys.indexOf(i)+1; var len=keys.length; var span; //console.log("VALS:", vals); while (j<len && svr.photos.cc<12){ //span=Number(vals[j].col.split("-")[2]); //col-xs-2 => 2 if(vals[j].available==true ){ if(vals[j].format=="landscape" && svr.photos.cc+5<=12) { svr.photos.formats.landscape(rx, keys[j], row); } else if (vals[j].format=="portrait"&& svr.photos.cc+3<=12){ svr.photos.formats.portrait(rx, keys[j], row);} } j+=1; } svr.photos.cc=0; //assume row filled so reset column count }; svr.photos.formats.landscape=function(rx, i, row){ row.push({ fig:true, captions:rx[i].captions, col:"col-xs-2", index:svr.photos.index, key:rx[i].key }); row.push({img:rx[i].path, col:"col-xs-4", index:svr.photos.index}); svr.photos.cc+=6; svr.photos.index+=1; rx[i].available=false; }; svr.photos.formats.portrait=function(rx, i, row){ row.push({ fig:true, captions:rx[i].captions, col:"col-xs-2", index:svr.photos.index, key:rx[i].key }) row.push({img:rx[i].path, col:"col-xs-3", index:svr.photos.index}); svr.photos.cc+=5; svr.photos.index+=1; rx[i].available=false; }; svr.photos.formats.wide=function(rx, i, row){ row.push({ fig:true, captions:rx[i].captions, col:"col-xs-2", index:svr.photos.index, key:rx[i].key }) row.push({img:rx[i].path, col:"col-xs-10", index:svr.photos.index}); svr.photos.cc+=12; svr.photos.index+=1; rx[i].available=false; }; svr.photos.layouts={}; svr.photos.layouts.azEasy=function(svrdata){ //reformat result for photos template... //svrdata.xdata = {img01:{caption:"", date:"", format:"", path:"..."}, ...} svr.photos.index=0; svr.photos.cc=0; //column counter var rx=svrdata.xdata; var rows=[], row=[]; for (var i in rx){ if(rx[i].available==true){ if(rx[i].format=="landscape") { if (svr.photos.cc > 6) {rows.push(row); row=[]; svr.photos.cc=0;} svr.photos.formats.landscape(rx, i, row); } else if (rx[i].format=="portrait") { if (svr.photos.cc > 7) {rows.push(row); row=[]; svr.photos.cc=0;} svr.photos.formats.portrait(rx, i, row); } else if (rx[i].format=="wide") { if (svr.photos.cc > 1) {rows.push(row); row=[]; svr.photos.cc=0;} svr.photos.formats.wide(rx, i, row); } }; }; rows.push(row); return rows; } svr.photos.layouts.azTight=function(svrdata){ //svrdata.xdata = {img01:{caption:"", date:"", format:"", path:"..."}, ...} svr.photos.index=0; //image counter svr.photos.cc=0; //column counter, 12 columns is 1 row in bootstrap var rx=svrdata.xdata; var rows=[],row=[]; for (var i in rx){ if(rx[i].available==true){ if(rx[i].format=="landscape") { svr.photos.formats.landscape(rx, i, row); svr.photos.formats.filler(rx, i, row); rows.push(row); row=[]; } else if (rx[i].format=="portrait") { svr.photos.formats.portrait(rx, i, row); svr.photos.formats.filler(rx, i, row); rows.push(row); row=[]; } else if (rx[i].format=="wide") { svr.photos.formats.wide(rx, i, row); svr.photos.formats.filler(rx, i, row); rows.push(row); row=[]; } }; }; return rows; } svr.photos.render=function(svrdata){ //layout photos in svrdata.xdata to photorows as required for handlebar template //svr.data.photorows=svr.layouts.azEasy(svrdata) svr.data.photorows=svr.photos.layouts.azTight(svrdata); $("#svr-photos-placeholder").html(svr.photos.template({rows:svr.data.photorows})); } svr.photos.template=Handlebars.compile($("#svr-photos-template").html()); ///////////// // titleblocks svr.titleblock_left={}; svr.titleblock_right={}; svr.titleblock_right.edit=function(el){ svr.ed.text(el, function(){ var field=$(el).attr("field"); //eg. 'date' var text=svr.ed.val(); svr.data[field]=text; //svr.data[field][index]=; //eg. ['first comment','revised comment 2'...] console.log("FIELD",field, " UPDATED TEXT:", text); svr.change(field, svr.data[field], function(){ svr.ed.hide(); //refresh (server request and render) or just render cache for now... //svr.titleblock.render(svr.data); $("#svr-titleblock-right-placeholder").html(svr.titleblock_right.template({svr:svr.data})); }); }); }; svr.titleblock_left.refresh=function(){ //console.log("PROJECT SELECT"); //var svr_id=localStorage.getItem("svr_id"); $.ajax({ contentType: "application/x-www-form-urlencoded; charset=UTF-8", data: $.param({ action:"PROJECT-SELECT", //select info for this project only... project_id:localStorage.getItem("project_id") }), error: function(err){ console.log("Error", err);}, success: function(result){ $("#svr-titleblock-left-placeholder").html(svr.titleblock_left.template(result)); }, type:"POST", url:"/uploads" }); }; svr.titleblock_left.template=Handlebars.compile($("#svr-titleblock-left").html()); svr.titleblock_right.template=Handlebars.compile($("#svr-titleblock-right").html()); svr.header={}; svr.header.template=Handlebars.compile($("#svr-header").html()); svr.header.edit=function(el){ svr.ed.text(el, function(){ var field=$(el).attr("field"); //eg. 'title' var text=svr.ed.val(); svr.data[field]=text; //svr.data[field][index]=; //eg. ['first comment','revised comment 2'...] console.log("FIELD",field, " UPDATED TEXT:", text); svr.change(field, svr.data[field], function(){ svr.ed.hide(); //refresh (server request and render) or just render cache for now... //svr.titleblock.render(svr.data); $("#svr-header-placeholder").html(svr.header.template({svr:svr.data})); }); }); }; /** /////////////////////////////////////// // Render page header and titleblock $.get("client/header.html", function(htm){ //console.log('tbh loaded:', htm); if (typeof svr.header=="undefined"){svr.header={};} svr.header.template=Handlebars.compile(htm); $("#svr-header").html(svr.header.template({doc_type:"Site Visit Review"})); }); */ /////////////////////////////////////// // Render page svr.titleblock_left.refresh(); //renders titleblock_left IE project info svr.refresh(); //renders header, notes & titleblock_right IE report info </script> <!-- PAGE STARTS HERE --> <div class="container"> <div id="svr-header-placeholder">placeholder</div> <div class="row"> <div id="svr-titleblock-left-placeholder" class="col-xs-6">Project part of titleblock</div> <div id="svr-titleblock-right-placeholder" class="col-xs-6">Report part of titleblock</div> </div> <p class="row row__" >This report is a general review of progress and construction activities on site. Architectural Work was visually reviewed on a random basis for general conformity with Architectural Contract Documents prepared by this firm. Refer also to Mechanical and Electrical field reports issued separately. </p> <br><br> <div id="svr-notes-placeholder" class="marz">notes-placeholder</div> <div id="svr-photos-placeholder" class="marz">photos-placeholder</div> <br><br> </div> <!-- TEMPLATES --> <template id='svr-header' type="text/x-handlebars-template"> {{#with svr}} <div id="backup-logo"></div> <img style="width:100px; float:left;" src="uploads/logo.png" alt="" onerror="$('#backup-logo').load('client/casbah_logo.html');"> <h2 style="float:right;" field="title" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.header.edit(this)">{{title}}</h2> <div style="clear:both"></div> {{/with}} </template> <template id='svr-titleblock-left' type="text/x-handlebars-template"> {{#each projects}} <div class="row row__"> <strong class="col-xs-4">Project Id:</strong> <p class="col-xs-8">{{project_id}}</p> </div> <div class="row row"> <strong class="col-xs-4">Project Name:</strong> <p class="col-xs-8">{{project_name}}</p> </div> <div class="row row"> <strong class="col-xs-4">Project Address:</strong> <p class="col-xs-8">{{address}}</p> </div> <div class="row row"> <strong class="col-xs-4">Contractor:</strong> <p class="col-xs-8">{{contractor}}</p> </div> <div class="row row"> <strong class="col-xs-4">Permit No:</strong> <p id="svr-permit" class="col-xs-8">{{permit}}</p> </div> {{/each}} </template> <template id='svr-titleblock-right' type="text/x-handlebars-template"> {{#with svr}} <div class="row row__"> <strong class="col-xs-4">Report Id:</strong> <p class="col-xs-8">{{svr_id}}</p> </div> <div class="row row"> <strong id="svr-date" class="col-xs-4">Date of Visit:</strong> <p class="col-xs-8" title="click to edit, then double-click to save..." field="date" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.titleblock_right.edit(this)">{{date}}</p> </div> <div class="row row"> <strong class="col-xs-4">Date Issued:</strong> <p class="col-xs-8" title="click to edit, then double-click to save..." field="date_issued" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.titleblock_right.edit(this)">{{date_issued}}</p> </div> <div class="row row"> <strong class="col-xs-4">Reviewer:</strong> <p class="col-xs-8" title="click to edit, then double-click to save..." field="author" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.titleblock_right.edit(this)">{{author}}</p> </div> <div class="row row"> <strong class="col-xs-4" title="click to edit, then double-click to save..." field="misc_key" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.titleblock_right.edit(this)">{{#if misc_key}}{{misc_key}}{{else}}--{{/if}}</strong> <p class="col-xs-8" title="click to edit, then double-click to save..." field="misc_valu" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.titleblock_right.edit(this)">{{#if misc_valu}}{{misc_valu}}{{else}}--{{/if}}</p> </div> {{/with}} </template> <template id='svr-notes-template' type="text/x-handlebars-template"> {{#each rows as |row rowindex|}} {{#if row.section_heading}} <div class="row marz" section_name="{{row.section_name}}" section_num="{{row.section_num}}" section_index="{{row.section_index}}" ondrop="svr.notes.ondrop(this, event)"> <h3 class="col-xs-1 marz" >{{row.section_num}}</h3> <h3 class="col-xs-10 border-left marz" section_name="{{row.section_name}}" section_index="{{row.section_index}}" oncontextmenu="casbah.showMenu(svr.notes.menu, event)">{{row.txt}}</h3> <h3 class="col-xs-1 marz" ></h3> </div> {{/if}} {{#if row.section_item}} <div class="row marz" section_name="{{row.section_name}}" section_num="{{row.section_num}}" section_index="{{row.section_index}}" draggable="true" ondragstart="svr.notes.ondragstart(this, event)" ondragover="event.preventDefault()" ondrop="svr.notes.ondrop(this, event)"> <p class="col-xs-1 marz">{{row.section_num}}.{{plusOne row.section_index}}</p> <p class="col-xs-10 border-left marz" title="click to edit, then double-click to save" section_name="{{row.section_name}}" section_num="{{row.section_num}}" section_index="{{row.section_index}}" onmouseenter="$(this).addClass('highlite')" onmouseleave="$(this).removeClass('highlite')" onclick="svr.notes.edit(this)" oncontextmenu="casbah.showMenu(svr.notes.menu, event)">{{row.txt}}</p> <p class="col-xs-1 marz"></p> </div> {{/if}} {{/each}} </template> <template id='svr-photos-template' type="text/x-handlebars-template"> <div class="row marz" title="Drop photos here to add to report..." ondragenter="event.preventDefault();$(this).addClass('highlite');" ondragover="event.preventDefault();" ondragleave="event.preventDefault();$(this).removeClass('highlite');" ondrop="svr.photos.ondrop(event);" oncontextmenuNOT="casbah.showMenu(svr.photo.menu, event)"> <h3 class="col-xs-1 marz" >5.0</h3> <h3 class="col-xs-10 border-left marz">Photos</h3> <h3 class="col-xs-1 marz" >&nbsp;</h3> </div> {{#each rows as |row ri|}} <div class="row marz"> {{#each row as |item ii|}} {{#if item.img}} <div class="{{item.col}} photo-frame border" onclick="svr.photos.onclick(this)"> <img class="photo-image" src="{{item.img}}"> </div> {{/if}} {{#if item.fig}} <div class="{{item.col}} photo-text border" title="{{item.key}}"> <figure style="display:inline;">5.{{plusOne item.index}}</figure> <caption>{{array item.captions}}</caption> <p>{{item.dateTaken}}</p> </div> {{/if}} {{/each}} </div> <div class="control-joint"></div> {{/each}} </template> <!-- MENUS --> <div id="svr-notes-menu" onmouseleave="svr.notes.menu.hide()" class="hide9999"> <p onclick="svr.notes.remove()">Delete</p> <p onclick="svr.notes.edit(svr.notes.menu.menu('option', 'caller'))">Edit</p> <p onclick="svr.notes.insert()">Insert</p> <p onclick="svr.notes.menu.hide()">Exit</p> <p onclick="svr.refresh();svr.notes.menu.hide()">Refresh</p> </div>