
1 // 2 // Copyright (c) 2006, Brian Frank and Andy Frank 3 // Licensed under the Academic Free License version 3.0 4 // 5 // History: 6 // 24 Jul 06 Andy Frank Creation 7 // 8 9 ** 10 ** UserAgent identifies a user agent. 11 ** 12 class UserAgent 13 { 14 15 ////////////////////////////////////////////////////////////////////////// 16 // Constructor 17 ////////////////////////////////////////////////////////////////////////// 18 19 ** 20 ** Construct a new UserAgent with this user agent string. 21 ** 22 new make(Str userAgentStr) 23 { 24 // TODO - this does not allow for multiple spaces, which no one 25 // uses in real user agents, but should be allow for them? 26 27 // Tokens 28 arr := userAgentStr.split(" ") 29 for (i:=0; i<arr.size; i++) 30 { 31 s := arr[i] 32 if (s.startsWith("(")) 33 while (!arr[i].endsWith(")")) 34 s += " " + arr[++i] 35 tokens.push(s) 36 } 37 tokens = tokens.ro 38 39 // Browser 40 isIE = tokens.any |Str s->Bool| { return s.contains("MSIE") } 41 isFirefox = tokens.any |Str s->Bool| { return s.startsWith("Firefox/") } 42 isSafari = tokens.any |Str s->Bool| { return s.startsWith("Safari/") } 43 isOpera = tokens.any |Str s->Bool| { return s.startsWith("Opera/") } 44 45 // Version 46 for (i:=0; i<tokens.size; i++) 47 { 48 s := tokens[i] 49 50 if (s.contains("MSIE")) 51 { 52 start := s.index("MSIE") + 5 53 end := s.index(";", start) 54 version = parseVer(s[start...end]) 55 break 56 } 57 58 if (s.startsWith("Firefox/")) 59 { 60 version = parseVer(s["Firefox/".size..-1]) 61 break 62 } 63 64 if (s.startsWith("Safari/")) 65 { 66 version = parseVer(s["Safari/".size..-1]) 67 break 68 } 69 70 if (s.startsWith("Opera/")) 71 { 72 version = parseVer(s["Opera/".size..-1]) 73 break 74 } 75 } 76 77 // Check for valid version 78 if (version == null) version = Version.fromStr("0.0") 79 } 80 81 ** 82 ** Parse this version string into a Version object. This 83 ** method is more forgiving than straight Version.fromStr(). 84 ** It will strip out invalid characters and attempt to 85 ** return a reasonable version match. Returns null if 86 ** nothing could be salvaged. 87 ** 88 ** "7.0b" -> 7.0 89 ** "2.1a2" -> 2.1 90 ** "1b" -> 1 91 ** "abc" -> null 92 ** 93 internal Version parseVer(Str s) 94 { 95 v := "" 96 for (i:=0; i<s.size; i++) 97 { 98 curr := s[i] 99 next := (i < s.size-1) ? s[i+1] : -1 100 last := (i > 0) ? s[i-1] : -1 101 102 if (curr === '.') 103 { 104 if (last === '.') break 105 if (next === '.') break 106 if (!next.isDigit) break 107 108 } 109 else if (!curr.isDigit) break 110 111 v += s[i].toChar 112 } 113 114 try { return Version.fromStr(v) } catch {} 115 return null 116 } 117 118 ////////////////////////////////////////////////////////////////////////// 119 // Obj 120 ////////////////////////////////////////////////////////////////////////// 121 122 ** 123 ** Return a string representation of this object. The result 124 ** of this method will match the original Str that was used 125 ** to parse this UserAgent. 126 ** 127 override Str toStr() 128 { 129 s := "" 130 tokens.each |Str p, Int i| 131 { 132 if (i > 0) s += " " 133 s += p 134 } 135 return s 136 } 137 138 ////////////////////////////////////////////////////////////////////////// 139 // Fields 140 ////////////////////////////////////////////////////////////////////////// 141 142 ** 143 ** Is this user agent Microsoft Internet Explorer. 144 ** 145 readonly Bool isIE 146 147 ** 148 ** Is this user agent Mozilla Firefox. 149 ** 150 readonly Bool isFirefox 151 152 ** 153 ** Is this agent Apple Safari. 154 ** 155 readonly Bool isSafari 156 157 ** 158 ** Is this agent Opera. 159 ** 160 readonly Bool isOpera 161 162 ** 163 ** The primary version for this user agent. This field is 164 ** only valid if the user agent is IE, Firefox, Safari, or 165 ** Opera. For all other browsers, or if the above list has 166 ** an invalid version string, this field will default 167 ** to "0.0". 168 ** 169 readonly Version version 170 171 ** 172 ** The tokens identifying this user agent in the order 173 ** they were parsed. This list is readonly. 174 ** 175 Str[] tokens := Str[,] 176 177 }