scratch – Diff between revs 75 and 125

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 75 Rev 125
1   1  
2 Pattern = require './Pattern' 2 Pattern = require './Pattern'
3   3  
4 # A bunch of utility methods 4 # A bunch of utility methods
5 # 5 #
6 class Utils 6 class Utils
7   7  
8 @REGEX_LEFT_TRIM_BY_CHAR: {} 8 @REGEX_LEFT_TRIM_BY_CHAR: {}
9 @REGEX_RIGHT_TRIM_BY_CHAR: {} 9 @REGEX_RIGHT_TRIM_BY_CHAR: {}
10 @REGEX_SPACES: /\s+/g 10 @REGEX_SPACES: /\s+/g
11 @REGEX_DIGITS: /^\d+$/ 11 @REGEX_DIGITS: /^\d+$/
12 @REGEX_OCTAL: /[^0-7]/gi 12 @REGEX_OCTAL: /[^0-7]/gi
13 @REGEX_HEXADECIMAL: /[^a-f0-9]/gi 13 @REGEX_HEXADECIMAL: /[^a-f0-9]/gi
14   14  
15 # Precompiled date pattern 15 # Precompiled date pattern
16 @PATTERN_DATE: new Pattern '^'+ 16 @PATTERN_DATE: new Pattern '^'+
17 '(?<year>[0-9][0-9][0-9][0-9])'+ 17 '(?<year>[0-9][0-9][0-9][0-9])'+
18 '-(?<month>[0-9][0-9]?)'+ 18 '-(?<month>[0-9][0-9]?)'+
19 '-(?<day>[0-9][0-9]?)'+ 19 '-(?<day>[0-9][0-9]?)'+
20 '(?:(?:[Tt]|[ \t]+)'+ 20 '(?:(?:[Tt]|[ \t]+)'+
21 '(?<hour>[0-9][0-9]?)'+ 21 '(?<hour>[0-9][0-9]?)'+
22 ':(?<minute>[0-9][0-9])'+ 22 ':(?<minute>[0-9][0-9])'+
23 ':(?<second>[0-9][0-9])'+ 23 ':(?<second>[0-9][0-9])'+
24 '(?:\.(?<fraction>[0-9]*))?'+ 24 '(?:\.(?<fraction>[0-9]*))?'+
25 '(?:[ \t]*(?<tz>Z|(?<tz_sign>[-+])(?<tz_hour>[0-9][0-9]?)'+ 25 '(?:[ \t]*(?<tz>Z|(?<tz_sign>[-+])(?<tz_hour>[0-9][0-9]?)'+
26 '(?::(?<tz_minute>[0-9][0-9]))?))?)?'+ 26 '(?::(?<tz_minute>[0-9][0-9]))?))?)?'+
27 '$', 'i' 27 '$', 'i'
28   28  
29 # Local timezone offset in ms 29 # Local timezone offset in ms
30 @LOCAL_TIMEZONE_OFFSET: new Date().getTimezoneOffset() * 60 * 1000 30 @LOCAL_TIMEZONE_OFFSET: new Date().getTimezoneOffset() * 60 * 1000
31   31  
32 # Trims the given string on both sides 32 # Trims the given string on both sides
33 # 33 #
34 # @param [String] str The string to trim 34 # @param [String] str The string to trim
35 # @param [String] _char The character to use for trimming (default: '\\s') 35 # @param [String] _char The character to use for trimming (default: '\\s')
36 # 36 #
37 # @return [String] A trimmed string 37 # @return [String] A trimmed string
38 # 38 #
39 @trim: (str, _char = '\\s') -> 39 @trim: (str, _char = '\\s') ->
40 return str.trim() -  
41 regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char] 40 regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char]
42 unless regexLeft? 41 unless regexLeft?
43 @REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*' 42 @REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*'
44 regexLeft.lastIndex = 0 43 regexLeft.lastIndex = 0
45 regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char] 44 regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char]
46 unless regexRight? 45 unless regexRight?
47 @REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$' 46 @REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$'
48 regexRight.lastIndex = 0 47 regexRight.lastIndex = 0
49 return str.replace(regexLeft, '').replace(regexRight, '') 48 return str.replace(regexLeft, '').replace(regexRight, '')
50   49  
51   50  
52 # Trims the given string on the left side 51 # Trims the given string on the left side
53 # 52 #
54 # @param [String] str The string to trim 53 # @param [String] str The string to trim
55 # @param [String] _char The character to use for trimming (default: '\\s') 54 # @param [String] _char The character to use for trimming (default: '\\s')
56 # 55 #
57 # @return [String] A trimmed string 56 # @return [String] A trimmed string
58 # 57 #
59 @ltrim: (str, _char = '\\s') -> 58 @ltrim: (str, _char = '\\s') ->
60 regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char] 59 regexLeft = @REGEX_LEFT_TRIM_BY_CHAR[_char]
61 unless regexLeft? 60 unless regexLeft?
62 @REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*' 61 @REGEX_LEFT_TRIM_BY_CHAR[_char] = regexLeft = new RegExp '^'+_char+''+_char+'*'
63 regexLeft.lastIndex = 0 62 regexLeft.lastIndex = 0
64 return str.replace(regexLeft, '') 63 return str.replace(regexLeft, '')
65   64  
66   65  
67 # Trims the given string on the right side 66 # Trims the given string on the right side
68 # 67 #
69 # @param [String] str The string to trim 68 # @param [String] str The string to trim
70 # @param [String] _char The character to use for trimming (default: '\\s') 69 # @param [String] _char The character to use for trimming (default: '\\s')
71 # 70 #
72 # @return [String] A trimmed string 71 # @return [String] A trimmed string
73 # 72 #
74 @rtrim: (str, _char = '\\s') -> 73 @rtrim: (str, _char = '\\s') ->
75 regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char] 74 regexRight = @REGEX_RIGHT_TRIM_BY_CHAR[_char]
76 unless regexRight? 75 unless regexRight?
77 @REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$' 76 @REGEX_RIGHT_TRIM_BY_CHAR[_char] = regexRight = new RegExp _char+''+_char+'*$'
78 regexRight.lastIndex = 0 77 regexRight.lastIndex = 0
79 return str.replace(regexRight, '') 78 return str.replace(regexRight, '')
80   79  
81   80  
82 # Checks if the given value is empty (null, undefined, empty string, string '0') 81 # Checks if the given value is empty (null, undefined, empty string, string '0', empty Array, empty Object)
83 # 82 #
84 # @param [Object] value The value to check 83 # @param [Object] value The value to check
85 # 84 #
86 # @return [Boolean] true if the value is empty 85 # @return [Boolean] true if the value is empty
87 # 86 #
88 @isEmpty: (value) -> 87 @isEmpty: (value) ->
89 return not(value) or value is '' or value is '0' or (value instanceof Array and value.length is 0) 88 return not(value) or value is '' or value is '0' or (value instanceof Array and value.length is 0) or @isEmptyObject(value)
-   89  
-   90 # Checks if the given value is an empty object
-   91 #
-   92 # @param [Object] value The value to check
-   93 #
-   94 # @return [Boolean] true if the value is empty and is an object
-   95 #
-   96 @isEmptyObject: (value) ->
90   97 return value instanceof Object and (k for own k of value).length is 0
91   98  
92 # Counts the number of occurences of subString inside string 99 # Counts the number of occurences of subString inside string
93 # 100 #
94 # @param [String] string The string where to count occurences 101 # @param [String] string The string where to count occurences
95 # @param [String] subString The subString to count 102 # @param [String] subString The subString to count
96 # @param [Integer] start The start index 103 # @param [Integer] start The start index
97 # @param [Integer] length The string length until where to count 104 # @param [Integer] length The string length until where to count
98 # 105 #
99 # @return [Integer] The number of occurences 106 # @return [Integer] The number of occurences
100 # 107 #
101 @subStrCount: (string, subString, start, length) -> 108 @subStrCount: (string, subString, start, length) ->
102 c = 0 109 c = 0
103 110  
104 string = '' + string 111 string = '' + string
105 subString = '' + subString 112 subString = '' + subString
106 113  
107 if start? 114 if start?
108 string = string[start..] 115 string = string[start..]
109 if length? 116 if length?
110 string = string[0...length] 117 string = string[0...length]
111 118  
112 len = string.length 119 len = string.length
113 sublen = subString.length 120 sublen = subString.length
114 for i in [0...len] 121 for i in [0...len]
115 if subString is string[i...sublen] 122 if subString is string[i...sublen]
116 c++ 123 c++
117 i += sublen - 1 124 i += sublen - 1
118 125  
119 return c 126 return c
120   127  
121   128  
122 # Returns true if input is only composed of digits 129 # Returns true if input is only composed of digits
123 # 130 #
124 # @param [Object] input The value to test 131 # @param [Object] input The value to test
125 # 132 #
126 # @return [Boolean] true if input is only composed of digits 133 # @return [Boolean] true if input is only composed of digits
127 # 134 #
128 @isDigits: (input) -> 135 @isDigits: (input) ->
129 @REGEX_DIGITS.lastIndex = 0 136 @REGEX_DIGITS.lastIndex = 0
130 return @REGEX_DIGITS.test input 137 return @REGEX_DIGITS.test input
131   138  
132   139  
133 # Decode octal value 140 # Decode octal value
134 # 141 #
135 # @param [String] input The value to decode 142 # @param [String] input The value to decode
136 # 143 #
137 # @return [Integer] The decoded value 144 # @return [Integer] The decoded value
138 # 145 #
139 @octDec: (input) -> 146 @octDec: (input) ->
140 @REGEX_OCTAL.lastIndex = 0 147 @REGEX_OCTAL.lastIndex = 0
141 return parseInt((input+'').replace(@REGEX_OCTAL, ''), 8) 148 return parseInt((input+'').replace(@REGEX_OCTAL, ''), 8)
142   149  
143   150  
144 # Decode hexadecimal value 151 # Decode hexadecimal value
145 # 152 #
146 # @param [String] input The value to decode 153 # @param [String] input The value to decode
147 # 154 #
148 # @return [Integer] The decoded value 155 # @return [Integer] The decoded value
149 # 156 #
150 @hexDec: (input) -> 157 @hexDec: (input) ->
151 @REGEX_HEXADECIMAL.lastIndex = 0 158 @REGEX_HEXADECIMAL.lastIndex = 0
152 input = @trim(input) 159 input = @trim(input)
153 if (input+'')[0...2] is '0x' then input = (input+'')[2..] 160 if (input+'')[0...2] is '0x' then input = (input+'')[2..]
154 return parseInt((input+'').replace(@REGEX_HEXADECIMAL, ''), 16) 161 return parseInt((input+'').replace(@REGEX_HEXADECIMAL, ''), 16)
155   162  
156   163  
157 # Get the UTF-8 character for the given code point. 164 # Get the UTF-8 character for the given code point.
158 # 165 #
159 # @param [Integer] c The unicode code point 166 # @param [Integer] c The unicode code point
160 # 167 #
161 # @return [String] The corresponding UTF-8 character 168 # @return [String] The corresponding UTF-8 character
162 # 169 #
163 @utf8chr: (c) -> 170 @utf8chr: (c) ->
164 ch = String.fromCharCode 171 ch = String.fromCharCode
165 if 0x80 > (c %= 0x200000) 172 if 0x80 > (c %= 0x200000)
166 return ch(c) 173 return ch(c)
167 if 0x800 > c 174 if 0x800 > c
168 return ch(0xC0 | c>>6) + ch(0x80 | c & 0x3F) 175 return ch(0xC0 | c>>6) + ch(0x80 | c & 0x3F)
169 if 0x10000 > c 176 if 0x10000 > c
170 return ch(0xE0 | c>>12) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F) 177 return ch(0xE0 | c>>12) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F)
171   178  
172 return ch(0xF0 | c>>18) + ch(0x80 | c>>12 & 0x3F) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F) 179 return ch(0xF0 | c>>18) + ch(0x80 | c>>12 & 0x3F) + ch(0x80 | c>>6 & 0x3F) + ch(0x80 | c & 0x3F)
173   180  
174   181  
175 # Returns the boolean value equivalent to the given input 182 # Returns the boolean value equivalent to the given input
176 # 183 #
177 # @param [String|Object] input The input value 184 # @param [String|Object] input The input value
178 # @param [Boolean] strict If set to false, accept 'yes' and 'no' as boolean values 185 # @param [Boolean] strict If set to false, accept 'yes' and 'no' as boolean values
179 # 186 #
180 # @return [Boolean] the boolean value 187 # @return [Boolean] the boolean value
181 # 188 #
182 @parseBoolean: (input, strict = true) -> 189 @parseBoolean: (input, strict = true) ->
183 if typeof(input) is 'string' 190 if typeof(input) is 'string'
184 lowerInput = input.toLowerCase() 191 lowerInput = input.toLowerCase()
185 if not strict 192 if not strict
186 if lowerInput is 'no' then return false 193 if lowerInput is 'no' then return false
187 if lowerInput is '0' then return false 194 if lowerInput is '0' then return false
188 if lowerInput is 'false' then return false 195 if lowerInput is 'false' then return false
189 if lowerInput is '' then return false 196 if lowerInput is '' then return false
190 return true 197 return true
191 return !!input 198 return !!input
192   199  
193   200  
194   201  
195 # Returns true if input is numeric 202 # Returns true if input is numeric
196 # 203 #
197 # @param [Object] input The value to test 204 # @param [Object] input The value to test
198 # 205 #
199 # @return [Boolean] true if input is numeric 206 # @return [Boolean] true if input is numeric
200 # 207 #
201 @isNumeric: (input) -> 208 @isNumeric: (input) ->
202 @REGEX_SPACES.lastIndex = 0 209 @REGEX_SPACES.lastIndex = 0
203 return typeof(input) is 'number' or typeof(input) is 'string' and !isNaN(input) and input.replace(@REGEX_SPACES, '') isnt '' 210 return typeof(input) is 'number' or typeof(input) is 'string' and !isNaN(input) and input.replace(@REGEX_SPACES, '') isnt ''
204   211  
205   212  
206 # Returns a parsed date from the given string 213 # Returns a parsed date from the given string
207 # 214 #
208 # @param [String] str The date string to parse 215 # @param [String] str The date string to parse
209 # 216 #
210 # @return [Date] The parsed date or null if parsing failed 217 # @return [Date] The parsed date or null if parsing failed
211 # 218 #
212 @stringToDate: (str) -> 219 @stringToDate: (str) ->
213 unless str?.length 220 unless str?.length
214 return null 221 return null
215   222  
216 # Perform regular expression pattern 223 # Perform regular expression pattern
217 info = @PATTERN_DATE.exec str 224 info = @PATTERN_DATE.exec str
218 unless info 225 unless info
219 return null 226 return null
220   227  
221 # Extract year, month, day 228 # Extract year, month, day
222 year = parseInt info.year, 10 229 year = parseInt info.year, 10
223 month = parseInt(info.month, 10) - 1 # In javascript, january is 0, february 1, etc... 230 month = parseInt(info.month, 10) - 1 # In javascript, january is 0, february 1, etc...
224 day = parseInt info.day, 10 231 day = parseInt info.day, 10
225   232  
226 # If no hour is given, return a date with day precision 233 # If no hour is given, return a date with day precision
227 unless info.hour? 234 unless info.hour?
228 date = new Date Date.UTC(year, month, day) 235 date = new Date Date.UTC(year, month, day)
229 return date 236 return date
230   237  
231 # Extract hour, minute, second 238 # Extract hour, minute, second
232 hour = parseInt info.hour, 10 239 hour = parseInt info.hour, 10
233 minute = parseInt info.minute, 10 240 minute = parseInt info.minute, 10
234 second = parseInt info.second, 10 241 second = parseInt info.second, 10
235   242  
236 # Extract fraction, if given 243 # Extract fraction, if given
237 if info.fraction? 244 if info.fraction?
238 fraction = info.fraction[0...3] 245 fraction = info.fraction[0...3]
239 while fraction.length < 3 246 while fraction.length < 3
240 fraction += '0' 247 fraction += '0'
241 fraction = parseInt fraction, 10 248 fraction = parseInt fraction, 10
242 else 249 else
243 fraction = 0 250 fraction = 0
244   251  
245 # Compute timezone offset if given 252 # Compute timezone offset if given
246 if info.tz? 253 if info.tz?
247 tz_hour = parseInt info.tz_hour, 10 254 tz_hour = parseInt info.tz_hour, 10
248 if info.tz_minute? 255 if info.tz_minute?
249 tz_minute = parseInt info.tz_minute, 10 256 tz_minute = parseInt info.tz_minute, 10
250 else 257 else
251 tz_minute = 0 258 tz_minute = 0
252   259  
253 # Compute timezone delta in ms 260 # Compute timezone delta in ms
254 tz_offset = (tz_hour * 60 + tz_minute) * 60000 261 tz_offset = (tz_hour * 60 + tz_minute) * 60000
255 if '-' is info.tz_sign 262 if '-' is info.tz_sign
256 tz_offset *= -1 263 tz_offset *= -1
257   264  
258 # Compute date 265 # Compute date
259 date = new Date Date.UTC(year, month, day, hour, minute, second, fraction) 266 date = new Date Date.UTC(year, month, day, hour, minute, second, fraction)
260 if tz_offset 267 if tz_offset
261 date.setTime date.getTime() + tz_offset 268 date.setTime date.getTime() - tz_offset
262   269  
263 return date 270 return date
264   271  
265   272  
266 # Repeats the given string a number of times 273 # Repeats the given string a number of times
267 # 274 #
268 # @param [String] str The string to repeat 275 # @param [String] str The string to repeat
269 # @param [Integer] number The number of times to repeat the string 276 # @param [Integer] number The number of times to repeat the string
270 # 277 #
271 # @return [String] The repeated string 278 # @return [String] The repeated string
272 # 279 #
273 @strRepeat: (str, number) -> 280 @strRepeat: (str, number) ->
274 res = '' 281 res = ''
275 i = 0 282 i = 0
276 while i < number 283 while i < number
277 res += str 284 res += str
278 i++ 285 i++
279 return res 286 return res
280   287  
281   288  
282 # Reads the data from the given file path and returns the result as string 289 # Reads the data from the given file path and returns the result as string
283 # 290 #
284 # @param [String] path The path to the file 291 # @param [String] path The path to the file
285 # @param [Function] callback A callback to read file asynchronously (optional) 292 # @param [Function] callback A callback to read file asynchronously (optional)
286 # 293 #
287 # @return [String] The resulting data as string 294 # @return [String] The resulting data as string
288 # 295 #
289 @getStringFromFile: (path, callback = null) -> 296 @getStringFromFile: (path, callback = null) ->
290 xhr = null 297 xhr = null
291 if window? 298 if window?
292 if window.XMLHttpRequest 299 if window.XMLHttpRequest
293 xhr = new XMLHttpRequest() 300 xhr = new XMLHttpRequest()
294 else if window.ActiveXObject 301 else if window.ActiveXObject
295 for name in ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP"] 302 for name in ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP"]
296 try 303 try
297 xhr = new ActiveXObject(name) 304 xhr = new ActiveXObject(name)
298   305  
299 if xhr? 306 if xhr?
300 # Browser 307 # Browser
301 if callback? 308 if callback?
302 # Async 309 # Async
303 xhr.onreadystatechange = -> 310 xhr.onreadystatechange = ->
304 if xhr.readyState is 4 311 if xhr.readyState is 4
305 if xhr.status is 200 or xhr.status is 0 312 if xhr.status is 200 or xhr.status is 0
306 callback(xhr.responseText) 313 callback(xhr.responseText)
307 else 314 else
308 callback(null) 315 callback(null)
309 xhr.open 'GET', path, true 316 xhr.open 'GET', path, true
310 xhr.send null 317 xhr.send null
311 318  
312 else 319 else
313 # Sync 320 # Sync
314 xhr.open 'GET', path, false 321 xhr.open 'GET', path, false
315 xhr.send null 322 xhr.send null
316   323  
317 if xhr.status is 200 or xhr.status == 0 324 if xhr.status is 200 or xhr.status == 0
318 return xhr.responseText 325 return xhr.responseText
319   326  
320 return null 327 return null
321 else 328 else
322 # Node.js-like 329 # Node.js-like
323 req = require 330 req = require
324 fs = req('fs') # Prevent browserify from trying to load 'fs' module 331 fs = req('fs') # Prevent browserify from trying to load 'fs' module
325 if callback? 332 if callback?
326 # Async 333 # Async
327 fs.readFile path, (err, data) -> 334 fs.readFile path, (err, data) ->
328 if err 335 if err
329 callback null 336 callback null
330 else 337 else
331 callback String(data) 338 callback String(data)
332   339  
333 else 340 else
334 # Sync 341 # Sync
335 data = fs.readFileSync path 342 data = fs.readFileSync path
336 if data? 343 if data?
337 return String(data) 344 return String(data)
338 return null 345 return null
339   346  
340   347  
341   348  
342 module.exports = Utils 349 module.exports = Utils
343   350