· 8 years ago · Dec 18, 2017, 04:02 PM
1<?php
2
3 session_start();
4
5 /* MB = Megabyte */
6 define( 'MB', pow( 1024,2 ) );
7
8 /* Configure phone lines and session variable name */
9 $maxlines=30;
10 $svar='calls';
11
12 /* Photo upload directory */
13 $dir = 'c:/temp/fileuploads/1/';
14
15 /* Thumbnail handler */
16 $thumb = '/assets/thumb';
17
18 /* actual filename that is generated will be prefixed by date by _xml() class*/
19 $xmlfile='callers.xml';
20
21 /* these are passed to _xml() class */
22 $xmlfields=array('line','name','town','topic','image','filename','date');
23
24 /* Create the session */
25 if( !isset( $_SESSION[ $svar ] ) )$_SESSION[ $svar ]=array();
26
27
28
29 /*
30 delete the session and reload the page ( ?del=1 )
31 optionally delete the xml file ( &delxml=1 )
32 */
33 if( $_SERVER['REQUEST_METHOD']=='GET' && !empty( $_GET ) ){
34 if( !empty( $_GET['del'] ) ){
35 unset( $_SESSION[ $svar ] );
36 if( !empty( $_GET['delxml'] ) ){
37 /* use class delete method due to file name prefixing */
38 $obj=new _xml( false, $xmlfile, array() );
39 $obj->deletexml();
40 $obj=null;
41 }
42 header('Location: ?del=0');
43 }
44 }
45
46
47
48
49
50
51 class _xml{
52 private $svar;
53 private $xmlfile;
54 private $xmlfields;
55
56 public function __construct( $svar=false, $file=false, $fields=array() ){
57 $this->svar=$svar;
58 $this->xmlfile=__DIR__ . DIRECTORY_SEPARATOR . date('Y_m_d__') . $file;
59 $this->xmlfields=$fields;
60 }
61 public function deletexml(){
62 return realpath( $this->xmlfile ) ? unlink( $this->xmlfile ) : false;
63 }
64 public function writexml(){
65 $dom=new DOMDocument;
66 $dom->appendChild( $dom->createComment( 'Call Management System Queue' ) );
67 $root=$dom->createElement('callers');
68 $dom->appendChild( $root );
69 $root->appendChild( $dom->createElement('date', date( DATE_COOKIE ) ) );
70 $pttn='@[^a-zA-Z_\-0-9\s]@';
71 foreach( $_SESSION[ $this->svar ] as $i => $arr ){
72 $caller=$dom->createElement( 'caller' );
73 $root->appendChild( $caller );
74 $obj=(object)$arr;
75 foreach( $this->xmlfields as $key ){
76 if( property_exists( $obj, $key ) ){
77 if( preg_match( $pttn, $obj->$key ) ){
78 $cdata=$dom->createCDATASection( $obj->$key );
79 $node=$dom->createElement( $key );
80 $node->appendChild( $cdata );
81 } else { $node=$dom->createElement( $key,$obj->$key ); }
82 $caller->appendChild( $node );
83 }
84 }
85 }
86 $dom->appendChild( $dom->createComment( sprintf('%d callers in Queue', $i ) ) );
87 $xml=$dom->saveXML();
88 $dom->save( $this->xmlfile );
89 return $xml;
90 }
91 public function updatexml( $json=false ){
92 if( $json ){
93 $obj=json_decode( $json );
94 $keys=array_keys( get_object_vars( $obj ) );
95 $dom=new DOMDocument;
96 $dom->load( $this->xmlfile );
97 $xp=new DOMXPath( $dom );
98 $query='//callers/caller[./line="'.$obj->line.'"]';
99 $col=$xp->query( $query );
100 if( !empty( $col ) && $col->length > 0 ){
101 $node=$col->item(0);
102 foreach( $keys as $field ){
103 try{
104 $child=$xp->query( $field, $node );
105 if( !empty( $child ) && $child->length==1 ){
106 if( $child->item(0)->nodeValue != $obj->$field ){
107 $child->item(0)->nodeValue = $obj->$field;
108 }
109 }
110 }catch( Exception $e ){
111 continue;
112 }
113 }
114 $dom->save( $this->xmlfile );
115 }
116 }
117 }
118 public function editxml( $line=false ){
119 if( $line && array_key_exists( $line, $_SESSION[ $this->svar ] ) ){
120 $dom=new DOMDocument;
121 $dom->load( $this->xmlfile );
122 $xp=new DOMXPath( $dom );
123 $query='//callers/caller[./line="'.$line.'"]';
124 $col=$xp->query( $query );
125 if( !empty( $col ) && $col->length > 0 ){
126 foreach( $col as $node ){
127 $img=$xp->query('image',$node);
128 if( $img->length > 0 ){
129 list( $thumb, $path ) = explode( 'file=', $img->item(0)->nodeValue );
130 if( realpath( $path ) )@unlink( $path );
131 }
132 $node->parentNode->removeChild( $node );
133 }
134 $count = $dom->getElementsByTagName('caller')->length;
135 $dom->appendChild( $dom->createComment( sprintf('%d callers in Queue', $count-- ) ) );
136 $dom->save( $this->xmlfile );
137 return true;
138 }
139 }
140 }
141 public function readxml(){
142 $data=array();
143 $fields=array();
144 $dom=new DOMDocument;
145 $dom->load( $this->xmlfile );
146 $col=$dom->getElementsByTagName('caller');
147 if( !empty( $col ) && $col->length > 0 ){
148 foreach( $col as $line => $caller ){
149 $key=$line + 1;
150 $childnodes=$caller->childNodes;
151 $data[ $key ]=array();
152 $data[ $key ]['time']=date('H:i:s');
153 foreach( $childnodes as $child ){
154 if( in_array( $child->tagName, $this->xmlfields ) ){
155 $data[ $key ][ $child->tagName ]=$child->nodeValue;
156 }
157 }
158 }
159 }
160 $dom->appendChild( $dom->createComment( sprintf('Queue imported: %s', date( DATE_COOKIE ) ) ) );
161 $dom->save( $this->xmlfile );
162 $_SESSION[ $this->svar ]=$data;
163 $json=json_encode( $_SESSION[ $this->svar ] );
164 return $json;
165 }
166 /* magic methods */
167 public function __get( $name ){
168 return $this->$name;
169 }
170 public function __set( $name,$value ){
171 $this->$name=$value;
172 }
173 }//end class
174
175
176
177
178
179
180
181 if( $_SERVER['REQUEST_METHOD']=='POST' && !empty( $_POST ) ){
182 ob_clean();
183
184 try{
185 $cmd=filter_input( INPUT_POST, 'cmd', FILTER_SANITIZE_STRING );
186 $line=filter_input( INPUT_POST, 'line', FILTER_SANITIZE_NUMBER_INT );
187 $image=$name=false;
188
189 if( !empty( $_FILES ) ){
190 $permitted=array( 'jpg','jpeg','png' );
191 $maxfilesize=MB * 1;
192
193 function uploaderror( $code ){
194 switch( $code ) {
195 case UPLOAD_ERR_INI_SIZE: return "The uploaded file exceeds the upload_max_filesize directive in php.ini";
196 case UPLOAD_ERR_FORM_SIZE: return "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form";
197 case UPLOAD_ERR_PARTIAL: return "The uploaded file was only partially uploaded";
198 case UPLOAD_ERR_NO_FILE: return "No file was uploaded";
199 case UPLOAD_ERR_NO_TMP_DIR: return "Missing a temporary folder";
200 case UPLOAD_ERR_CANT_WRITE: return "Failed to write file to disk";
201 case UPLOAD_ERR_EXTENSION: return "File upload stopped by extension";
202 default: return "Unknown upload error";
203 }
204 }
205 $obj=(object)$_FILES['photo'];
206 $name=$obj->name;
207 $tmp=$obj->tmp_name;
208 $size=$obj->size;
209 $error=$obj->error;
210 $type=$obj->type;
211
212 if( !empty( $name ) ){
213 if( $error == UPLOAD_ERR_OK && is_uploaded_file( $tmp ) ){
214 $targetfile = $dir . $name;
215
216 $ext=strtolower( pathinfo( $name, PATHINFO_EXTENSION ) );
217 $imgsize=getimagesize( $tmp );
218
219 if( !in_array( $ext, $permitted ) ) throw new Exception('Disallowed filetype');
220 if( !$imgsize )throw new Exception('Not an image');
221 if( $size > $maxfilesize )throw new Exception('File is too large');
222
223 $status = empty( $errors ) ? move_uploaded_file( $tmp, $targetfile ) : false;
224
225 if( $status ) $image=$thumb . '?fixed=1&file='.$targetfile;
226
227 /* do not exit, continue to next block of code */
228
229 } else {
230 throw new Exception( sprintf( 'Upload error: %s', uploaderror( $error ) ) );
231 }
232 }
233 }
234
235
236
237
238
239
240
241
242
243
244 if( $cmd ){
245 switch( $cmd ){
246
247 /* regular updates sent to ajax callback */
248 case 'poll':
249 $json=json_encode( $_SESSION[ $svar ] );
250 break;
251
252 /* add new caller to the session */
253 case 'add-caller':
254 if( is_nan( $line ) or empty( $line ) ) throw new Exception('Invalid line - this must be an integer');
255 $_POST['time']=date('H:i:s');
256 $_POST['date']=date('Y-m-d H:i:s');
257
258 if( $image ) $_POST['image']=$image;
259 if( $name ) $_POST['filename']=$name;
260
261 $_SESSION[ $svar ][ $line ]=$_POST ;
262 $json=json_encode( $_SESSION[ $svar ] );
263 break;
264
265 /* delete the caller from the session & from the xml if it exists */
266 case 'delete':
267 if( $line && array_key_exists( $line, $_SESSION[ $svar ] ) ) {
268
269 $obj=new _xml( $svar, $xmlfile, $xmlfields );
270 $obj->editxml( $line );
271 $obj=null;
272
273 unset( $_SESSION[ $svar ][ $line ] );
274 }
275 $json=json_encode( $_SESSION[ $svar ] );
276 break;
277
278 /* export session/callers to xml file */
279 case 'xml':
280 if( !empty( $_SESSION[ $svar ] ) ){
281
282 $obj=new _xml( $svar, $xmlfile, $xmlfields );
283 $xml=$obj->writexml();
284 $obj=null;
285
286 header('Content-Type: application/xml');
287 header('HTTP/1.1 200 OK', true, 200 );
288 exit( $xml );
289 } else {
290 throw new Exception('Session variable is empty - nothing to Export!');
291 }
292 break;
293
294 /* re-import callers & regenerate session */
295 case 'import':
296 $obj=new _xml( $svar, $xmlfile, $xmlfields );
297 $file=$obj->xmlfile;
298
299 if( realpath( $file ) ){
300 $json=$obj->readxml();
301 $obj=null;
302 } else {
303 throw new Exception( sprintf( 'File %s does not exist', $file ) );
304 }
305 break;
306
307 /* delete session and queue file */
308 case 'delqueue':
309 if( isset( $_SESSION[ $svar ] ) )unset( $_SESSION[ $svar ] );
310 $result_session=!empty( $_SESSION[ $svar ] );
311 $result_xmlfile = @unlink( $xmlfile );
312
313 throw new Exception( sprintf( 'Session deleted: %b Queue deleted: %b', $result_session, $result_xmlfile ) );
314 break;
315
316 /* edit basic caller info ( name,town,topic )*/
317 case 'update':
318 $json=json_encode( $_POST );
319 if( $line && array_key_exists( $line, $_SESSION[ $svar ] ) ) {
320 $obj=new _xml( $svar, $xmlfile, $xmlfields );
321 $obj->updatexml( $json );
322 $obj=null;
323
324 $current=$_SESSION[ $svar ][ $line ];
325 $merged=array_merge( $current, $_POST );
326 $_SESSION[ $svar ][ $line ]=$merged;
327 }
328 break;
329
330 /* generate html form to allow editing of caller */
331 case 'edit':
332 if( $line && array_key_exists( $line, $_SESSION[ $svar ] ) ) {
333
334 $obj=(object)$_SESSION[ $svar ][ $line ];
335
336 $html=array();
337 $html[]="<form name='caller-edit' method='post'>";
338 $html[]="<label for='edit_name'>Name:<input type='text' name='name' value='{$obj->name}' /></label>";
339 $html[]="<label for='edit_town'>Town:<input type='text' name='town' value='{$obj->town}' /></label>";
340 $html[]="<label for='edit_topic'>Topic:<input type='text' name='topic' value='{$obj->topic}' /></label>";
341 $html[]="<input type='hidden' name='line' value='$line' />";
342 $html[]="<input type='button' value='Edit' onclick='_edit(event);' />";
343 $html[]="<input type='button' value='Cancel' onclick='_cancel(event);' />";
344 $html[]="</form>";
345
346
347 header('Content-Type: text/html');
348 header('HTTP/1.1 200 OK',true,200);
349 exit( implode( PHP_EOL, $html ) );
350 } else {
351 throw new Exception('Unable to find called');
352 }
353 break;
354
355 /* error */
356 default:
357 throw new Exception('Unknown command');
358 break;
359 }
360 }
361
362 header('Content-Type: application/json');
363 header('HTTP/1.1 200 OK',true,200);
364 exit( $json );
365
366 } catch( Exception $e ){
367 header('Content-Type: text/plain');
368 header('HTTP/1.1 404 Not Found',true,404);
369 exit( $e->getMessage() );
370 }
371 }
372?>
373<!doctype html>
374<html>
375 <head>
376 <meta charset='utf-8' />
377 <title>Call Manager</title>
378 <style>
379 html,
380 html *{font-family:calibri,verdana,arial;font-size:1rem;box-sizing:border-box;}
381
382 #container{width:95%;float:none;margin:0 auto;height:80vh;z-index:1;}
383 #lhs{width:calc(20% - 0.5rem );float:left;height:80vh;}
384 #rhs{width:calc(80% - 0.5rem );float:right;height:80vh;}
385 #lhs,#rhs{display:block; clear:none; margin:0 0.25rem}
386
387 form,
388 table{width:100%;}
389 input[type='button']{float:none;display:inline-block;margin:1rem auto;}
390
391 input[type='text'],
392 select{width:100%;padding:0.5rem;box-sizing:border-box;}
393
394 input[type='file']{
395 width: 0.1px;
396 height: 0.1px;
397 opacity: 0;
398 overflow: hidden;
399 position: absolute;
400 z-index: -1;
401 }
402 input[type='file'] + label{
403 font-weight: 700;
404 color: black;
405 background-color: #E5E4E2;
406 display: inline-block;
407 border:1px solid black;
408 padding:0.25rem;
409 width:90%;
410 cursor:pointer;
411 float:right;
412 }
413 .input[type='file']:focus + label,
414 .input[type='file'] + label:hover {
415 background-color: red;
416 }
417
418
419 td{text-align:center;}
420 tr td:not([colspan]):first-of-type{text-align:left;}
421
422
423 h1{font-size:2rem;text-align:center;}
424 h2{font-size:1.25rem;text-align:center;}
425 pre{clear:both;}
426 ul,li{ display:block; width:100%;float:left;}
427 ul{list-style: none inside none;}
428 li:nth-of-type(even){background:whitesmoke;}
429 option[disabled]{background:rgba(255,0,0,0.25)}
430 li{padding:0.25rem 0.5rem;}
431 li div,li div span{font-size:0.85rem!important;}
432
433 li div span{font-weight:bold;margin:0 0.5rem 0 1rem;}
434 li div span[data-id]{color:green;font-weight:normal;}
435 li div input{ color:red;float:right!important;clear:none;margin:0 0.1rem!important; }
436 #msgs{height:2rem;}
437 [colspan="2"] input[type="button"]{width:100%!important;padding:0.5rem;}
438 [name="add"]{margin:2rem auto!important;}
439 [name="xml"],[name="imp"],[name="del"]{margin:0.1rem auto!important;}
440
441 div.preview,
442 div.edit{
443 z-index:100!important;
444 position:absolute;
445 top:0;
446 left:0;
447 height:100vh!important;
448 width:100%!important;
449 background:rgba(0,0,0,0.95);
450 overflow:hidden;
451 display:flex;
452 }
453 div.preview > img { margin:auto; border:2px solid gray}
454 div.edit > form{
455 width:80%;
456 height:80%;
457 margin:auto;
458 border:2px solid gray;
459 background:white;
460 padding:1rem;
461 }
462 div.edit > form > label{ width:80%; margin:1rem auto; float:none; }
463 div.edit > form[type='button']{padding:1rem}
464 </style>
465 <script>
466
467 var _int;
468 var _poll=2.5;
469
470 function _edit( event ){
471 /* inline event handler assigned to button in form */
472 var form=event.target.parentNode;
473 var fd=new FormData( form );
474 fd.append('cmd','update');
475 fd.append('line',form.querySelector('[name="line"]').value);
476 fd.append('name',form.querySelector('[name="name"]').value);
477 fd.append('town',form.querySelector('[name="town"]').value);
478 fd.append('topic',form.querySelector('[name="topic"]').value);
479
480
481 ajax.call( this, 'post', location.href, fd, function(r){
482 location.reload();
483 },{ formdata:true, node:form } );
484 };
485 function _cancel(event){document.body.removeChild(event.target.parentNode.parentNode)};
486
487 function ajax(m,u,p,c,o){
488 /*
489 Utility function for basic Ajax requests
490 m = method ~ GET or POST only
491 u = url ~ the script or resource to which the request will be sent
492 p = parameters ~ an object literal of parameters to send
493 c = callback ~ asynchronous callback function that processes the response
494 o = options ~ object literal o foptions which are also passed to callback
495
496 */
497 var xhr=new XMLHttpRequest();
498 xhr.onreadystatechange=function(){
499 /*
500 the callback is passed 4 arguments
501 r = response
502 o = options ( as passed to the ajax function )
503 h = headers
504 x = xhr object to allow access to it's properties
505 */
506 if( xhr.readyState==4 )c.call( this, xhr.response, o, xhr.getAllResponseHeaders(), xhr );
507 };
508 if( o.hasOwnProperty('formdata') && o.formdata===true && m.toLowerCase()=='post' ){
509 /* send formdata object "as-is" ~ set p = FormData */
510 } else {
511 var params=[];
512 for( var n in p )params.push( n+'='+p[ n ] );
513 switch( m.toLowerCase() ){
514 case 'post': p=params.join('&'); break;
515 case 'get': u+='?'+params.join('&'); p=null; break;
516 }
517 }
518 xhr.open( m.toUpperCase(), u, true );
519 if( !o.hasOwnProperty('formdata') ) xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
520 if( o && Object.keys( o ).length > 0 && o.hasOwnProperty('headers') ){
521 for( var h in o.headers )xhr.setRequestHeader( h, o.headers[ h ] );
522 }
523 xhr.send( p );
524 }
525
526 function createNode( t, a, p ) {
527 try{
528 /*
529 utility function to simplify creation of new DOM nodes
530 t = type ~ node type or tag name of node
531 a = attributes ~ object literal of attributes to add to node
532 p = parent ~ the DOM node to which the new node will be added
533 */
534 var el = ( typeof( t )=='undefined' || t==null ) ? document.createElement( 'div' ) : document.createElement( t );
535 for( var x in a ) if( a.hasOwnProperty( x ) && x!=='innerHTML' ) el.setAttribute( x, a[ x ] );
536 if( a.hasOwnProperty('innerHTML') ) el.innerHTML=a.innerHTML;
537 if( p!=null ) typeof( p )=='object' ? p.appendChild( el ) : document.getElementById( p ).appendChild( el );
538 return el;
539 }catch(err){
540 console.warn('createNode: %s, %o, %o',t,a,p);
541 }
542 }
543
544 function getArgs() {
545 var a = {};
546 var p = location.search.substring(1).split("&");
547 for(var i=0; i < p.length;i++) {
548 var x = p[i].indexOf('=');
549 if(x == -1) continue;
550 a[p[i].substring(0,x)] = unescape(p[i].substring(x+1));
551 }
552 return a;
553 }
554
555 function pad(i) {
556 return ( parseInt(i)<10 ) ? '0' + parseInt(i) : parseInt(i);
557 }
558
559 function timediff( timestamp ) {
560 var s = parseInt( timestamp, 10 );
561 var days = Math.floor( s / ( 3600 * 24 ) );
562 var hours = Math.floor( s / 3600 );
563 var minutes = Math.floor( ( s - ( hours * 3600 ) ) / 60 );
564 var seconds = s - ( hours * 3600 ) - ( minutes * 60 );
565 var output=[];
566 if( days > 0 )output.push( pad( days )+'days' );
567 if( hours > 0 )output.push( pad( hours )+'hrs' );
568 if( minutes > 0 )output.push( pad( minutes )+'mins' );
569 if( seconds > 0 )output.push( pad( seconds )+'secs' );
570 return output.join(' ');
571 }
572
573
574 function bindEvents( event ){
575 /* get references to dom nodes */
576 var form=document.getElementById('caller-info');
577 var select=form.querySelector('select');
578 var rhs=document.getElementById('rhs').querySelector('ul');
579 var msgs=document.getElementById('msgs');
580 var args=getArgs();
581
582 /* utility functions */
583 var _clearform=function(){
584 Array.prototype.slice.call( form.querySelectorAll('input[type="text"], input[type="file"]') ).forEach(function(n){n.value=''});
585 };
586 var _disable=function(i){
587 if( select.querySelector('option[data-id="'+i+'"]') ) select.querySelector('option[data-id="'+i+'"]').disabled=true;
588 };
589 var _enable=function(i){
590 if( select.querySelector('option[data-id="'+i+'"]') ) select.querySelector('option[data-id="'+i+'"]').removeAttribute('disabled');
591 };
592 var _deletecaller=function(e){
593 _enable.call( this, this.dataset.id );
594 _setcaller.call( this, this.dataset.id, 'Line '+this.dataset.id );
595 this.parentNode.parentNode.parentNode.removeChild( this.parentNode.parentNode );
596 };
597 var _setcaller=function(i,name){
598 if( select.querySelector('option[data-id="'+i+'"]') )select.querySelector('option[data-id="'+i+'"]').innerHTML=name;
599 };
600 var _getline=function(){
601 return select.options[ select.options.selectedIndex ].value;
602 };
603 var _message=function(msg,i){
604 msgs.innerHTML=msg;
605 setTimeout(function(){ msgs.innerHTML=''; },1000 * i );
606 };
607 var xmlcallback=function(r,o,h){
608 if( o.poll===false )clearInterval( _int );
609 if( o.clear === true )o.node.innerHTML='';
610 _message.call( this, 'Queue exported', _poll / 2 );
611 };
612 var viewimage=function(){
613 var div=createNode(null,{'class':'preview'},document.body);
614 div.onclick=function(){
615 this.parentNode.removeChild( this );
616 }.bind( div );
617
618 var img=new Image();
619 img.src=this.src +'&fullsize=true';
620
621 div.appendChild( img );
622 };
623
624 /* ajax config */
625 var method='post';
626 var url=location.href;
627 var options={ formdata:true, node:rhs, clear:true, poll:false };
628 var callback=function(r,o,h,x){
629
630 if( parseInt( x.status )!=200 || x.getResponseHeader('Content-Type')!='application/json' ){
631 alert( r );
632 return;
633 }
634
635 /* clear text fields in the form */
636 if( o.clear===true ) _clearform.call( this );
637
638 var data=JSON.parse( r );
639 for( var n in data ){
640 if( typeof( data[ n ] )!='object' ) return;
641
642 var json=data[n];
643 var id='caller_'+json.line;
644
645
646
647 _enable.call( this, json.line );
648 _disable.call( this, json.line );
649 _setcaller.call( this, json.line, 'Line '+json.line+' - '+json.name );
650
651
652
653 if( !document.getElementById( id ) ){
654 var content=[
655 '<span>Line:</span>'+json.line,
656 '<span>Name:</span>'+json.name,
657 '<span>Town:</span>'+json.town,
658 '<span>Topic:</span>'+json.topic,
659 '<span>Waiting since:</span>'+( new Date( json.date ).toLocaleTimeString() ),
660 '<span data-id="'+json.line+'"></span>'
661 ];
662
663 var li=createNode( 'li', { id:id }, o.node );
664 if( json.hasOwnProperty('image') && json.hasOwnProperty('filename') ){
665 var img=new Image();
666 img.src=json.image;
667 img.title=json.filename;
668 img.onclick=viewimage.bind( img );
669 li.appendChild( img );
670 }
671
672 var div=createNode( null,{ 'data-id':json.line, innerHTML:content.join(' ') },li);
673 var bttn=createNode( 'input', { type:'button',value:'Delete','data-id':json.line }, div );
674 bttn.onclick=function( event ){
675 ajax.call( this, method, url, { cmd:'delete', line:this.dataset.id }, _deletecaller.bind( this ), { node:rhs } );
676 }.bind( bttn );
677
678 bttn=createNode( 'input', { type:'button', value:'Edit', 'data-id':json.line }, div );
679 bttn.onclick=function( event ){
680 ajax.call( this, method, url, { cmd:'edit', line:this.dataset.id }, _editcaller.bind( this ), { node:rhs } );
681 }.bind( bttn );
682
683 } else {
684
685 var _now=new Date().getTime();
686 var _then=new Date( json.date ).getTime();
687 var _diff=( ( _now - _then ) / 1000 );
688
689 var _span=document.getElementById( id ).querySelector('span[data-id="'+json.line+'"]');
690 _span.innerHTML=timediff( _diff );
691 }
692 }
693
694 if( rhs.childNodes.length == 0 && Object.keys( data ).length==0 ){
695 clearInterval( _int );
696 _int=Number.NaN;
697 _message.call( this, 'stop polling...', _poll / 2 );
698 }
699 if( o.poll===true ){
700 _beginpolling.call( this );
701 }
702 };
703
704
705 var _beginpolling=function(){
706 if( isNaN( _int ) ){
707 _message.call( this, 'start polling...', _poll / 2 );
708 _int=setInterval(function(){
709 ajax.call( this, method, url, { cmd:'poll' }, callback, { node:rhs, clear:false } );
710 }, 1000 * _poll );
711 }
712 };
713
714
715
716 /* Add caller */
717 var evtSubmitCaller=function( event ){
718 if( typeof _getline()=='undefined' )return;
719 var fd=new FormData( form );
720 fd.append('cmd','add-caller');
721
722 ajax.call( this, method, url, fd, callback, options );
723 _beginpolling.call( this );
724 };
725
726 /* Export callers list to XML */
727 var evtExportCallers=function( event ){
728 ajax.call( this, method, url, { cmd:'xml' }, xmlcallback, {} );
729 }
730
731 /* Import previous XML file */
732 var evtImportCallers=function( event ){
733 ajax.call( this, method, url, { cmd:'import' }, callback, { clear:false, node:rhs, poll:true } );
734 }
735
736 /* Delete queue */
737 var evtDeleteCallers=function(){
738 ajax.call( this, method, url, { cmd:'delqueue' }, xmlcallback, { clear:true, node:rhs, poll:false } );
739 }
740 var _editcaller=function(r,o,h,x){
741 if( r && x.getResponseHeader('Content-Type')=='text/html; charset=UTF-8' ){
742 var div=createNode(null,{'class':'edit',innerHTML:r},document.body);
743 }
744 }
745
746 form.querySelector('input[type="button"][name="add"]').onclick=evtSubmitCaller;
747 form.querySelector('input[type="button"][name="xml"]').onclick=evtExportCallers;
748 form.querySelector('input[type="button"][name="imp"]').onclick=evtImportCallers;
749 form.querySelector('input[type="button"][name="del"]').onclick=evtDeleteCallers;
750
751
752
753 /* Poll every n seconds */
754 _beginpolling.call( this );
755
756 }
757 document.addEventListener( 'DOMContentLoaded', bindEvents, false );
758 </script>
759 </head>
760 <body>
761 <h1>Call Manager</h1>
762 <div id='container'>
763 <div id='lhs' data-id='callerInfo'>
764 <form id='caller-info' method='post'>
765 <h2>Add Caller</h2>
766 <table>
767 <thead>
768 <tr>
769 <td colspan=2 id='msgs'></td>
770 </tr>
771 </thead>
772 <tbody>
773 <tr>
774 <td>Line</td>
775 <td>
776 <select name='line'>
777 <?php
778 for( $i=1; $i <= $maxlines; $i++ ){
779 $caller = !empty( $_SESSION[ $svar ] ) && array_key_exists( $i, $_SESSION[ $svar ] ) ? 'Line '.$i.' - '.$_SESSION[ $svar ][ $i ]['name'] : "Line $i";
780 $disabled = !empty( $_SESSION[ $svar ] ) && array_key_exists( $i, $_SESSION[ $svar ] ) ? 'disabled=true' : '';
781 echo "<option data-id='$i' value='$i' $disabled>$caller" . PHP_EOL;
782 }
783 ?>
784 </select>
785 </td>
786 </tr>
787 <tr>
788 <td>Name</td>
789 <td>
790 <input type='text' name='name' />
791 </td>
792 </tr>
793 <tr>
794 <td>Town</td>
795 <td>
796 <input type='text' name='town' />
797 </td>
798 </tr>
799 <tr>
800 <td>Topic</td>
801 <td>
802 <input type='text' name='topic' />
803 </td>
804 </tr>
805 <tr>
806 <td colspan=2> </td>
807 </tr>
808 <tr>
809 <td>Photo</td>
810 <td>
811 <input type='file' name='photo' id='photo' />
812 <label for='photo' title='Optional: Upload a photo for the caller'>
813 <svg xmlns='http://www.w3.org/2000/svg' width='20' height='17' viewBox='0 0 20 17'>
814 <path d='M10 0l-5.2 4.9h3.3v5.1h3.8v-5.1h3.3l-5.2-4.9zm9.3 11.5l-3.2-2.1h-2l3.4 2.6h-3.5c-.1 0-.2.1-.2.1l-.8 2.3h-6l-.8-2.2c-.1-.1-.1-.2-.2-.2h-3.6l3.4-2.6h-2l-3.2 2.1c-.4.3-.7 1-.6 1.5l.6 3.1c.1.5.7.9 1.2.9h16.3c.6 0 1.1-.4 1.3-.9l.6-3.1c.1-.5-.2-1.2-.7-1.5z'></path>
815 </svg> <span>Choose a file…</span>
816 </label>
817 </td>
818 </tr>
819 <tr>
820 <td colspan=2>
821 <input type='button' name='add' value='Add Caller' />
822 <input type='button' name='xml' value='Export Queue' />
823 <input type='button' name='imp' value='Import Queue' />
824 <input type='button' name='del' value='Delete Queue' />
825 </td>
826 </tr>
827 </tbody>
828 </table>
829 </form>
830 </div>
831 <div id='rhs' data-id='submittedInfo'>
832 <ul></ul><!-- will be populated by ajax callback -->
833 </div>
834 </div>
835 </body>
836</html>