1 /** 2 * SafeFN is a Javascript implementation of Christopher Smith's php 3 * SafeFN class which was written for Dokuwiki 4 * 5 * @author Myron Turner <turnermm@shaw.ca> 6 * @copyright Myron Turner (C) GPL 2 or greater 7 */ 8 9var SafeFN = { 10 plain: '-./[_0123456789abcdefghijklmnopqrstuvwxyz', // these characters aren't converted 11 pre_indicator: '%', 12 post_indicator:']', 13 14 /** 15 * convert numbers from base 10 to base 36 and base 36 to base 10 16 * 17 * @param string representing an integer or integer num number to be converted 18 * @param integer from base from which to convert 19 * @param integer to base to which to convert 20 * 21 * @return array int an array of unicode codepoints 22 * 23 * @author Myron Turner <turnermm02@shaw.ca> 24 */ 25 26 changeSafeBase: function(num, from, to) { 27 if(isNaN(from) || from < 2 || from > 36 || isNaN(to) || to < 2 || to > 36) { 28 throw (new RangeError("Illegal radix. Radices must be integers between 2 and 36, inclusive.")); 29 } 30 num = parseInt(num, from); 31 if(from == 36) return num; 32 return num.toString(to); 33 }, 34 35 36 /** 37 * convert a UTF8 string into an array of unicode code points 38 * 39 * @param UTF8 string 40 * @return array int an array of unicode codepoints 41 * 42 * @author Myron Turner <turnermm02@shaw.ca> 43 */ 44 45 get_u_array: function(utf8str) { 46 var unicode_array = new Array(); 47 for (var i=0; i<utf8str.length; i++) { 48 unicode_array[i] = utf8str.charCodeAt(i);; 49 } 50 return unicode_array; 51 }, 52 53 54 /** 55 * convert a 'safe_filename' string into an array of unicode codepoints 56 * 57 * @param string safe a filename in 'safe_filename' format 58 * @return array int an array of unicode codepoints 59 * @author Christopher Smith <chris@jalakai.co.uk> 60 * @author Myron Turner<turnermm02@shaw.ca> 61 */ 62 safe_to_unicode: function(safe) { 63 var unicode = new Array(); 64 var regex = new RegExp('(?=[' + this.pre_indicator + '\\' + this.post_indicator + '])'); 65 var split_array = safe.split(regex); 66 var converted = false; 67 68 for (var i = 0; i<split_array.length; i++ ) { 69 var sub = split_array[i]; 70 if (sub.charAt(0) != this.pre_indicator) { // i.e. sub.charAt(0) != '%' 71 var start = converted?1:0; 72 for (j=start; j < sub.length; j++) { 73 unicode.push(sub.charCodeAt(j)); 74 } 75 converted = false; 76 } else if (sub.length==1) { 77 unicode.push(sub.charCodeAt(0)); 78 converted = true; 79 } else { 80 81 unicode.push(32 + this.changeSafeBase(sub.slice(1),36,10)); 82 converted = true; 83 } 84 } 85 86 return unicode; 87 }, 88 89 /** 90 * convert an array of unicode codepoints into 'safe_filename' format 91 * 92 * @param array int $unicode an array of unicode codepoints 93 * @return string the unicode represented in 'safe_filename' format 94 * 95 * @author Christopher Smith <chris@jalakai.co.uk> 96 * @author Myron Turner <turnermm02@shaw.ca> 97 */ 98 unicode_to_safe: function (unicode) { 99 var safe = ''; 100 var converted = false; 101 var plain_str = this.plain + this.post_indicator; 102 103 for (var i=0; i< unicode.length; i++) { 104 codepoint = unicode[i]; 105 var match = ''; 106 if(String.fromCharCode(codepoint) != '\\') { 107 var regex = new RegExp(String.fromCharCode(codepoint)); 108 var match = plain_str.match(regex); 109 } 110 111 if (codepoint < 127 && match) { 112 if (converted) { 113 safe += this.post_indicator; 114 converted = false; 115 } 116 safe += String.fromCharCode(codepoint); 117 118 } else if (codepoint == this.pre_indicator.charCodeAt(0)) { 119 safe += this.pre_indicator; 120 converted = true; 121 } else { 122 safe += this.pre_indicator + this.changeSafeBase((codepoint-32), 10, 36); 123 converted = true; 124 } 125 } 126 if(converted) safe += this.post_indicator; 127 128 return safe; 129 }, 130 131 /** 132 * Convert an UTF-8 string to a safe ASCII String 133 * 134 * 135 * @param string filename a utf8 string, should only include printable characters - not 0x00-0x1f 136 * @return string an encoded representation of filename using only 'safe' ASCII characters 137 * 138 * @author Myron Turner <turnermm02@shaw.ca> 139 */ 140 encode: function(filename) { 141 return this.unicode_to_safe(this.get_u_array(filename)); 142 }, 143 144 /** 145 * decode a 'safe' encoded file name and return a UTF8 string 146 * 147 * @param string filename a 'safe' encoded ASCII string, 148 * @return string decoded utf8 string 149 * 150 * @author Myron Turner <turnermm02@shaw.ca> 151 */ 152 153 decode: function (filename) { 154 var unic = this.safe_to_unicode(filename); 155 156 // convert unicode code points to utf8 157 var str = new Array(); 158 for (var i=0; i < unic.length; i++) { 159 str[i] = this.code2utf(unic[i]); 160 } 161 // return the decoded string 162 return this.utf8Decode(str.join('')); 163 }, 164 165/* UTF8 encoding/decoding functions 166 * Copyright (c) 2006 by Ali Farhadi. 167 * released under the terms of the Gnu Public License. 168 * see the GPL for details. 169 * 170 * Email: ali[at]farhadi[dot]ir 171 * Website: http://farhadi.ir/ 172 */ 173 174//an alias of String.fromCharCode 175chr: function (code) 176{ 177 return String.fromCharCode(code); 178}, 179 180//returns utf8 encoded charachter of a unicode value. 181//code must be a number indicating the Unicode value. 182//returned value is a string between 1 and 4 charachters. 183code2utf: function (code) 184{ 185 if (code < 128) return this.chr(code); 186 if (code < 2048) return this.chr(192+(code>>6)) + this.chr(128+(code&63)); 187 if (code < 65536) return this.chr(224+(code>>12)) + this.chr(128+((code>>6)&63)) + this.chr(128+(code&63)); 188 if (code < 2097152) return this.chr(240+(code>>18)) + this.chr(128+((code>>12)&63)) + this.chr(128+((code>>6)&63)) + this.chr(128+(code&63)); 189}, 190 191//it is a private function for internal use in utf8Decode function 192_utf8Decode: function (utf8str) 193{ 194 195 var str = new Array(); 196 var code,code2,code3,code4,j = 0; 197 for (var i=0; i<utf8str.length; ) { 198 code = utf8str.charCodeAt(i++); 199 200 201 if (code > 127) code2 = utf8str.charCodeAt(i++); 202 if (code > 223) code3 = utf8str.charCodeAt(i++); 203 if (code > 239) code4 = utf8str.charCodeAt(i++); 204 205 if (code < 128) str[j++]= this.chr(code); 206 else if (code < 224) str[j++] = this.chr(((code-192)<<6) + (code2-128)); 207 else if (code < 240) str[j++] = this.chr(((code-224)<<12) + ((code2-128)<<6) + (code3-128)); 208 else str[j++] = this.chr(((code-240)<<18) + ((code2-128)<<12) + ((code3-128)<<6) + (code4-128)); 209 210 } 211 return str.join(''); 212}, 213 214//Decodes a UTF8 formated string 215utf8Decode: function (utf8str) 216{ 217 var str = new Array(); 218 var pos = 0; 219 var tmpStr = ''; 220 var j=0; 221 while ((pos = utf8str.search(/[^\x00-\x7F]/)) != -1) { 222 tmpStr = utf8str.match(/([^\x00-\x7F]+[\x00-\x7F]{0,10})+/)[0]; 223 str[j++]= utf8str.substr(0, pos) + this._utf8Decode(tmpStr); 224 utf8str = utf8str.substr(pos + tmpStr.length); 225 } 226 227 str[j++] = utf8str; 228 return str.join(''); 229} 230 231}; 232 233function SafeFN_encode(filename) { 234 return SafeFN.encode(filename); 235} 236 237function SafeFN_decode(filename) { 238 return SafeFN.decode(filename); 239} 240 241 242function dwikiUTF8_encodeFN(file, encoding){ 243 244 if(encoding == 'utf-8') return file; 245 246 if(file.match(/^[a-zA-Z0-9\/_\-\.%\]]+$/)){ 247 return file; 248 } 249 250 if(encoding == 'safe'){ 251 return SafeFN_encode(file); 252 } 253 254 file = encodeURIComponent(file); 255 file = file.replace(/%2F/g,'/'); 256 return file; 257} 258 259 260function dwikiUTF8_decodeFN(file, encoding){ 261 262 if(encoding == 'utf-8') return file; 263 264 if(encoding == 'safe'){ 265 return SafeFN_decode(file); 266 } 267 268 return decodeURIComponent(file); 269} 270