File Coverage

blib/lib/CGI/Lazy/Javascript.pm
Criterion Covered Total %
statement 15 59 25.4
branch 0 12 0.0
condition n/a
subroutine 5 11 45.4
pod 6 7 85.7
total 26 89 29.2


line stmt bran cond sub pod time code
1             package CGI::Lazy::Javascript;
2              
3 1     1   1221 use strict;
  1         2  
  1         31  
4              
5 1     1   5 use CGI::Lazy::Globals;
  1         2  
  1         91  
6 1     1   570 use CGI::Lazy::Javascript::JSONParser;
  1         2  
  1         103  
7 1     1   7 use JavaScript::Minifier qw(minify);
  1         4  
  1         1153  
8              
9             #javascript for ajax requests
10             our $AJAXJS = q[
11             function ajaxSend(request, outgoing, returnHandler, returnTarget) {
12             try {
13             request = new XMLHttpRequest();
14             browser = "standards-compliant";
15             } catch (err) {
16             try {
17             request = new ActiveXObject("Msxml12.XMLHTTP");
18             browser = "bogus";
19             } catch (err) {
20             try {
21             request = new ActiveXObject("Microsoft.XMLHTTP");
22             browser = "bogus";
23             } catch (err) {
24             alert("your browser doesn't support AJAX, try upgrading to Firefox");
25             request = null;
26             }
27             }
28             }
29              
30             try {
31             request.open('POST',parent.location,true);
32             } catch (err) {
33             alert("AJAX call failed: "+ err);
34             }
35            
36             request.setRequestHeader('Content-Type', 'application/json');
37             request.send(JSON.stringify(outgoing));
38             request.onreadystatechange = function() {
39             if (request.readyState == 4) {
40             returnHandler(request.status, request.responseText, returnTarget);
41             }
42             }
43             }
44             ];
45              
46             #javascript for sjax requests
47             our $SJAXJS = q[
48             function sjaxSend(request, outgoing, returnHandler) {
49             try {
50             request = new XMLHttpRequest();
51             browser = "standards-compliant";
52             } catch (err) {
53             try {
54             request = new ActiveXObject("Msxml12.XMLHTTP");
55             browser = "bogus";
56             } catch (err) {
57             try {
58             request = new ActiveXObject("Microsoft.XMLHTTP");
59             browser = "bogus";
60             } catch (err) {
61             alert("your browser doesn't support AJAX, try upgrading to Firefox");
62             request = null;
63             }
64             }
65             }
66              
67             try {
68             request.open('POST',parent.location,false);
69             } catch (err) {
70             alert("AJAX call failed: "+ err);
71             }
72            
73             request.setRequestHeader('Content-Type', 'application/json');
74             request.send(JSON.stringify(outgoing));
75             returnHandler(request.status, request.responseText);
76             }
77             ];
78              
79             #javascript for Widget::Dataset
80             our $DatasetJS = <
81             function datasetController(ID, validator, containerID, searchObject, flagcolor) {
82             this.widgetID = ID;
83             this.validator = validator;
84             this.containerID = containerID;
85             this.flagcolor = flagcolor;
86             this.fieldcolor = null;
87             this.searchObject = searchObject;
88             }
89              
90             datasetController.prototype.constructor = datasetController;
91              
92             datasetController.prototype.deleteRow = function(caller) {
93             var myRow = caller.parentNode.parentNode;
94             var deleter;
95              
96             for (i=0;i
97             var Cell;
98             var Widget;
99              
100             for (var j=0; j< myRow.cells[i].childNodes.length; j++) {
101             if (myRow.cells[i].childNodes[j].id) { //grab the first thing with an id
102             Cell = myRow.cells[i];
103             Widget = Cell.childNodes[j];
104             }
105             }
106            
107             if (/UPDATE/.test(Widget.name)) {
108             Widget.name = Widget.name.replace(/UPDATE/, 'DELETE');
109             Widget.disabled = true;
110             try {
111             this.validator[Widget.id].ignore = 1;
112             } catch(e) {
113             }
114             if (!deleter) {
115             deleter = document.createElement('input');
116             deleter.type = 'hidden';
117             deleter.name = Widget.name;
118             deleter.id = Widget.name+'DELETER';
119             deleter.value = 1;
120             myRow.appendChild(deleter);
121             }
122              
123             } else if (/DELETE/.test(Widget.name)) {
124             Widget.disabled = false;
125             try {
126             this.validator[Widget.id].ignore = 0;
127             } catch(e) {
128             }
129             Widget.name = Widget.name.replace(/DELETE/, 'UPDATE');
130              
131             if (deleter) {
132             myRow.removeChild(deleter);
133             }
134             } else if (/INSERT/.test(Widget.name)) {
135             Widget.name = Widget.name.replace(/INSERT/, 'IGNORE');
136             try {
137             this.validator[Widget.id].ignore = 1;
138             } catch(e) {
139             }
140             Widget.disabled = true;
141             } else if (/IGNORE/.test(Widget.name)) {
142             Widget.disabled = false;
143             try {
144             this.validator[Widget.id].ignore = 0;
145             } catch(e) {
146             }
147             Widget.name = Widget.name.replace(/IGNORE/, 'INSERT');
148             }
149            
150              
151             }
152             }
153              
154             datasetController.prototype.pushRow = function(caller) {
155             var callername = caller.name;
156             var callervalue = caller.value;
157             var callerid = caller.id;
158             var oldRow = caller.parentNode.parentNode;
159             var table = oldRow.parentNode.parentNode;
160              
161             var oldRownum = oldRow.rowIndex;
162             var newRownum = oldRownum +1;
163              
164             if (! document.getElementById(this.widgetID + "Row" + newRownum)) {
165             var newRow = table.insertRow(table.rows.length);
166             newRow.id = this.widgetID + 'Row' + newRownum;
167             var newRownum = newRow.rowIndex;
168              
169             for (var i = 0; i< oldRow.cells.length; i++) {
170             var oldCell;
171             var oldWidget;
172             var newCell;
173             var position = newRow.cells.length;
174             newCell = newRow.insertCell(position);
175              
176             for (var j=0; j< oldRow.cells[i].childNodes.length; j++) {
177             oldCell = oldRow.cells[i];
178             oldWidget = oldCell.childNodes[j];
179             newCell.align = oldCell.align;
180              
181             var fieldName;
182              
183             if (oldWidget.name && oldWidget.id) {
184             oldWidget.name = oldWidget.name.replace(/(.+)-(.+)--(\\d+\$)/, "\$1-:INSERT:\$2--\$3");
185             fieldName = oldWidget.id.replace(/\\d+\$/, '');
186             }
187              
188             var newWidget = oldWidget.cloneNode(true);
189              
190             try {
191             newWidget.name = fieldName + newRownum;
192             newWidget.id = fieldName + newRownum;
193              
194             var re = /^checkbox/;
195             if (re.test(newWidget.type)) {
196             newWidget.value = oldWidget.value;
197             } else {
198             newWidget.value = '';
199             }
200              
201             } catch (e) {
202              
203             }
204              
205             if (oldWidget.name && oldWidget.id) {
206             try {
207             this.validator[newWidget.id] = this.validator[oldWidget.id];
208             } catch (e) {
209              
210             }
211             }
212              
213             newCell.appendChild(newWidget);
214             }
215             }
216             }
217             }
218              
219             datasetController.prototype.validate = function () {
220             var errorMsg = null;
221             for (fieldname in this.validator) {
222             errorMsg += this.tester(fieldname);
223             }
224              
225             if (errorMsg) {
226             return false;
227             }
228             return true;
229             };
230              
231             datasetController.prototype.tester = function(fieldname) {
232             var field = document.getElementById(fieldname);
233             //alert("fieldname: "+fieldname+" field: "+field);
234             var insert = /INSERT/;
235             var update = /UPDATE/;
236             var re = /^\\/.+\\/\$/;
237             var cmd = /^:.+\$/;
238              
239             if (insert.test(field.name) || update.test(field.name)) {
240             try {
241             if (!this.validator[fieldname].ignore) {
242             for (var i in this.validator[fieldname].rules) {
243             var rule = this.validator[fieldname].rules[i];
244             if (re.test(rule)) {
245             var match = new RegExp(rule.replace(/^\\/(.+)\\/\$/, "\$1"));
246             if (!match.test(field.value)) {
247             this.flag(fieldname);
248             return fieldname;
249             } else {
250             return null;
251             }
252             } else if (cmd.test(rule)) {
253              
254              
255             }
256             }
257             }
258             } catch(e) {
259             //alert(fieldname + e);
260             }
261             }
262             };
263              
264             datasetController.prototype.flag = function(fieldname) {
265             var field = document.getElementById(fieldname);
266             this.fieldcolor = field.style.backgroundColor;
267              
268             var flagBackgroundColor = this.flagcolor ? this.flagcolor : "red";
269              
270             field.style.backgroundColor = flagBackgroundColor;
271             };
272              
273             datasetController.prototype.unflag = function(field) {
274             field.style.backgroundColor = this.fieldcolor;
275             };
276              
277             datasetController.prototype.searchResults = function(status, text, target) {
278             var ok = /200/;
279             if (ok.test(status)) {
280             var incoming;
281             try {
282             incoming = JSON.parse(text);
283             } catch (e) {
284             alert('Your request could not be completed. Usually this is due to internal redirection, often as a result of an invalid or expired user session. You may need to log in again.');
285             return;
286             }
287              
288             for (widgetname in incoming.validator) {
289             var controller = eval(widgetname + 'Controller');
290             delete controller.validator;
291             controller.validator = incoming.validator[widgetname];
292             }
293              
294             var html = incoming.html;
295              
296             document.getElementById(target).innerHTML = html;
297             } else {
298             alert('The server returned an HTTP error of type '+status+' and the application cannot continue.');
299             }
300              
301             };
302              
303             datasetController.prototype.multiSearch = function(id) {
304             var primaryKey = eval(this.widgetID + 'MultiSearchPrimaryKey');
305             var outgoing = {CGILazyID : this.widgetID};
306             outgoing[primaryKey] = id;
307             outgoing['noSearchLike'] = 1;
308              
309             var multiSearchRequest;
310             ajaxSend(multiSearchRequest, outgoing, this.searchResults, this.containerID);
311              
312             };
313              
314             datasetController.prototype.search = function() {
315             var outgoing = {CGILazyID : this.widgetID};
316             for (i in this.searchObject) {
317             var element = document.getElementById(this.searchObject[i]);
318             try {
319             if (element.type == 'checkbox') {
320             if (element.checked == true) {
321             outgoing[this.searchObject[i]] = element.value;
322             }
323             } else {
324             outgoing[this.searchObject[i]] = element.value;
325             }
326             }
327             catch (e) {
328             //silently let it go if theres no input for this name built in the template
329             }
330             }
331              
332             var sendRequest;
333             ajaxSend(sendRequest, outgoing, this.searchResults, this.widgetID);
334              
335             };
336              
337             datasetController.prototype.compositeSearch = function() {
338             var outgoing = {CGILazyID : this.widgetID};
339             for (i in this.searchObject) {
340             var element = document.getElementById(this.searchObject[i]);
341             try {
342             if (element.type == 'checkbox') {
343             if (element.checked == true) {
344             outgoing[this.searchObject[i]] = element.value;
345             }
346             } else {
347             outgoing[this.searchObject[i]] = element.value;
348             }
349             }
350             catch (e) {
351             //silently let it go if theres no input for this name built in the template
352             }
353             }
354              
355             var sendRequest;
356             ajaxSend(sendRequest, outgoing, this.searchResults, this.containerID);
357              
358             };
359              
360             END
361              
362             our $CONTROLLERJS = <
363              
364             function controllerController(ID, containerID, selectObject) {
365             this.widgetID = ID;
366             this.containerID = containerID;
367             this.selectObject = selectObject;
368             }
369              
370             controllerController.prototype.constructor = controllerController;
371              
372             controllerController.prototype.select = function() {
373             var outgoing = {};
374             var selectRequest;
375              
376             for (i in this.selectObject) {
377             var re = /^select/;
378             if (re.test(document.getElementById(this.selectObject[i].name).type)) {
379             if (this.selectObject[i].required && !document.getElementById(this.selectObject[i].name).options[document.getElementById(this.selectObject[i].name).selectedIndex].value) {
380             return;
381             }
382             outgoing[this.selectObject[i].name] = document.getElementById(this.selectObject[i].name).options[document.getElementById(this.selectObject[i].name).selectedIndex].value;
383             if (document.getElementById(this.selectObject[i].name).options[document.getElementById(this.selectObject[i].name).selectedIndex].value) {
384             }
385             } else {
386             if (this.selectObject[i].required && !document.getElementById(this.selectObject[i].name).value) {
387             return;
388             }
389             outgoing[this.selectObject[i].name] = document.getElementById(this.selectObject[i].name).value;
390             }
391             }
392              
393             ajaxSend(selectRequest, outgoing, this.selectResults, this.containerID);
394             }
395              
396             controllerController.prototype.selectResults = function(status, text, target) {
397             var ok = /200/;
398             if (ok.test(status)) {
399             var incoming;
400             try {
401             incoming = JSON.parse(text);
402             } catch (e) {
403             alert('Your request could not be completed. Usually this is due to internal redirection, often as a result of an invalid or expired user session. You may need to login again');
404             return;
405             }
406              
407             for (widgetname in incoming.validator) {
408             var controller = eval(widgetname + 'Controller');
409             delete controller.validator;
410             controller.validator = incoming.validator[widgetname];
411             }
412              
413             var html = incoming.html;
414              
415             document.getElementById(target).innerHTML = html;
416             } else {
417             alert('The server returned an HTTP error of type '+status+' and the application cannot continue.');
418             }
419             }
420              
421              
422             END
423              
424             #javascript for domloader
425             our $DOMLOADJS;
426             #javascript for composite
427             our $COMPJS;
428              
429             our %component = (
430             'CGI::Lazy::Widget::Dataset' => $DatasetJS,
431             'CGI::Lazy::Widget::DomLoader' => $DOMLOADJS,
432             'CGI::Lazy::Widget::Composite' => $COMPJS,
433             'CGI::Lazy::Widget::Controller' => $CONTROLLERJS,
434             );
435              
436             #-------------------------------------------------------------------------------------------------
437             sub dir {
438 0     0 1 0 my $self = shift;
439              
440 0         0 return $self->{_dir};
441             }
442              
443             #-------------------------------------------------------------------------------------------------
444             sub file {
445 0     0 1 0 my $self = shift;
446 0         0 my $file = shift;
447              
448 0         0 my $dir = $self->dir;
449              
450 0         0 return "$dir/$file";
451             }
452              
453             #-------------------------------------------------------------------------------------------------
454             sub q {
455 0     0 1 0 my $self = shift;
456              
457 0         0 return $self->{_q};
458             }
459              
460             #----------------------------------------------------------------------------------------
461             sub load {
462 0     0 1 0 my $self = shift;
463 0         0 my $file = shift;
464            
465 0         0 my $jsdir = $self->dir;
466 0         0 $jsdir =~ s/^\///; #strip a leading slash so we don't double it
467 0         0 my $docroot = $ENV{DOCUMENT_ROOT};
468 0         0 $docroot =~ s/\/$//; #strip the trailing slash so we don't double it
469              
470 0 0       0 open IF, "< $docroot/$jsdir/$file" or $self->q->errorHandler->couldntOpenJsFile($docroot, $jsdir, $file, $!);
471              
472 0         0 my $script;
473              
474 0 0       0 if ($self->q->config->noMinify) {
475 0         0 local $/;
476 0         0 $script = ;
477              
478             } else {
479 0         0 $script = minify(input => *IF);
480             }
481              
482 0         0 close IF;
483              
484 0         0 return $self->q->jswrap($script);
485              
486             }
487              
488             #-------------------------------------------------------------------------------------------------
489             sub modules {
490 0     0 1 0 my $self = shift;
491 0         0 my @args = @_;
492              
493 0         0 my $output;
494              
495 0 0       0 if ($self->q->config->noMinify) {
496 0         0 $output = $JSONPARSER . $AJAXJS. $SJAXJS;
497              
498             } else {
499 0         0 $output = $JSONPARSER . minify(input => $AJAXJS). minify(input => $SJAXJS);
500             }
501            
502 0 0       0 if (@args) {
503 0         0 my $inc = {};
504            
505 0         0 $self->parsewidget($inc, $_) foreach @args;
506            
507 0 0       0 if ($self->q->config->noMinify) {
508 0         0 $output .= $component{$_} foreach keys %$inc;
509              
510             } else {
511 0         0 $output .= minify(input => $component{$_}) foreach keys %$inc;
512             }
513             }
514            
515 0         0 return $self->q->jswrap($output);
516             }
517            
518             #-------------------------------------------------------------------------------------------------
519             sub parsewidget {
520 0     0 0 0 my $self = shift;
521 0         0 my $list = shift;
522 0         0 my $widget = shift;
523              
524 0 0       0 if (ref $widget eq 'CGI::Lazy::Widget::Composite') {
525 0         0 $list->{ref $widget} = 1;
526            
527 0         0 $self->parsewidget($list, $_) for @{$widget->memberarray};
  0         0  
528            
529             } else {
530 0         0 $list->{ref $widget} = 1;
531             }
532            
533 0         0 return;
534             }
535            
536             #-------------------------------------------------------------------------------------------------
537             sub new {
538 1     1 1 2 my $class = shift;
539 1         2 my $q = shift;
540              
541 1         4 return bless {
542             _q => $q,
543             _dir => $q->config->jsDir,
544            
545             }, $class;
546             }
547              
548             1
549              
550             __END__