Changeset 688

Show
Ignore:
Timestamp:
03/15/08 14:35:12 (9 months ago)
Author:
ma..@jesperkristensen.dk
Message:

merge in config-service branch

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/chrome/chromeFiles/content/browser.js

    r675 r688  
    2828  this.menuCommanders = []; 
    2929  this.currentMenuCommander = null; 
    30  
    31   GM_updateVersion(); 
    3230 
    3331  GM_listen(window, "load", GM_hitch(this, "chromeLoad")); 
     
    233231  this.scriptDownloader_ = scriptDownloader; 
    234232 
    235   var ioSvc = Components.classes["@mozilla.org/network/io-service;1"] 
    236                         .getService(Components.interfaces.nsIIOService); 
    237   var uri = ioSvc.newFileURI(scriptDownloader.script.file); 
    238  
    239   var tab = this.tabBrowser.addTab(uri.spec); 
     233  var tab = this.tabBrowser.addTab(scriptDownloader.script.previewURL); 
    240234  var browser = this.tabBrowser.getBrowserForTab(tab); 
    241235 
     
    265259 
    266260GM_BrowserUI.installScript = function(script){ 
    267   var config = new Config(); 
    268   config.load(); 
    269   config.install(script); 
     261  GM_getConfig().install(script); 
    270262  this.showHorrayMessage(script.name); 
    271263}; 
     
    449441 
    450442  function scriptsMatching(urls) { 
    451     var matching = []; 
    452     for (var i = 0, script = null; script = config.scripts[i]; i++) { 
    453       for (var j = 0; j < urls.length; j++) { 
    454         if (GM_scriptMatchesUrl(script, urls[j])) { 
    455           matching.push(script); 
    456           break; 
    457         } 
     443 
     444    function testMatchURLs(script) { 
     445 
     446      function testMatchURL(url) { 
     447        return script.matchesURL(url); 
    458448      } 
    459     } 
    460     return matching; 
     449 
     450      return urls.some(testMatchURL); 
     451    } 
     452 
     453    return GM_getConfig().getMatchingScripts(testMatchURLs); 
    461454  } 
    462455 
     
    464457    var mi = document.createElement("menuitem"); 
    465458    mi.setAttribute("label", script.name); 
    466     mi.setAttribute("value", i)
     459    mi.script = script
    467460    mi.setAttribute("type", "checkbox"); 
    468461    mi.setAttribute("checked", script.enabled.toString()); 
     
    470463  } 
    471464 
    472   var config = new Config(); 
    473   config.load(); 
    474465  var popup = aEvent.target; 
    475466  var tail = document.getElementById("gm-status-no-scripts-sep"); 
     
    480471  // remove all the scripts from the list 
    481472  for (var i = popup.childNodes.length - 1; i >= 0; i--) { 
    482     if (popup.childNodes[i].hasAttribute("value")) { 
     473    if (popup.childNodes[i].script) { 
    483474      popup.removeChild(popup.childNodes[i]); 
    484475    } 
     
    520511function GM_popupClicked(aEvent) { 
    521512  if (aEvent.button == 0 || aEvent.button == 2) { 
    522     var config = new Config(); 
    523     config.load(); 
    524     var scriptNum=aEvent.target.value; 
    525     if (!config.scripts[scriptNum]) return; 
    526  
    527     if (aEvent.button == 0) { 
    528       // left-click: toggle enabled state 
    529       config.scripts[scriptNum].enabled=!config.scripts[scriptNum].enabled; 
    530       config.save(); 
    531     } else { 
    532       // right-click: open in editor 
    533       openInEditor(config.scripts[scriptNum]); 
    534     } 
     513    var script = aEvent.target.script; 
     514    if (!script) return; 
     515 
     516    if (aEvent.button == 0) // left-click: toggle enabled state 
     517      script.enabled =! script.enabled; 
     518    else // right-click: open in editor 
     519      openInEditor(script); 
    535520 
    536521    closeMenus(aEvent.target); 
     
    668653 
    669654GM_BrowserUI.manageMenuItemClicked = function() { 
    670    window.openDialog("chrome://greasemonkey/content/manage.xul", "manager", 
    671     "resizable,centerscreen,modal"); 
     655   GM_openUserScriptManager(); 
    672656}; 
    673657 
  • trunk/src/chrome/chromeFiles/content/browser.xul

    r590 r688  
    1111  <script type="application/x-javascript" src="chrome://greasemonkey/content/prefmanager.js" /> 
    1212  <script type="application/x-javascript" src="chrome://greasemonkey/content/convert2RegExp.js" /> 
    13   <script type="application/x-javascript" src="chrome://greasemonkey/content/versioning.js" /> 
    1413  <script type="application/x-javascript" src="chrome://greasemonkey/content/menucommander.js" /> 
    1514  <script type="application/x-javascript" src="chrome://greasemonkey/content/xmlhttprequester.js" /> 
     
    1716  <script type="application/x-javascript" src="chrome://greasemonkey/content/dochandler.js" /> 
    1817  <script type="application/x-javascript" src="chrome://greasemonkey/content/scriptdownloader.js" /> 
    19   <script type="application/x-javascript" src="chrome://greasemonkey/content/config.js" /> 
    2018  <script type="application/x-javascript" src="chrome://greasemonkey/content/browser.js" /> 
    2119  <script type="application/x-javascript" src="chrome://greasemonkey/content/accelimation.js" /> 
  • trunk/src/chrome/chromeFiles/content/config.js

    r548 r688  
     1// In this file protected properties (prefixed with an underscore) may be 
     2// used anywhere within this file and versioning.js 
     3 
    14function Config() { 
    2   this.onload = null; 
    3   this.scripts = null; 
    4   this.configFile = getConfigFile(); 
    5 }; 
    6  
    7 Config.prototype.find = function(namespace, name) { 
    8   namespace = namespace.toLowerCase(); 
    9   name = name.toLowerCase(); 
    10  
    11   for (var i = 0, script = null; (script = this.scripts[i]); i++) { 
    12     if (script.namespace.toLowerCase() == namespace && script.name.toLowerCase() == name) { 
    13       return i; 
    14     } 
     5  this._scripts = null; 
     6  this._configFile = this._scriptDir; 
     7  this._configFile.append("config.xml"); 
     8 
     9  this._observers = []; 
     10 
     11  this._updateVersion(); 
     12  this._load(); 
     13}; 
     14 
     15Config.prototype = { 
     16  addObserver: function(observer, script) { 
     17    var observers = script ? script._observers : this._observers; 
     18    observers.push(observer); 
     19  }, 
     20 
     21  removeObserver: function(observer, script) { 
     22    var observers = script ? script._observers : this._observers; 
     23    var index = observers.indexOf(observer); 
     24    if (index == -1) throw new Error("Observer not found"); 
     25    observers.splice(index, 1); 
     26  }, 
     27 
     28  _notifyObservers: function(script, event, data) { 
     29    var observers = this._observers.concat(script._observers); 
     30    for (var i = 0, observer; observer = observers[i]; i++) { 
     31      observer.notifyEvent(script, event, data); 
     32    } 
     33  }, 
     34 
     35  _changed: function(script, event, data) { 
     36    this._save(); 
     37    this._notifyObservers(script, event, data); 
     38  }, 
     39 
     40  installIsUpdate: function(script) { 
     41    return this._find(script) > -1; 
     42  }, 
     43 
     44  _find: function(aScript) { 
     45    namespace = aScript._namespace.toLowerCase(); 
     46    name = aScript._name.toLowerCase(); 
     47 
     48    for (var i = 0, script; script = this._scripts[i]; i++) { 
     49      if (script._namespace.toLowerCase() == namespace  
     50        && script._name.toLowerCase() == name) { 
     51        return i; 
     52      } 
     53    } 
     54 
     55    return -1; 
     56  }, 
     57 
     58  _load: function() { 
     59    var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"] 
     60                              .createInstance(Components.interfaces.nsIDOMParser); 
     61 
     62    var configContents = getContents(this._configFile); 
     63    var doc = domParser.parseFromString(configContents, "text/xml"); 
     64    var nodes = doc.evaluate("/UserScriptConfig/Script", doc, null, 0, null); 
     65 
     66    this._scripts = []; 
     67 
     68    for (var node = null; node = nodes.iterateNext(); ) { 
     69      var script = new Script(this); 
     70 
     71      for (var i = 0, childNode; childNode = node.childNodes[i]; i++) { 
     72        switch (childNode.nodeName) { 
     73        case "Include": 
     74          script._includes.push(childNode.firstChild.nodeValue); 
     75          break; 
     76        case "Exclude": 
     77          script._excludes.push(childNode.firstChild.nodeValue); 
     78          break; 
     79        case "Require": 
     80          var scriptRequire = new ScriptRequire(script); 
     81          scriptRequire._filename = childNode.getAttribute("filename"); 
     82          script._requires.push(scriptRequire); 
     83          break; 
     84        case "Resource": 
     85          var scriptResource = new ScriptResource(script); 
     86          scriptResource._name = childNode.getAttribute("name"); 
     87          scriptResource._filename = childNode.getAttribute("filename"); 
     88          scriptResource._mimetype = childNode.getAttribute("mimetype"); 
     89          scriptResource._charset = childNode.getAttribute("charset"); 
     90          script._resources.push(scriptResource); 
     91          break; 
     92        } 
     93      } 
     94 
     95      script._filename = node.getAttribute("filename"); 
     96      script._name = node.getAttribute("name"); 
     97      script._namespace = node.getAttribute("namespace"); 
     98      script._description = node.getAttribute("description"); 
     99      script._enabled = node.getAttribute("enabled") == true.toString(); 
     100      script._basedir = node.getAttribute("basedir") || "."; 
     101 
     102      this._scripts.push(script); 
     103    } 
     104  }, 
     105 
     106  _save: function() { 
     107    var doc = Components.classes["@mozilla.org/xmlextras/domparser;1"] 
     108      .createInstance(Components.interfaces.nsIDOMParser) 
     109      .parseFromString("<UserScriptConfig></UserScriptConfig>", "text/xml"); 
     110 
     111    for (var i = 0, scriptObj; scriptObj = this._scripts[i]; i++) { 
     112      var scriptNode = doc.createElement("Script"); 
     113 
     114      for (var j = 0; j < scriptObj._includes.length; j++) { 
     115        var includeNode = doc.createElement("Include"); 
     116        includeNode.appendChild(doc.createTextNode(scriptObj._includes[j])); 
     117        scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
     118        scriptNode.appendChild(includeNode); 
     119      } 
     120 
     121      for (var j = 0; j < scriptObj._excludes.length; j++) { 
     122        var excludeNode = doc.createElement("Exclude"); 
     123        excludeNode.appendChild(doc.createTextNode(scriptObj._excludes[j])); 
     124        scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
     125        scriptNode.appendChild(excludeNode); 
     126      } 
     127 
     128      for (var j = 0; j < scriptObj._requires.length; j++) { 
     129        var req = scriptObj._requires[j]; 
     130        var resourceNode = doc.createElement("Require"); 
     131 
     132        resourceNode.setAttribute("filename", req._filename); 
     133 
     134        scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
     135        scriptNode.appendChild(resourceNode); 
     136      } 
     137 
     138      for (var j = 0; j < scriptObj._resources.length; j++) { 
     139        var imp = scriptObj._resources[j]; 
     140        var resourceNode = doc.createElement("Resource"); 
     141 
     142        resourceNode.setAttribute("name", imp._name); 
     143        resourceNode.setAttribute("filename", imp._filename); 
     144        resourceNode.setAttribute("mimetype", imp._mimetype); 
     145        if (imp._charset) { 
     146          resourceNode.setAttribute("charset", imp._charset); 
     147        } 
     148 
     149        scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
     150        scriptNode.appendChild(resourceNode); 
     151      } 
     152 
     153      scriptNode.appendChild(doc.createTextNode("\n\t")); 
     154 
     155      scriptNode.setAttribute("filename", scriptObj._filename); 
     156      scriptNode.setAttribute("name", scriptObj._name); 
     157      scriptNode.setAttribute("namespace", scriptObj._namespace); 
     158      scriptNode.setAttribute("description", scriptObj._description); 
     159      scriptNode.setAttribute("enabled", scriptObj._enabled); 
     160      scriptNode.setAttribute("basedir", scriptObj._basedir); 
     161 
     162      doc.firstChild.appendChild(doc.createTextNode("\n\t")); 
     163      doc.firstChild.appendChild(scriptNode); 
     164    } 
     165 
     166    doc.firstChild.appendChild(doc.createTextNode("\n")); 
     167 
     168    var configStream = getWriteStream(this._configFile); 
     169    Components.classes["@mozilla.org/xmlextras/xmlserializer;1"] 
     170      .createInstance(Components.interfaces.nsIDOMSerializer) 
     171      .serializeToStream(doc, configStream, "utf-8"); 
     172    configStream.close(); 
     173  }, 
     174 
     175  parse: function(source, uri) { 
     176    var ioservice = Components.classes["@mozilla.org/network/io-service;1"] 
     177                              .getService(Components.interfaces.nsIIOService); 
     178 
     179    var script = new Script(this); 
     180    script._downloadURL = uri.spec; 
     181    script._enabled = true; 
     182 
     183    // read one line at a time looking for start meta delimiter or EOF 
     184    var lines = source.match(/.+/g); 
     185    var lnIdx = 0; 
     186    var result = {}; 
     187    var foundMeta = false; 
     188 
     189    while ((result = lines[lnIdx++])) { 
     190      if (result.indexOf("// ==UserScript==") == 0) { 
     191        foundMeta = true; 
     192        break; 
     193      } 
     194    } 
     195 
     196    // gather up meta lines 
     197    if (foundMeta) { 
     198      // used for duplicate resource name detection 
     199      var previousResourceNames = {}; 
     200 
     201      while ((result = lines[lnIdx++])) { 
     202        if (result.indexOf("// ==/UserScript==") == 0) { 
     203          break; 
     204        } 
     205 
     206        var match = result.match(/\/\/ \@(\S+)\s+([^\n]+)/); 
     207        if (match != null) { 
     208          switch (match[1]) { 
     209            case "name": 
     210            case "namespace": 
     211            case "description": 
     212              script["_" + match[1]] = match[2]; 
     213              break; 
     214            case "include": 
     215              script._includes.push(match[2]); 
     216              break; 
     217            case "exclude": 
     218              script._excludes.push(match[2]); 
     219              break; 
     220            case "require": 
     221              var reqUri = ioservice.newURI(match[2], null, uri); 
     222              var scriptRequire = new ScriptRequire(script); 
     223              scriptRequire._downloadURL = reqUri.spec; 
     224              script._requires.push(scriptRequire); 
     225              break; 
     226            case "resource": 
     227              var res = match[2].match(/(\S+)\s+(.*)/); 
     228              if (res === null) { 
     229                // NOTE: Unlocalized strings 
     230                throw new Error("Invalid syntax for @resource declaration '" + 
     231                                match[2] + "'. Resources are declared like: " + 
     232                                "@resource <name> <url>."); 
     233              } 
     234 
     235              var resName = res[1]; 
     236              if (previousResourceNames[resName]) { 
     237                throw new Error("Duplicate resource name '" + resName + "' " + 
     238                                "detected. Each resource must have a unique " + 
     239                                "name."); 
     240              } else { 
     241                previousResourceNames[resName] = true; 
     242              } 
     243 
     244              var resUri = ioservice.newURI(res[2], null, uri); 
     245              var scriptResource = new ScriptResource(script); 
     246              scriptResource._name = resName; 
     247              scriptResource._downloadURL = resUri.spec; 
     248              script._resources.push(scriptResource); 
     249              break; 
     250          } 
     251        } 
     252      } 
     253    } 
     254 
     255    // if no meta info, default to reasonable values 
     256    if (script._name == null) script._name = parseScriptName(uri); 
     257    if (script._namespace == null) script._namespace = uri.host; 
     258    if (!script._description) script._description = ""; 
     259    if (script._includes.length == 0) script._includes.push("*"); 
     260 
     261    return script; 
     262  }, 
     263 
     264  install: function(script) { 
     265    GM_log("> Config.install"); 
     266 
     267    var existingIndex = this._find(script); 
     268    if (existingIndex > -1) { 
     269      this.uninstall(this._scripts[existingIndex], false); 
     270    } 
     271 
     272    script._initFile(script._tempFile); 
     273    script._tempFile = null; 
     274 
     275    for (var i = 0; i < script._requires.length; i++) { 
     276      script._requires[i]._initFile(); 
     277    } 
     278 
     279    for (var i = 0; i < script._resources.length; i++) { 
     280      script._resources[i]._initFile(); 
     281    } 
     282 
     283    this._scripts.push(script); 
     284    this._changed(script, "install", null); 
     285 
     286    GM_log("< Config.install"); 
     287  }, 
     288 
     289  uninstall: function(script, uninstallPrefs) { 
     290    var idx = this._find(script); 
     291    this._scripts.splice(idx, 1); 
     292    this._changed(script, "uninstall", null); 
     293 
     294    // watch out for cases like basedir="." and basedir="../gm_scripts" 
     295    if (!script._basedirFile.equals(this._scriptDir)) { 
     296      // if script has its own dir, remove the dir + contents 
     297      script._basedirFile.remove(true); 
     298    } else { 
     299      // if script is in the root, just remove the file 
     300      script._file.remove(false); 
     301    } 
     302 
     303    if (uninstallPrefs) { 
     304      // Remove saved preferences 
     305      GM_prefRoot.remove("scriptvals." + script._namespace + "/" + script._name + "."); 
     306    } 
     307  }, 
     308 
     309  /** 
     310   * Moves an installed user script to a new position in the array of installed scripts. 
     311   * 
     312   * @param script The script to be moved. 
     313   * @param destination Can be either (a) a numeric offset for the script to be 
     314   *                    moved or (b) another installet script to which position 
     315   *                    the script will be moved. 
     316   */ 
     317  move: function(script, destination) { 
     318    var from = this._scripts.indexOf(script); 
     319    var to = -1; 
     320 
     321    // Make sure the user script is installed 
     322    if (from == -1) return; 
     323 
     324    if (typeof destination == 'number') { // if destination is an offset 
     325      to = from + destination; 
     326      to = Math.max(0, to); 
     327      to = Math.min(this._scripts.length - 1, to); 
     328    } else { // if destination is a script object 
     329      to = this._scripts.indexOf(destination); 
     330    } 
     331 
     332    if (to == -1) return; 
     333 
     334    var tmp = this._scripts.splice(from, 1)[0]; 
     335    this._scripts.splice(to, 0, tmp); 
     336    this._changed(script, 'move', to); 
     337  }, 
     338 
     339  get _scriptDir() { 
     340    var newDir = this._newScriptDir; 
     341    if (newDir.exists()) return newDir; 
     342 
     343    var oldDir = this._oldScriptDir; 
     344    if (oldDir.exists()) return oldDir; 
     345 
     346    // if we called this function, we want a script dir. 
     347    // but, at this branch, neither the old nor new exists, so create one 
     348    newDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755); 
     349 
     350    var defaultConfigFile = getContentDir(); 
     351    defaultConfigFile.append("default-config.xml"); 
     352 
     353    defaultConfigFile.copyTo(newDir, "config.xml"); 
     354    defaultConfigFile.permissions = 0644; 
     355 
     356    return newDir; 
     357  }, 
     358 
     359  get _newScriptDir() { 
     360    var file = Components.classes["@mozilla.org/file/directory_service;1"] 
     361                         .getService(Components.interfaces.nsIProperties) 
     362                         .get("ProfD", Components.interfaces.nsILocalFile); 
     363    file.append("gm_scripts"); 
     364    return file; 
     365  }, 
     366 
     367  get _oldScriptDir() { 
     368    var file = getContentDir(); 
     369    file.append("scripts"); 
     370    return file; 
     371  }, 
     372 
     373  get scripts() { return this._scripts.concat(); }, 
     374  getMatchingScripts: function(testFunc) { return this._scripts.filter(testFunc); } 
     375}; 
     376 
     377Components.classes["@mozilla.org/moz/jssubscript-loader;1"] 
     378  .getService(Components.interfaces.mozIJSSubScriptLoader) 
     379  .loadSubScript("chrome://greasemonkey/content/versioning.js"); 
     380 
     381function Script(config) { 
     382  this._config = config; 
     383  this._observers = []; 
     384 
     385  this._downloadURL = null; // Only for scripts not installed 
     386  this._tempFile = null; // Only for scripts not installed 
     387  this._basedir = null; 
     388  this._filename = null; 
     389 
     390  this._name = null; 
     391  this._namespace = null; 
     392  this._description = null; 
     393  this._enabled = true; 
     394  this._includes = []; 
     395  this._excludes = []; 
     396  this._requires = []; 
     397  this._resources = []; 
     398}; 
     399 
     400Script.prototype = { 
     401  matchesURL: function(url) { 
     402    function test(page) { 
     403      return convert2RegExp(page).test(url); 
     404    } 
     405 
     406    return this._includes.some(test) && !this._excludes.some(test); 
     407  }, 
     408 
     409  _changed: function(event, data) { this._config._changed(this, event, data); }, 
     410 
     411  get name() { return this._name; }, 
     412  get namespace() { return this._namespace; }, 
     413  get description() { return this._description; }, 
     414  get enabled() { return this._enabled; }, 
     415  set enabled(enabled) { this._enabled = enabled; this._changed('edit-enabled', enabled); }, 
     416 
     417  get includes() { return this._includes.concat(); }, 
     418  get excludes() { return this._excludes.concat(); }, 
     419  addInclude: function(url) { this._includes.push(url); this._changed('edit-include-add', url); }, 
     420  removeIncludeAt: function(index) { this._includes.splice(index, 1); this._changed('edit-include-remove', index); }, 
     421  addExclude: function(url) { this._excludes.push(url); this._changed('edit-exclude-add', url); }, 
     422  removeExcludeAt: function(index) { this._excludes.splice(index, 1); this._changed('edit-exclude-remove', index); }, 
     423 
     424  get requires() { return this._requires.concat(); }, 
     425  get resources() { return this._resources.concat(); }, 
     426 
     427  get _file() { 
     428    var file = this._basedirFile; 
     429    file.append(this._filename); 
     430    return file; 
     431  }, 
     432 
     433  get editFile() { return this._file; }, 
     434 
     435  get _basedirFile() { 
     436    var file = this._config._scriptDir; 
     437    file.append(this._basedir); 
     438    return file; 
     439  }, 
     440 
     441  get fileURL() { return GM_getUriFromFile(this._file).spec; }, 
     442  get textContent() { return getContents(this._file); }, 
     443 
     444  _initFileName: function(name, useExt) { 
     445    var ext = ""; 
     446    name = name.toLowerCase(); 
     447 
     448    var dotIndex = name.lastIndexOf("."); 
     449    if (dotIndex > 0 && useExt) { 
     450      ext = name.substring(dotIndex + 1); 
     451      name = name.substring(0, dotIndex); 
     452    } 
     453 
     454    name = name.replace(/\s+/g, '_').replace(/[^-_A-Z0-9]+/gi, '');  
     455    ext = ext.replace(/\s+/g, '_').replace(/[^-_A-Z0-9]+/gi, '');  
     456 
     457    // If no Latin characters found - use default 
     458    if (!name) name = "gm_script"; 
     459 
     460    // 24 is a totally arbitrary max length 
     461    if (name.length > 24) name = name.substring(0, 24); 
     462 
     463    if (ext) name += "." + ext; 
     464   
     465    return name; 
     466  }, 
     467 
     468  _initFile: function(tempFile) { 
     469    var file = this._config._scriptDir; 
     470    var name = this._initFileName(this._name, false); 
     471 
     472    file.append(name); 
     473    file.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0755); 
     474    this._basedir = file.leafName; 
     475 
     476    file.append(name + ".user.js"); 
     477    file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644); 
     478    this._filename = file.leafName; 
     479 
     480    GM_log("Moving script file from " + tempFile.path + " to " + file.path); 
     481 
     482    file.remove(true); 
     483    tempFile.moveTo(file.parent, file.leafName); 
     484  }, 
     485 
     486  get urlToDownload() { return this._downloadURL; }, 
     487  setDownloadedFile: function(file) { this._tempFile = file; }, 
     488   
     489  get previewURL() { 
     490    return Components.classes["@mozilla.org/network/io-service;1"] 
     491                     .getService(Components.interfaces.nsIIOService) 
     492                     .newFileURI(this._tempFile).spec; 
    15493  } 
    16  
    17   return -1; 
    18 }; 
    19  
    20 Config.prototype.initFilename = function(script) { 
    21   var index = {}; 
    22   var base = script.name.replace(/[^A-Z0-9_]/gi, "").toLowerCase(); 
    23  
    24   // If no Latin characters found - use default 
    25   if (!base) { 
    26     base = "gm_script"; 
     494}; 
     495 
     496function ScriptRequire(script) { 
     497  this._script = script; 
     498 
     499  this._downloadURL = null; // Only for scripts not installed 
     500  this._tempFile = null; // Only for scripts not installed 
     501  this._filename = null; 
     502}; 
     503 
     504ScriptRequire.prototype = { 
     505  get _file() { 
     506    var file = this._script._basedirFile; 
     507    file.append(this._filename); 
     508    return file; 
     509  }, 
     510 
     511  get fileURL() { return GM_getUriFromFile(this._file).spec; }, 
     512  get textContent() { return getContents(this._file); }, 
     513 
     514  _initFile: function() { 
     515    var name = this._downloadURL.substr(this._downloadURL.lastIndexOf("/") + 1); 
     516    if(name.indexOf("?") > 0) { 
     517      name = name.substr(0, name.indexOf("?")); 
     518    } 
     519    name = this._script._initFileName(name, true); 
     520 
     521    var file = this._script._basedirFile; 
     522    file.append(name); 
     523    file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644); 
     524    this._filename = file.leafName; 
     525 
     526    GM_log("Moving dependency file from " + this._tempFile.path + " to " + file.path); 
     527 
     528    file.remove(true); 
     529    this._tempFile.moveTo(file.parent, file.leafName); 
     530    this._tempFile = null; 
     531  }, 
     532 
     533  get urlToDownload() { return this._downloadURL; }, 
     534  setDownloadedFile: function(file) { this._tempFile = file; } 
     535}; 
     536 
     537function ScriptResource(script) { 
     538  this._script = script; 
     539 
     540  this._downloadURL = null; // Only for scripts not installed 
     541  this._tempFile = null; // Only for scripts not installed 
     542  this._filename = null; 
     543  this._mimetype = null; 
     544  this._charset = null; 
     545 
     546  this._name = null; 
     547}; 
     548 
     549ScriptResource.prototype = { 
     550  get name() { return this._name; }, 
     551 
     552  get _file() { 
     553    var file = this._script._basedirFile; 
     554    file.append(this._filename); 
     555    return file; 
     556  }, 
     557 
     558  get textContent() { return getContents(this._file); }, 
     559 
     560  get dataContent() { 
     561    var appSvc = Components.classes["@mozilla.org/appshell/appShellService;1"] 
     562                           .getService(Components.interfaces.nsIAppShellService); 
     563 
     564    var window = appSvc.hiddenDOMWindow; 
     565    var binaryContents = getBinaryContents(this._file); 
     566 
     567    var mimetype = this._mimetype; 
     568    if (this._charset && this._charset.length > 0) { 
     569      mimetype += ";charset=" + this._charset; 
     570    } 
     571 
     572    return "data:" + mimetype + ";base64," + 
     573      window.encodeURIComponent(window.btoa(binaryContents)); 
     574  }, 
     575 
     576  _initFile: ScriptRequire.prototype._initFile, 
     577 
     578  get urlToDownload() { return this._downloadURL; }, 
     579  setDownloadedFile: function(tempFile, mimetype, charset) { 
     580    this._tempFile = tempFile; 
     581    this._mimetype = mimetype; 
     582    this._charset = charset; 
    27583  } 
    28  
    29   // 24 is a totally arbitrary max length 
    30   if (base.length > 24) { 
    31     base = base.substring(0, 24); 
    32   } 
    33  
    34   for (var i = 0; i < this.scripts.length; i++) { 
    35     index[this.scripts[i].basedir] = this.scripts[i]; 
    36   } 
    37  
    38   if (!index[base]) { 
    39     script.filename = base + ".user.js"; 
    40     script.basedir = base; 
    41     return; 
    42   } 
    43  
    44   for (var count = 1; count < Number.MAX_VALUE; count++) { 
    45     if (!index[base + count]) { 
    46       script.filename = base + ".user.js"; 
    47       script.basedir = base + "("+ count + ")"; 
    48       return; 
    49     } 
    50  
    51     if (!index[filename]) { 
    52       // Check to make sure there's no file already in that space. 
    53       var file = getScriptDir().clone(); 
    54       file.append(filename); 
    55       if (!file.exists()) { 
    56         script.filename = filename; 
    57         return; 
    58       } 
    59     } 
    60   } 
    61   // NOTE: non localised string 
    62   throw new Error("doooooooode. get some different user script or something."); 
    63 }; 
    64  
    65 Config.prototype.initDependencyFilename = function(script, req) { 
    66   var remoteFilename = req.url.substr(req.url.lastIndexOf("/") + 1); 
    67  
    68   if(remoteFilename.indexOf("?")>0){ 
    69     remoteFilename = remoteFilename.substr(0, remoteFilename.indexOf("?")); 
    70   } 
    71  
    72   var dotIndex = remoteFilename.lastIndexOf("."); 
    73   if (dotIndex > 0) { 
    74     var base = remoteFilename.substring(0, dotIndex); 
    75     var ext = remoteFilename.substring(dotIndex+1); 
    76   } else { 
    77     var base = remoteFilename; 
    78     var ext = ""; 
    79   } 
    80  
    81   ext = ext.replace(/[^A-Z0-9_]/gi, ""); 
    82   base = base.replace(/[^A-Z0-9_]/gi, ""); 
    83  
    84   if (base.length > 24) { 
    85     base = base.substring(0, 24); 
    86   } 
    87  
    88   if (ext.length > 0){ 
    89     ext = "."+ext; 
    90   } 
    91  
    92   for (var count = 0; count < Number.MAX_VALUE; count++) { 
    93     var stamp = (count > 0) ? "(" + count + ")" : ""; 
    94     var filename = base + stamp + ext; 
    95     var file = getScriptBasedir(script) 
    96     file.append(filename); 
    97  
    98     if (!file.exists()) { 
    99       return filename; 
    100     } 
    101   } 
    102   return undefined; 
    103 
    104  
    105 Config.prototype.load = function() { 
    106   var domParser = Components.classes["@mozilla.org/xmlextras/domparser;1"] 
    107                             .createInstance(Components.interfaces.nsIDOMParser); 
    108  
    109   var configContents = getContents(getConfigFileURI()); 
    110   var doc = domParser.parseFromString(configContents, "text/xml"); 
    111   var nodes = doc.evaluate("/UserScriptConfig/Script", doc, null, 0, null); 
    112  
    113   this.scripts = []; 
    114  
    115   for (var node = null; (node = nodes.iterateNext()); ) { 
    116     var script = new Script(); 
    117  
    118     for (var i = 0, childNode = null; (childNode = node.childNodes[i]); i++) { 
    119       if (childNode.nodeName == "Include") { 
    120         script.includes.push(childNode.firstChild.nodeValue); 
    121       } else if (childNode.nodeName == "Exclude") { 
    122         script.excludes.push(childNode.firstChild.nodeValue); 
    123       } else if (childNode.nodeName == "Require") { 
    124         script.requires.push({ filename : childNode.getAttribute("filename")}); 
    125       } else if (childNode.nodeName == "Resource") { 
    126         script.resources.push({ name : childNode.getAttribute("name"), 
    127                                 filename : childNode.getAttribute("filename"), 
    128                                 mimetype : childNode.getAttribute("mimetype"), 
    129                                 charset  : childNode.getAttribute("charset")}); 
    130       } 
    131     } 
    132  
    133     script.filename = node.getAttribute("filename"); 
    134     script.name = node.getAttribute("name"); 
    135     script.namespace = node.getAttribute("namespace"); 
    136     script.description = node.getAttribute("description"); 
    137     script.enabled = node.getAttribute("enabled") == true.toString(); 
    138     script.basedir = node.getAttribute("basedir") || "."; 
    139  
    140     this.scripts.push(script); 
    141   } 
    142 }; 
    143  
    144 Config.prototype.save = function() { 
    145   var doc = document.implementation.createDocument("", "UserScriptConfig", null); 
    146  
    147   for (var i = 0, scriptObj = null; (scriptObj = this.scripts[i]); i++) { 
    148     var scriptNode = doc.createElement("Script"); 
    149  
    150     for (var j = 0; j < scriptObj.includes.length; j++) { 
    151       var includeNode = doc.createElement("Include"); 
    152       includeNode.appendChild(doc.createTextNode(scriptObj.includes[j])); 
    153       scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
    154       scriptNode.appendChild(includeNode); 
    155     } 
    156  
    157     for (var j = 0; j < scriptObj.excludes.length; j++) { 
    158       var excludeNode = doc.createElement("Exclude"); 
    159       excludeNode.appendChild(doc.createTextNode(scriptObj.excludes[j])); 
    160       scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
    161       scriptNode.appendChild(excludeNode); 
    162     } 
    163  
    164     for (var j = 0; j < scriptObj.requires.length; j++) { 
    165       var req = scriptObj.requires[j]; 
    166       var resourceNode = doc.createElement("Require"); 
    167  
    168       resourceNode.setAttribute("filename", req.filename); 
    169  
    170       scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
    171       scriptNode.appendChild(resourceNode); 
    172     } 
    173  
    174     for (var j = 0; j< scriptObj.resources.length; j++) { 
    175       var imp = scriptObj.resources[j]; 
    176       var resourceNode = doc.createElement("Resource"); 
    177  
    178       resourceNode.setAttribute("name", imp.name); 
    179       resourceNode.setAttribute("filename", imp.filename); 
    180       resourceNode.setAttribute("mimetype", imp.mimetype); 
    181       if (imp.charset) { 
    182         resourceNode.setAttribute("charset", imp.charset); 
    183       } 
    184  
    185       scriptNode.appendChild(doc.createTextNode("\n\t\t")); 
    186       scriptNode.appendChild(resourceNode); 
    187     } 
    188  
    189     scriptNode.appendChild(doc.createTextNode("\n\t")); 
    190  
    191     scriptNode.setAttribute("filename", scriptObj.filename); 
    192     scriptNode.setAttribute("name", scriptObj.name); 
    193     scriptNode.setAttribute("namespace", scriptObj.namespace); 
    194     scriptNode.setAttribute("description", scriptObj.description); 
    195     scriptNode.setAttribute("enabled", scriptObj.enabled); 
    196     scriptNode.setAttribute("basedir", scriptObj.basedir); 
    197  
    198     doc.firstChild.appendChild(doc.createTextNode("\n\t")); 
    199     doc.firstChild.appendChild(scriptNode); 
    200   } 
    201  
    202   doc.firstChild.appendChild(doc.createTextNode("\n")); 
    203  
    204   var configStream = getWriteStream(this.configFile); 
    205   new XMLSerializer().serializeToStream(doc, configStream, "utf-8"); 
    206   configStream.close(); 
    207 }; 
    208  
    209 Config.prototype.install = function(script) { 
    210   GM_log("> Config.install"); 
    211  
    212   try { 
    213     // initialize a new script object 
    214     script.filename = script.file.leafName; 
    215  
    216     var newDir = getScriptDir(); 
    217     var existingIndex = this.find(script.namespace, script.name); 
    218     var existingFile = null; 
    219     var oldScripts = new Array(this.scripts); 
    220  
    221     if (existingIndex > -1) { 
    222         existingFile = getScriptBasedir(this.scripts[existingIndex]); 
    223         existingFile.normalize(); 
    224         if (existingFile.equals(getScriptDir())) { 
    225           existingFile = getScriptFile(this.scripts[existingIndex]); 
    226         } 
    227         if (existingFile.exists()) { 
    228           existingFile.remove(true); 
    229         } 
    230         this.scripts.splice(existingIndex, 1); 
    231     } 
    232  
    233     this.initFilename(script); 
    234     newDir.append(script.basedir); 
    235     script.file.copyTo(newDir, script.filename); 
    236  
    237     for (var i = 0; i < script.requires.length; i++) { 
    238       this.installDependency(script, script.requires[i]); 
    239     } 
    240  
    241     for (var i = 0; i < script.resources.length; i++) { 
    242       this.installDependency(script, script.resources[i]); 
    243     } 
    244  
    245     this.scripts.push(script); 
    246     this.save(); 
    247  
    248     GM_log("< Config.install"); 
    249   } catch (e2) { 
    250     // NOTE: unlocalised string 
    251     alert("Error installing user script:\n\n" + (e2 ? e2 : "")); 
    252   } 
    253 }; 
    254  
    255 Config.prototype.installDependency = function(script, req){ 
    256   GM_log("Installing dependency: " + req.url  + " from " + req.file.path); 
    257  
    258   var scriptDir = getScriptDir(); 
    259   GM_log("Installing to " + script.basedir); 
    260   scriptDir.append(script.basedir); 
    261  
    262   req.filename = this.initDependencyFilename(script, req); 
    263   GM_log("Installing as: " + req.filename); 
    264  
    265   try { 
    266     req.file.copyTo(scriptDir, req.filename) 
    267   } catch(e) { 
    268     throw e; 
    269   } 
    270 }; 
    271  
    272 function Script() { 
    273   this.filename = null; 
    274   this.name = null; 
    275   this.namespace = null; 
    276   this.description = null; 
    277   this.enabled = true; 
    278   this.includes = []; 
    279   this.excludes = []; 
    280   this.basedir = null; 
    281   this.requires = []; 
    282   this.resources = []; 
    283 }; 
    284  
    285 function ScriptDependency(){ 
    286   this.url = null 
    287   this.file = null; 
    288   this.filename = null; 
    289 }; 
    290  
    291 function ScriptResource(){ 
    292   this.url = null; 
    293   this.name = null; 
    294   this.file = null; 
    295   this.filename = null; 
    296   this.mimetype = null; 
    297 }; 
     584}; 
  • trunk/src/chrome/chromeFiles/content/install.xul

    r547 r688  
    2626 
    2727  <script type="application/x-javascript" src="chrome://greasemonkey/content/utils.js" /> 
    28   <script type="application/x-javascript" src="chrome://greasemonkey/content/config.js" /> 
    2928  <script type="application/x-javascript" src="chrome://greasemonkey/content/scriptdownloader.js" /> 
    3029  <script type="application/x-javascript" src="chrome://greasemonkey/content/prefmanager.js" /> 
  • trunk/src/chrome/chromeFiles/content/manage.css

    r546 r688  
    44the original starting size of the elements before flexing. 
    55*/ 
    6 dialog
     6window
    77  width: 600px; 
    8   height: 500px 
     8  height: 500px; 
     9  padding: 10px; 
    910} 
    1011#col_left { 
  • trunk/src/chrome/chromeFiles/content/manage.js

    r652 r688  
    1 var config = new Config(); 
    2 var uninstallList = []; 
     1var config = GM_getConfig(); 
    32 
    43window.addEventListener("load", function(ev) { 
    5   config.load(); 
    64  loadControls(); 
    75 
     
    108    chooseScript(0); 
    119  } 
     10 
     11  config.addObserver(observer); 
    1212}, false); 
    1313 
    14 function handleOkButton() { 
    15   for (var i = 0, script = null; (script = uninstallList[i]); i++) { 
    16     var idx = config.find(script.namespace, script.name); 
    17     config.scripts.splice(idx, 1); 
    18   } 
    19   config.save(); 
    20  
    21   var chkUninstallPrefs = document.getElementById('chkUninstallPrefs'); 
    22   for (var i = 0, script = null; (script = uninstallList[i]); i++) { 
    23     file = getScriptBasedir(script); 
    24     file.normalize(); 
    25     if (!file.equals(getScriptDir())) { 
    26       if (file.exists()) { 
    27         file.remove(true); // file==base directory recursive delete 
    28       } 
    29     } else { 
    30       file = getScriptFile(script); 
    31       if (file.exists()) { 
    32         file.remove(false); 
    33       } 
    34     } 
    35     if (chkUninstallPrefs.checked) { 
    36        // Remove saved preferences 
    37        var scriptPrefRoot = ["scriptvals.", 
    38                   script.namespace, 
    39                   "/", 
    40                   script.name, 
    41                   "."].join(""); 
    42        GM_prefRoot.remove(scriptPrefRoot); 
    43     } 
    44   } 
    45   return true; 
     14window.addEventListener("unload", function(ev) { 
     15  pagesControl.clear(); 
     16  config.removeObserver(observer); 
     17}, false); 
     18 
     19var observer = { 
     20  notifyEvent: function(script, event, data) { 
     21    var node = null; 
     22    for (var i = 0; node = listbox.childNodes[i]; i++) 
     23      if (node.script == script) 
     24        break; 
     25 
     26    switch (event) { 
     27    case "edit-enabled": 
     28      node.style.color = data ? "" : "gray"; 
     29      if (script == selectedScript) 
     30        chkEnabled.checked = data; 
     31      break; 
     32    case "install": 
     33      addListitem(script, -1); 
     34      break; 
     35    case "uninstall": 
     36      var selected = listbox.selectedItem == node; 
     37      listbox.removeChild(node); 
     38 
     39      if (selected && listbox.childNodes.length > 0) { 
     40        chooseScript(Math.max(Math.min(listbox.selectedIndex, listbox.childNodes.length - 1), 0)); 
     41      } 
     42      break; 
     43    case "move": 
     44      listbox.removeChild(node); 
     45      listbox.insertBefore(node, listbox.childNodes[data]); 
     46      // then re-select the dropped script 
     47      listbox.selectedIndex = data; 
     48      break; 
     49    } 
     50 
     51    // fix the listbox indexes 
     52    for (var i = 0, n = null; n = listbox.childNodes[i]; i++) n.index=i; 
     53  } 
    4654}; 
    4755 
     
    6371  btnUninstall.addEventListener("command", function() { handleUninstallButton(); }, false); 
    6472  chkEnabled.addEventListener("command", function() { 
    65      if (selectedScript) { 
     73     if (selectedScript) 
    6674       selectedScript.enabled = chkEnabled.checked; 
    67        if (selectedScript.enabled) { 
    68          listbox.selectedItem.style.color = ''; 
    69        } else { 
    70          listbox.selectedItem.style.color = 'gray'; 
    71        }