]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/jsolait/lib/lang.js
Release 6.2.1
[Github/sugarcrm.git] / jssource / src_files / include / jsolait / lib / lang.js
1 /*
2
3 Modification information for LGPL compliance
4
5 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
6
7 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
8
9 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
10
11 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
12
13 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
14
15 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
16
17 r42807 - 2008-12-29 11:16:59 -0800 (Mon, 29 Dec 2008) - dwong - Branch from trunk/sugarcrm r42806 to branches/tokyo/sugarcrm
18
19 r4085 - 2005-04-13 17:30:42 -0700 (Wed, 13 Apr 2005) - robert - adding meeting scheduler and accept/decline
20
21
22 */
23
24 /*
25   Copyright (c) 2004 Jan-Klaas Kollhof
26   
27   This file is part of the JavaScript o lait library(jsolait).
28   
29   jsolait is free software; you can redistribute it and/or modify
30   it under the terms of the GNU Lesser General Public License as published by
31   the Free Software Foundation; either version 2.1 of the License, or
32   (at your option) any later version.
33  
34   This software is distributed in the hope that it will be useful,
35   but WITHOUT ANY WARRANTY; without even the implied warranty of
36   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   GNU Lesser General Public License for more details.
38  
39   You should have received a copy of the GNU Lesser General Public License
40   along with this software; if not, write to the Free Software
41   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
42 */
43
44 /**
45     Module providing language services like tokenizing JavaScript code
46     or converting JavaScript objects to and from JSON (see json.org).
47     To customize JSON serialization of Objects just overwrite the toJSON method in your class.
48 */
49 Module("lang", "0.3.7", function(mod){
50    
51     var ISODate = function(d){
52         if(/^(\d{4})(\d{2})(\d{2})T(\d{2}):(\d{2}):(\d{2})/.test(d)){
53             return new Date(Date.UTC(RegExp.$1, RegExp.$2-1, RegExp.$3, RegExp.$4, RegExp.$5, RegExp.$6));
54         }else{ //todo error message
55             throw new mod.Exception("Not an ISO date: " + d);
56         }
57     }
58         
59     mod.JSONParser=Class("JSONParser", function(publ, supr){
60         publ.init=function(){
61             this.libs = {};
62             var sys = {"ISODate" : ISODate};
63             this.addLib(sys, "sys", ["ISODate"]);
64         }
65         
66         publ.addLib = function(obj, name, exports){
67             if(exports == null){
68                 this.libs[name] = obj;
69             }else{
70                 for(var i=0;i<exports.length;i++){
71                     this.libs[name + "." + exports[i]] = obj[exports[i]];
72                 }
73             }
74         }
75         
76         var EmptyValue = {};
77         var SeqSep = {};
78         
79         var parseValue = function(tkns, libs){
80             var tkn = tkns.nextNonWS();
81             switch(tkn.type){
82                 case mod.tokens.STR:
83                 case mod.tokens.NUM:
84                     return eval(tkn.value);
85                 case mod.tokens.NAME:
86                     return parseName(tkn.value);
87                 case mod.tokens.OP:
88                     switch(tkn.value){
89                         case "[":
90                             return parseArray(tkns, libs);
91                             break;
92                         case "{":
93                             return parseObj(tkns, libs);
94                             break;
95                         case "}": case "]":
96                             return EmptyValue;
97                         case ",":
98                             return SeqSep;
99                         default:
100                             throw new mod.Exception("expected '[' or '{' but found: '" + tkn.value + "'");
101                     }
102             }
103             return EmptyValue;
104         }
105         
106         var parseArray = function(tkns, libs){
107             var a = [];
108             while(! tkns.finished()){
109                 var v = parseValue(tkns, libs);
110                 if(v == EmptyValue){
111                     return a;
112                 }else{
113                     a.push(v);
114                     v = parseValue(tkns, libs);
115                     if(v == EmptyValue){
116                         return a;
117                     }else if(v != SeqSep){
118                         throw new mod.Exception("',' expected but found: '" + v + "'");
119                     }
120                 }
121             }
122             throw new mod.Exception("']' expected");
123         }
124                      
125         var parseObj = function(tkns, libs){
126             var obj = {};
127             var nme =""
128             while(! tkns.finished()){
129                 var tkn = tkns.nextNonWS();
130                 if(tkn.type == mod.tokens.STR){
131                     var nme =  eval(tkn.value);
132                     tkn = tkns.nextNonWS();
133                     if(tkn.value == ":"){
134                         var v = parseValue(tkns, libs);
135                         if(v == SeqSep || v == EmptyValue){
136                             throw new mod.Exception("value expected");
137                         }else{
138                             obj[nme] = v;
139                             v = parseValue(tkns, libs);
140                             if(v == EmptyValue){
141                                 return transformObj(obj, libs);
142                             }else if(v != SeqSep){
143                                 throw new mod.Exception("',' expected");
144                             }
145                         }
146                     }else{
147                         throw new mod.Exception("':' expected but found: '" + tkn.value + "'");
148                     }
149                 }else if(tkn.value == "}"){
150                     return transformObj(obj, libs);
151                 }else{
152                     throw new mod.Exception("String expected");
153                 }
154             }
155             throw new mod.Exception("'}' expected.")
156         }
157         
158         var transformObj = function(obj, libs){
159             var o2;
160             if(obj.jsonclass != null){
161                 var clsName = obj.jsonclass[0];
162                 var params = obj.jsonclass[1]
163                 if(libs[clsName]){
164                     o2 = libs[clsName].apply(this, params);
165                     for(var nme in obj){
166                         if(nme != "jsonclass"){
167                             if(typeof obj[nme] != "function"){
168                                 o2[nme] = obj[nme];
169                             }
170                         }
171                     }
172                 }else{
173                     throw new mod.Exception("jsonclass not found: " + clsName);
174                 }
175             }else{
176                 o2 = obj;
177             }
178             return o2;
179         }
180         
181         var parseName = function(name){
182             switch(name){
183                 case "null":
184                     return null;
185                 case "true":
186                     return true;
187                 case "false":
188                     return false;
189                 default:
190                     throw new mod.Exception("'null', 'true', 'false' expected but found: '" + name + "'");
191             }
192         }
193         
194         publ.jsonToObj = function(data){
195             var t = new mod.Tokenizer(data);
196             return parseValue(t, this.libs);
197         }
198                 
199         publ.objToJson=function(obj){
200             if(obj == null){
201                 return "null";
202             }else{
203                 return obj.toJSON();
204             }
205         }
206     })
207         
208     mod.parser = new mod.JSONParser();
209     
210     /**
211         Turns JSON code into JavaScript objects.
212         @param src  The source as a String.
213     */
214     mod.jsonToObj=function(src){
215         return mod.parser.jsonToObj(src);
216     }
217     
218     /**
219         Turns an object into JSON.
220         This is the same as calling obj.toJSON();
221         @param obj  The object to marshall.
222     */
223     mod.objToJson=function(obj){
224         return mod.parser.objToJson(obj);
225     }
226     
227     ///Token constants for the tokenizer.
228     mod.tokens = {};
229     mod.tokens.WSP = 0;
230     mod.tokens.OP =1;
231     mod.tokens.STR = 2;
232     mod.tokens.NAME = 3;
233     mod.tokens.NUM = 4;
234     mod.tokens.ERR = 5;
235     mod.tokens.NL = 6;
236     mod.tokens.COMMENT = 7;
237     mod.tokens.DOCCOMMENT = 8;
238     mod.tokens.REGEXP = 9;
239     
240     
241     //todo:doc
242     mod.Token=Class(function(publ, supr){
243         
244         publ.init=function(type, value, pos, err){
245             this.type = type;
246             this.value = value;
247             this.pos = pos;
248             this.err= err;
249         }
250         
251     })
252     
253     /**
254         Tokenizer Class which incrementally parses JavaScript code and returns the language tokens.
255     */
256     mod.Tokenizer=Class("Tokenizer", function(publ, supr){
257         publ.init=function(s){
258             this._working = s;
259             this._pos = 0;
260         }
261         
262         /**
263             Returns weather or not the code was parsed.
264             @return True if the complete code was parsed, false otherwise.
265         */
266         publ.finished=function(){
267             return this._working.length == 0;
268         }
269         
270         publ.nextNonWS = function(nlIsWS){
271             var tkn = this.next();
272             while((tkn.type == mod.tokens.WSP) ||  (nlIsWS && (tkn.type == mod.tokens.NL))){
273                 tkn = this.next();
274             }
275             return tkn;
276         }
277         
278         /**
279             Returns the next token.
280             @return The next token.
281         */
282         publ.next = function(){
283             if(this._working ==""){
284                 throw new mod.Exception("Empty");
285             } 
286             var s1 = this._working.charAt(0);
287             var s2 = s1 + this._working.charAt(1);
288             var s3 = s2 + this._working.charAt(2);
289             var rslt=[];
290             switch(s1){
291                 case '"': case "'":
292                     try{
293                         s1 = extractQString(this._working);
294                         rslt= new mod.Token(mod.tokens.STR, s1, this._pos);
295                     }catch(e){
296                         rslt= new mod.Token(mod.tokens.ERR, s1, this._pos, e);
297                     }
298                     break;
299                 case "\n": case "\r":
300                     rslt =new mod.Token(mod.tokens.NL, s1, this._pos);
301                     break;   
302                 case "-": //check for negative numbers
303                     s1=this._working.match(/-\d+\.\d+|-\d+/)[0];
304                     if(/^-\d|-\d\.\d/.test(s1)){//number
305                         rslt =  new mod.Token(mod.tokens.NUM, s1, this._pos);
306                         break;
307                     }
308                 
309                 case "{": case "}": case "[": case "]": case "(": case ")":
310                 case ":": case ",": case ".": case ";":
311                 case "*": case "-": case "+":
312                 case "=": case "<": case ">":  case "!":   
313                 case "|": case "&":
314                     switch(s2){
315                         case "==": case "!=": case "<>":  case "<=": case ">=":case "||": case "&&":
316                             rslt = new mod.Token(mod.tokens.OP, s2, this._pos);
317                             break;
318                         default:
319                             rslt = new mod.Token(mod.tokens.OP, s1, this._pos);
320                     }
321                     break;
322                 case "/":
323                     if(s2 == "//" || s3 =="///"){
324                         s1 = extractSLComment(this._working);
325                         rslt = new mod.Token(s1.charAt(2) != "/" ? mod.tokens.COMMENT:mod.tokens.DOCCOMMENT, s1, this._pos);
326                     }else if(s2 == "/*" || s3 =="/**"){
327                         try{
328                             s1 = extractMLComment(this._working);
329                             rslt = new mod.Token(s3 !="/**" ? mod.tokens.COMMENT: mod.tokens.DOCCOMMENT, s1, this._pos);
330                         }catch(e){
331                             rslt= new mod.Token(mod.tokens.ERR, s3 != "/**" ? s2 : s3, this._pos, e);
332                         }
333                     }else{
334                         try{
335                             s1 = extractRegExp(this._working);
336                             rslt  = new mod.Token(mod.tokens.REGEXP, s1, this._pos);
337                         }catch(e){
338                             rslt = new mod.Token(mod.tokens.OP, s1, this._pos, e);
339                         }
340                     }
341                     break;
342                 case " ":
343                     var i = 0;
344                     var s="";
345                     while(this._working.charAt(i) == " "){
346                         s+=" ";
347                         i++;
348                     }
349                     rslt = new mod.Token(mod.tokens.WSP, s, this._pos);
350                     break;
351                 default:
352                     s1=this._working.match(/\d+\.\d+|\d+|\w+/)[0];
353                     if(/^\d|\d\.\d/.test(s1)){//number
354                         rslt =  new mod.Token(mod.tokens.NUM, s1, this._pos);
355                     }else{//name
356                         rslt =new mod.Token(mod.tokens.NAME, s1, this._pos);
357                     }
358             }
359             
360             this._working=this._working.slice(rslt.value.length);
361             this._pos += rslt.value.length;
362             return rslt;
363         }
364         
365         var searchQoute = function(s, q){
366             if(q=="'"){
367                 return s.search(/[\\']/);
368             }else{
369                 return s.search(/[\\"]/);
370             }
371         }
372         
373         var extractQString=function(s){
374             if(s.charAt(0) == "'"){
375                 var q="'";
376             }else{
377                 var q='"';
378             }
379             s=s.slice(1);
380             var rs="";
381             var p= searchQoute(s, q);
382             while(p >= 0){
383                 if(p >=0){
384                     if(s.charAt(p) == q){
385                         rs += s.slice(0, p+1);
386                         s = s.slice(p+1);
387                         return q + rs;
388                     }else{
389                         rs+=s.slice(0, p+2);
390                         s = s.slice(p+2);
391                     }
392                 }
393                 p = searchQoute(s, q);
394             }
395             throw new mod.Exception("End of String expected.");
396         }
397         
398         var extractSLComment=function(s){
399             var p = s.search(/\n/);
400             if(p>=0){
401                 return s.slice(0,p+1);
402             }else{
403                 return s;
404             }
405         }
406         
407         var extractMLComment=function(s){
408             var p = s.search(/\*\//);
409             if(p>=0){
410                 return s.slice(0,p+2);
411             }else{
412                 throw new mod.Exception("End of comment expected.");
413             }
414         }
415         
416         var extractRegExp=function(s){
417             var p=0;
418             for(var i=0;i<s.length;i++){
419                 if(s.charAt(i) == "/"){
420                     p=i;
421                 }
422                 if(s.charAt(i) == "\n"){
423                     i = s.length;
424                 }
425             }
426             return s.slice(0,p+1);
427         }
428     })
429     
430     /**
431         Converts an object to JSON.
432     */
433     Object.prototype.toJSON = function(){
434         var v=[];
435         for(attr in this){
436             if(typeof this[attr] != "function"){
437                 v.push('"' + attr + '": ' + mod.objToJson(this[attr]));
438             }
439         }
440         return "{" + v.join(", ") + "}";
441     }
442     
443     /**
444         Converts a String to JSON.
445     */
446     String.prototype.toJSON = function(){
447         var s = '"' + this.replace(/(["\\])/g, '\\$1') + '"';
448         s = s.replace(/(\n)/g,"\\n");
449         return s;
450     }
451     
452     /**
453         Converts a Number to JSON.
454     */
455     Number.prototype.toJSON = function(){
456         return this.toString();
457     }
458     
459     /**
460         Converts a Boolean to JSON.
461     */
462     Boolean.prototype.toJSON = function(){
463         return this.toString();
464     }
465     
466     /**
467         Converts a Date to JSON.
468         Date representation is not defined in JSON.
469     */
470     Date.prototype.toJSON= function(){
471         var padd=function(s, p){
472             s=p+s
473             return s.substring(s.length - p.length)
474         }
475         var y = padd(this.getUTCFullYear(), "0000");
476         var m = padd(this.getUTCMonth() + 1, "00");
477         var d = padd(this.getUTCDate(), "00");
478         var h = padd(this.getUTCHours(), "00");
479         var min = padd(this.getUTCMinutes(), "00");
480         var s = padd(this.getUTCSeconds(), "00");
481         
482         var isodate = y +  m  + d + "T" + h +  ":" + min + ":" + s
483         
484         return '{"jsonclass":["sys.ISODate", ["' + isodate + '"]]}';
485     }
486     
487     /**
488         Converts an Array to JSON.
489     */
490     Array.prototype.toJSON = function(){
491         var v = [];
492         for(var i=0;i<this.length;i++){
493             v.push(mod.objToJson(this[i])) ;
494         }
495         return "[" + v.join(", ") + "]";
496     }
497         
498     mod.test=function(){
499         try{
500             print(mod.jsonToObj("['sds', -12377,-1212.1212, 12, '-2312']").toJSON());
501         }catch(e){
502             print(e.toTraceString());
503         }
504         
505     }
506     
507 })
508