joliclic
1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is Khromaxul 15 * 16 * The Initial Developer of the Original Code is 17 * Nicolas Martin. 18 * Portions created by the Initial Developer are Copyright (C) 2009 19 * the Initial Developer. All Rights Reserved. 20 * 21 * Contributor(s): 22 * Nicolas Martin <joliclic@gmail.com> 23 * 24 * Alternatively, the contents of this file may be used under the terms of 25 * either the GNU General Public License Version 2 or later (the "GPL"), or 26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 27 * in which case the provisions of the GPL or the LGPL are applicable instead 28 * of those above. If you wish to allow use of your version of this file only 29 * under the terms of either the GPL or the LGPL, and not to allow others to 30 * use your version of this file under the terms of the MPL, indicate your 31 * decision by deleting the provisions above and replace them with the notice 32 * and other provisions required by the GPL or the LGPL. If you do not delete 33 * the provisions above, a recipient may use your version of this file under 34 * the terms of any one of the MPL, the GPL or the LGPL. 35 * 36 * ***** END LICENSE BLOCK ***** */ 37 38 if (!Cc) var Cc = Components.classes; 39 if (!Ci) var Ci = Components.interfaces; 40 41 if (!kh) var kh = {}; 42 43 /** 44 * @namespace 45 */ 46 kh.file = { 47 _ios: null, 48 /** 49 * @static 50 * @returns {nsIIOService} readonly 51 */ 52 get ios() { 53 if (!this._ios) 54 this._ios = Cc["@mozilla.org/network/io-service;1"] 55 .getService(Ci.nsIIOService); 56 return this._ios; 57 }, 58 59 /** 60 * @constructor 61 * @param {String} aName name of the palette, not the filename 62 * @param {String} aSpec 63 */ 64 PaletteInfo: function(aName, aSpec) { 65 this.name = aName; 66 this.spec = aSpec; 67 }, 68 69 /** 70 * @param {String} aSpec 71 * @returns {nsIFile} 72 */ 73 getFile: function kh_file_getFile(aSpec) { 74 var uri = this.ios.newURI(aSpec, "", null); 75 var file = uri.QueryInterface(Ci.nsIFileURL).file; 76 77 return file; 78 }, 79 80 /** 81 * @param {nsiFile} aFile 82 * @returns {String} spec of the file 83 */ 84 getSpecOfFile: function kh_file_getSpecOfFile(aFile) { 85 return this.ios.newFileURI(aFile).spec; 86 }, 87 88 /** 89 * this is synchronous, it will block the main UI. 90 * But we use it to get chrome files and local xml files 91 * 92 * @param {String} aSpec 93 * @param {Object} aObj. An obj = {} should be passed by reference, 94 * aObj.value will contain the xml document of the request file. 95 * @returns {Boolean} inform that the request is done. 96 */ 97 getSyncRemoteDoc: function kh_file_getRemoteFile(aSpec, aObj) { 98 var req = new XMLHttpRequest(); 99 req.open("GET", aSpec, false); 100 101 req.send(null); 102 103 if(req.status == 0 || req.status == 200) { 104 if (aObj !== undefined) { 105 //try { 106 aObj.value = req.responseXML; 107 108 return true; 109 //} catch (e) { 110 // //alert(e); 111 //} 112 } 113 } 114 return false; 115 }, 116 117 /** 118 * Get asynchronously a remote XMLDocument. 119 * 120 * @param {String} aSpec 121 * @param {Object} aObj. An obj = {} should be passed by reference, 122 * aObj.value will contain the xml document of the request file. 123 * @param {Function} aCallbackFn. A function to execute after that the 124 * document has been received. 125 * @returns {void} 126 */ 127 getAsyncRemoteDoc: 128 function kh_file_getAsyncRemoteDoc(aSpec, aObj, aCallbackFn) { 129 var req = new XMLHttpRequest(); 130 req.open("GET", aSpec, true); 131 132 req.onreadystatechange = function (aEvt) { 133 if (req.readyState != 4 || req.status != 200) 134 return; 135 if (aObj !== undefined) 136 aObj.value = req.responseXML; 137 if (aCallbackFn instanceof Function) 138 aCallbackFn(); 139 }; 140 141 req.send(null); 142 }, 143 144 /** 145 * @returns {nsIFile} the directory of the personal palettes. 146 */ 147 getPersonalPalettesDir: function kh_file_getPersonalPalettesDir() { 148 var file = this.getKHDir(); 149 file.append("palettes"); 150 151 if (!file.exists()) { 152 file.create(Ci.nsIFile.DIRECTORY_TYPE, 0755); 153 } 154 155 return file; 156 }, 157 158 /** 159 * Return a special file determined by a name. 160 * <br /> for a list of available name, 161 * see http://mxr.mozilla.org/seamonkey/source/xpcom/io/nsDirectoryServiceDefs.h 162 * 163 * @param {String} aName 164 * @returns {nsIFile} 165 */ 166 getSpecialFile: function kh_file_getSpecialFile(aName) { 167 const dirService = "@mozilla.org/file/directory_service;1"; 168 return Cc[dirService].getService(Ci.nsIProperties) 169 .get(aName, Ci.nsIFile); 170 }, 171 172 /** 173 * @returns {nsIFile} the current profile directory 174 */ 175 getProfileDir: function kh_file_getProfileDir() { 176 return this.getSpecialFile("ProfD"); 177 }, 178 179 /** 180 * @returns {nsIFile} the khromaxul directory in the current profile 181 */ 182 getKHDir: function kh_file_getKHDir() { 183 var file = this.getProfileDir(); 184 file.append("khromaxul"); 185 if (!file.exists()) { 186 file.create(Ci.nsIFile.DIRECTORY_TYPE, 0755); 187 } 188 189 return file; 190 }, 191 192 /** 193 * @param {nsIFile} aDir 194 * @returns {PaletteInfo[]} ({name, spec}), containing all the palette 195 * files contained in aDir. name refer to the name of the palette 196 * (not the filename), and spec the spec of the file. 197 */ 198 listPaletteFiles: function kh_file_listPaletteFiles(aDir) { 199 var ios = Cc["@mozilla.org/network/io-service;1"] 200 .getService(Ci.nsIIOService); 201 202 var list = []; 203 204 if (!(aDir.exists() && aDir.isReadable() && aDir.isDirectory())) 205 return list; 206 207 var entries = aDir.directoryEntries; 208 while(entries.hasMoreElements()) { 209 var entry = entries.getNext(); 210 entry.QueryInterface(Ci.nsIFile); 211 212 var filename = entry.leafName; 213 if (filename.substr(-4, 4) != ".gpl") 214 continue; 215 216 var paletteName = {}; 217 if (!this.sniffGplPalette(entry, paletteName)) 218 continue; 219 220 var pname = paletteName.value; 221 if (pname == "") { 222 // todo remove illegal character (is there any?) 223 pname = filename.substr(0, filename.length - 4); 224 } 225 226 var spec = ios.newFileURI(entry).spec 227 var item = new this.PaletteInfo(pname, spec); 228 list.push(item); 229 } 230 231 return list.sort(this.comparePaletteName); 232 }, 233 234 /** 235 * Used to sort an array of palette by their names 236 * 237 * @param {PaletteInfo} aPal ( PaletteInfo = {name, spec} ) 238 * @param {PaletteInfo} bPal 239 * @returns {Int} 240 */ 241 comparePaletteName: function kh_file_comparePaletteName(aPal, bPal) { 242 if (aPal.name < bPal.name) 243 return -1; 244 if (aPal.name > bPal.name) 245 return 1; 246 return 0; 247 }, 248 249 /** 250 * @param {nsIFile} aFile 251 * @param {Object} aNameOb. Optional, if passed by reference, aNameObj.value 252 * will contains the internal palette name. 253 * @returns {Boolean} True if the file is a Gimp Palette. 254 */ 255 sniffGplPalette: function kh_file_sniffGplPalette(aFile, aNameObj) { 256 var data = ""; 257 var fstream = Cc["@mozilla.org/network/file-input-stream;1"]. 258 createInstance(Ci.nsIFileInputStream); 259 fstream.init(aFile, -1, 0, 0); 260 fstream.QueryInterface(Ci.nsILineInputStream); 261 262 var line = {}, hasmore, data; 263 264 hasmore = fstream.readLine(line); 265 data = line.value; 266 if (data.substr(0, 12) != "GIMP Palette"/* || !hasmore*/) { 267 fstream.close(); 268 return false; 269 } 270 271 // no object was passed as parameter 272 if (aNameObj === undefined) { 273 fstream.close(); 274 return true; 275 } 276 277 // old Gimp Palette format (without name) 278 aNameObj.value = ""; 279 280 if (hasmore) { 281 hasmore = fstream.readLine(line); 282 data = line.value; 283 284 if (data.substr(0, 6) == "Name: ") { 285 // TODO: UTF-8 conversion if necessary 286 aNameObj.value = data.substr(6); 287 } 288 } 289 290 fstream.close(); 291 292 return true; 293 }, 294 295 /** 296 * @returns {XMLDocument} 297 */ 298 createEmptyPalette: function kh_file_createEmptyPalette() { 299 var doc = document.implementation.createDocument("","palette", null); 300 var root = doc.documentElement; 301 var nameElt = doc.createElement("name"); 302 root.appendChild(nameElt); 303 var columnsElt = doc.createElement("columns"); 304 root.appendChild(columnsElt); 305 var commentsElt = doc.createElement("comments"); 306 root.appendChild(commentsElt); 307 var colorsElt = doc.createElement("colors"); 308 root.appendChild(colorsElt); 309 310 return doc; 311 }, 312 313 /** 314 * @param {String} aSpec 315 * @param {Object} aInfoObj optiona, if passed by reference, aInfoObj will 316 * contains a boolean that indicate if the targetted file is writable. 317 * @return {XMLDocument} 318 */ 319 openPaletteBySpec: function kh_file_openPaletteBySpec(aSpec, aInfoObj) { 320 var file = kh.file.getFile(aSpec); 321 if (!(file.exists() && file.isReadable() && file.isFile())) 322 return doc; 323 324 if (aInfoObj !== undefined) { 325 aInfoObj.value = file.isWritable(); 326 } 327 328 return this.openPaletteFile(file); 329 }, 330 331 /** 332 * @param {nsIFile} aFile 333 * @return {XMLDocument} 334 */ 335 openPaletteFile: function kh_file_openPaletteFile(aFile) { 336 var doc = kh.file.createEmptyPalette(); 337 338 var nameElt = doc.getElementsByTagName("name")[0]; 339 var specElt = doc.getElementsByTagName("spec")[0]; 340 var columnsElt = doc.getElementsByTagName("columns")[0]; 341 var commentsElt = doc.getElementsByTagName("comments")[0]; 342 var colorsElt = doc.getElementsByTagName("colors")[0]; 343 344 // open an input stream from file 345 var istream = Cc["@mozilla.org/network/file-input-stream;1"]. 346 createInstance(Ci.nsIFileInputStream); 347 istream.init(aFile, 0x01, 0444, 0); 348 istream.QueryInterface(Ci.nsILineInputStream); 349 350 // read line 351 var line = {}, hasmore, data; 352 353 // Gimp Palette Magic Header 354 hasmore = istream.readLine(line); 355 data = line.value; 356 if (data.substr(0, 12) != "GIMP Palette" || !hasmore) 357 return doc; 358 359 var defaultColumns = 16; 360 var maxColumns = 64; //internally Gimp use 256, it's 64 in the gimp UI. 361 362 hasmore = istream.readLine(line); 363 data = line.value; 364 if (data.substr(0, 6) == "Name: ") { 365 // TODO: UTF-8 conversion if necessary 366 nameElt.appendChild(doc.createTextNode(data.substr(6))); 367 368 if (!hasmore) 369 return doc; 370 371 hasmore = istream.readLine(line); 372 data = line.value; 373 if (data.substr(0, 9) == "Columns: ") { 374 var nb = parseInt(data.substr(9)); 375 if (isNaN(nb) || nb < 1 || nb > 256) { 376 nb = defaultColumns; 377 } 378 columnsElt.appendChild(doc.createTextNode(nb)); 379 380 if (!hasmore) 381 return doc; 382 383 hasmore = istream.readLine(line); 384 data = line.value; 385 } else { 386 columnsElt.appendChild(doc.createTextNode(defaultColumns)); 387 } 388 } else { 389 // old format 390 nameElt.appendChild(doc.createTextNode(file.leafName)); 391 columnsElt.appendChild(doc.createTextNode(defaultColumns)); 392 } 393 394 if (!hasmore) 395 return doc; 396 397 if (data.substr(0, 1) == "#") { 398 while (hasmore && data.substr(0, 1) == "#") { 399 var txt = data.substr(1); 400 var cm = doc.createElement("comment"); 401 cm.appendChild(doc.createTextNode(txt)); 402 commentsElt.appendChild(cm); 403 404 hasmore = istream.readLine(line); 405 data = line.value; 406 } 407 } 408 409 if (!hasmore) 410 return doc; 411 412 while(hasmore || data != "") { 413 if (data.substr(0, 1) == "#") 414 continue; 415 416 var colorData = /\s*(\d{1,3})\s*(\d{1,3})\s*(\d{1,3})\s*([\s\S]*)/ 417 .exec(data); 418 419 if (!colorData) 420 continue; 421 422 var r = parseInt(colorData[1]); 423 var g = parseInt(colorData[2]); 424 var b = parseInt(colorData[3]); 425 var colorName = colorData[4]; 426 427 var color = doc.createElement("color"); 428 color.setAttribute("r", r); 429 color.setAttribute("g", g); 430 color.setAttribute("b", b); 431 color.setAttribute("name", colorName); 432 colorsElt.appendChild(color); 433 434 hasmore = istream.readLine(line); 435 data = line.value; 436 } 437 438 istream.close(); 439 440 return doc; 441 }, 442 443 /** 444 * Return a filename not used in a directory. 445 * 446 * @param {String} aName 447 * @param {nsiFile} aDir 448 * @param {String} aExt 449 * 450 * @returns {String} the same filename if doesn't exist, otherwise append a integer 451 */ 452 ensureFileName: function kh_file_ensureFileName(aName, aDir, aExt) { 453 var filename = aName.replace(/[^\w_.-]/g, ""); 454 if (filename == "") 455 filename = "kh_palette"; 456 457 if (aExt === undefined) 458 aExt = ""; 459 460 var dir = aDir.clone(); 461 var clone = aDir.clone() 462 463 clone.append(filename + aExt); 464 var exist = clone.exists(); 465 if (!exist) 466 return filename + aExt; 467 468 var othername; 469 var i = 0; 470 while (exist) { 471 othername = "" + filename + i; 472 clone = aDir.clone(); 473 clone.append(othername + aExt); 474 exist = clone.exists(); 475 i++; 476 } 477 478 return othername + aExt; 479 }, 480 481 /** 482 * Convert a XML palette to a Gimp Palette format 483 * 484 * @param {XMLDocument} aDoc 485 * @param {String} aName optional 486 * @param {Int} aColumns optional 487 * @param {String[]} aComments optional 488 * 489 * @returns {String} 490 */ 491 xmlPaletteToGimp: 492 function kh_file_xmlPaletteToGimp(aDoc, aName, aColumns, aComments) { 493 if (aName === undefined || aName === null) { 494 aName = aDoc.getElementsByTagName("name")[0].textContent; 495 } 496 497 if (aColumns === undefined || aColumns === null) { 498 aColumns = aDoc.getElementsByTagName("columns")[0].textContent; 499 } 500 501 var txt = "GIMP Palette\n" + 502 "Name: " + aName + "\n" + 503 "Columns: " + aColumns + "\n"; 504 505 if (aComments === undefined || aComments === null) { 506 aComments = []; 507 var comments = aDoc.getElementsByTagName("comments")[0] 508 .getElementsByTagName("comment"); 509 for (let i = 0, l = comments.length; i < l; i++) { 510 aComments.push(comments[i].textContent); 511 } 512 } else { 513 aComments = aComments.split("\n"); 514 } 515 // we want a "#" bfore the list of colors 516 if (aComments.length > 0 && aComments[aComments.length -1] != "") 517 aComments.push(""); 518 519 var commentsTxt = ""; 520 for (let i = 0, l = aComments.length; i < l; i++) { 521 let comment = aComments[i]; 522 if (comment.length > 0 && comment[0] != " ") 523 comment = " " + comment; 524 commentsTxt += "#" + comment + "\n"; 525 } 526 txt += (commentsTxt == "") ? "#\n" : commentsTxt; 527 528 var colors = aDoc.getElementsByTagName("color"); 529 for (let i = 0, l = colors.length; i < l; i++) { 530 let color = colors[i]; 531 let r = "" + color.getAttribute("r"); 532 if (r.length == 1) { 533 r = " " + r; 534 } else if (r.length == 2) { 535 r = " " + r; 536 } 537 let g = "" + color.getAttribute("g"); 538 if (g.length == 1) { 539 g = " " + g; 540 } else if (g.length == 2) { 541 g = " " + g; 542 } 543 let b = "" + color.getAttribute("b"); 544 if (b.length == 1) { 545 b = " " + b; 546 } else if (b.length == 2) { 547 b = " " + b; 548 } 549 let colorName = color.getAttribute("name"); 550 551 txt += r + " " + g + " " + b; 552 553 if (colorName && colorName != "") 554 txt += " " + colorName; 555 556 txt += "\n"; 557 } 558 559 return txt; 560 }, 561 562 /** 563 * write to a nsIFile. The file must exists and be writable, otherwise it 564 * will not be created, and the method will just returns false. 565 * 566 * @param {nsIFile} aFile 567 * @param {String} aStr 568 * @param {Int} aFlags default: 0x02 | 0x08 | 0x20 569 * @param {Int} aPerm default: 0644 570 * 571 * @returns {Boolean} true if success 572 */ 573 writeFile: function kh_file_writeFile(aFile, aStr, aFlags, aPerm) { 574 if (!aFile.exists() || ! aFile.isWritable()) 575 return false; 576 577 if (typeof(aFlags) == "undefined") 578 // default = overwrite. Use 0x02 | 0x10 for appending 579 aFlags = 0x02 | 0x08 | 0x20; 580 581 if (typeof(aPerm) == "undefined") 582 aPerm = 0644; 583 584 var foStream = Cc["@mozilla.org/network/file-output-stream;1"]. 585 createInstance(Ci.nsIFileOutputStream); 586 foStream.init(aFile, aFlags, aPerm, 0); 587 foStream.write(aStr, aStr.length); 588 foStream.close(); 589 590 return true; 591 }, 592 593 /** 594 * remove illegal characters in a filename on various plateform. 595 * If the result is empty, aDefault is returned. 596 * <pre> 597 * white characters at beginning and end are removed 598 * \ / | " * : ? < > ~ \n \r are replaced by _ 599 * \0 \b \t \v \f \r \n are removed 600 * If the filename begins by ., it's remove too (hidden files on nix) 601 * </pre> 602 * 603 * see function validateFileName(aFileName) in 604 * toolkit/content/contentAreaUtils.js 605 * 606 * @param {String} aStr 607 * @param {String} aDefault 608 * 609 * @returns {String} 610 */ 611 escapeFileName: function kh_file_escapeFileName(aStr, aDefault) { 612 if (aDefault === undefined) 613 aDefault = "myfile"; 614 615 var re = /^\s+/; 616 var name = aStr.replace(re, ""); 617 618 re = /\s+$/; 619 name = name.replace(re, ""); 620 621 re = /[\\\/\|\"\*\:\?\<\>~]+/g; 622 name = name.replace(re, "_"); 623 624 re = /[\0\b\t\v\f\r\n]+/g; 625 name = name.replace(re, ""); 626 627 re = /^\.+/; 628 name = aStr.replace(re, ""); 629 630 if (name == "") 631 name = aDefault; 632 633 return name; 634 } 635 }; 636