diff --git a/bukkit-plugin/src/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java b/bukkit-plugin/src/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java index ecd9e52..3d8e96e 100644 --- a/bukkit-plugin/src/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java +++ b/bukkit-plugin/src/net/walterhiggins/scriptcraft/ScriptCraftPlugin.java @@ -9,6 +9,8 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.Collection; import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.command.*; @@ -22,9 +24,11 @@ public class ScriptCraftPlugin extends JavaPlugin //protected Map playerContexts = new HashMap(); protected ScriptEngine engine = null; private static final String JS_PLUGINS_DIR = "js-plugins"; - - @Override - public void onEnable() + + /** + * Unzips bundled javascript code. + */ + private void unzipJS() { // // does the js-plugins directory exist? @@ -66,6 +70,12 @@ public class ScriptCraftPlugin extends JavaPlugin ioe.printStackTrace(); } } + } + + @Override + public void onEnable() + { + unzipJS(); if (this.engine == null){ try{ @@ -81,6 +91,28 @@ public class ScriptCraftPlugin extends JavaPlugin } } } + public List onTabComplete(CommandSender sender, Command cmd, + String alias, + String[] args) + { + // + // delegate to javascript + // + List result = new ArrayList(); + try { + this.engine.put("__onTC_result",result); + this.engine.put("__onTC_sender",sender); + this.engine.put("__onTC_cmd",cmd); + this.engine.put("__onTC_alias",alias); + this.engine.put("__onTC_args",args); + this.engine.eval("_onTabComplete()"); + }catch (Exception e){ + sender.sendMessage(e.getMessage()); + e.printStackTrace(); + } + return result; + } + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if(cmd.getName().equalsIgnoreCase("js")) diff --git a/js-plugins/core/_scriptcraft.js b/js-plugins/core/_scriptcraft.js index 61e013a..6afdfe1 100644 --- a/js-plugins/core/_scriptcraft.js +++ b/js-plugins/core/_scriptcraft.js @@ -5,80 +5,200 @@ ScriptCraft.core = ScriptCraft.core || {}; // private implementation // (function(){ - // - // don't execute this more than once - // - if (typeof load == "function") - return ; - - var _originalScript = __script; - importPackage(java.io); - var _canonize = function(file){ - return file.getCanonicalPath().replaceAll("\\\\","/"); - }; - var _load = function(filename){ - var file = new File(filename); - print("loading " + _canonize(file)); - if (file.exists()){ - var parent = file.getParentFile(); - var reader = new FileReader(file); - __engine.put("__script",_canonize(file)); - __engine.put("__folder",(parent?_canonize(parent):"")+"/"); - __engine.eval(reader); - }else{ - print("Error: " + filename + " not found"); - } - }; - var _listJsFiles = function(store,dir) - { - if (typeof dir == "undefined"){ - dir = new File(_originalScript).getParentFile().getParentFile(); - } - var files = dir.listFiles(); - for (var i = 0;i < files.length; i++){ - if (files[i].isDirectory()){ - _listJsFiles(store,files[i]); - }else{ - if (files[i].getCanonicalPath().endsWith(".js") && - !(files[i].getName().startsWith("_"))) - { - store.push(files[i]); - } - } - } - }; - var _reload = function(pluginDir) - { - var jsFiles = []; - _listJsFiles(jsFiles,pluginDir); - // - // script files whose name begins with _ (underscore) - // will not be loaded automatically at startup. - // These files are assumed to be dependencies/private to plugins - // - // E.g. If you have a plugin called myMiniGame.js in the myMiniGame directory - // and which in addition to myMiniGame.js also includes _myMiniGame_currency.js _myMiniGame_events.js etc. - // then it's assumed that _myMiniGame_currency.js and _myMiniGame_events.js will be loaded - // as dependencies by myMiniGame.js and do not need to be loaded via js reload - // - for (var i = 0;i < jsFiles.length; i++){ - load(_canonize(jsFiles[i])); - } - }; - ScriptCraft.core.load = _load; - ScriptCraft.core.reload = _reload; + // + // don't execute this more than once + // + if (typeof load == "function") + return ; + + var _originalScript = __script; + importPackage(java.io); + var _canonize = function(file){ + return file.getCanonicalPath().replaceAll("\\\\","/"); + }; + var _load = function(filename){ + var file = new File(filename); + print("loading " + _canonize(file)); + if (file.exists()){ + var parent = file.getParentFile(); + var reader = new FileReader(file); + __engine.put("__script",_canonize(file)); + __engine.put("__folder",(parent?_canonize(parent):"")+"/"); + __engine.eval(reader); + }else{ + print("Error: " + filename + " not found"); + } + }; + var _listJsFiles = function(store,dir) + { + if (typeof dir == "undefined"){ + dir = new File(_originalScript).getParentFile().getParentFile(); + } + var files = dir.listFiles(); + for (var i = 0;i < files.length; i++){ + if (files[i].isDirectory()){ + _listJsFiles(store,files[i]); + }else{ + if (files[i].getCanonicalPath().endsWith(".js") && + !(files[i].getName().startsWith("_"))) + { + store.push(files[i]); + } + } + } + }; + var _reload = function(pluginDir) + { + var jsFiles = []; + _listJsFiles(jsFiles,pluginDir); + // + // script files whose name begins with _ (underscore) + // will not be loaded automatically at startup. + // These files are assumed to be dependencies/private to plugins + // + // E.g. If you have a plugin called myMiniGame.js in the myMiniGame directory + // and which in addition to myMiniGame.js also includes _myMiniGame_currency.js _myMiniGame_events.js etc. + // then it's assumed that _myMiniGame_currency.js and _myMiniGame_events.js will be loaded + // as dependencies by myMiniGame.js and do not need to be loaded via js reload + // + for (var i = 0;i < jsFiles.length; i++){ + load(_canonize(jsFiles[i])); + } + }; + var _isJavaObject = function(o){ + var result = false; + try { + o.hasOwnProperty("testForJava"); + }catch (e){ + // java will throw an error when an attempt is made to access the + // hasOwnProperty method. (it won't exist for Java objects) + result = true; + } + return result; + }; + var _javaLangObjectMethods = ["equals","getClass","class","getClass","hashCode","notify","notifyAll","toString","wait","clone","finalize"]; + var _getProperties = function(o) + { + var result = []; + if (_isJavaObject(o)){ + propertyLoop: + for (var i in o){ + // + // don't include standard Object methods + // + var isObjectMethod = false; + for (var j = 0;j < _javaLangObjectMethods.length; j++) + if (_javaLangObjectMethods[j] == i) + continue propertyLoop; + if (typeof o[i] == "function") + result.push(i+"()"); + else + result.push(i); + } + }else{ + for (var i in o){ + if (!o.hasOwnProperty(i)) + continue; + if (i.match(/^[^_]/)){ + if (typeof o[i] == "function") + result.push(i+"()"); + else + result.push(i); + } + } + } + return result.sort(); + }; - for (var f in ScriptCraft.core){ - global[f] = ScriptCraft.core[f]; - } - ScriptCraft.core.initialized = true; - // - // assumes this was loaded from js-plugins/core/ - // - reload(new File(__script).getParentFile().getParentFile()); + var __onTabComplete2 = function() + { + var _globalSymbols = _getProperties(global) + var result = global.__onTC_result; + var args = global.__onTC_args; + var propsOfLastArg = []; + var statement = args.join(" "); + statement = statement.replace(/^\s+/,"").replace(/\s+$/,""); + + if (statement.length == 0){ + propsOfLastArg = _globalSymbols; + }else{ + var statementSyms = statement.split(/[^a-zA-Z0-9_\.]/); + var lastSymbol = statementSyms[statementSyms.length-1]; + // + // try to complete the object ala java IDEs. + // + var parts = lastSymbol.split(/\./); + var name = parts[0]; + var symbol = global[name]; + var lastGoodSymbol = symbol; + if (typeof symbol != "undefined") + { + for (var i = 1; i < parts.length;i++){ + name = parts[i]; + symbol = symbol[name]; + if (typeof symbol == "undefined") + break; + lastGoodSymbol = symbol; + } + if (typeof symbol == "undefined"){ + // + // look up partial matches against last good symbol + // + var objectProps = _getProperties(lastGoodSymbol); + if (name == ""){ + // if the last symbol looks like this.. + // ScriptCraft. + // + for (var i =0;i < objectProps.length;i++) + propsOfLastArg.push(statement+objectProps[i]);; + }else{ + // it looks like this.. + // ScriptCraft.co + // + var li = statement.lastIndexOf(name); + statement = statement.substring(0,li); + + for (var i = 0; i < objectProps.length;i++){ + if (objectProps[i].indexOf(name) == 0){ + propsOfLastArg.push(statement + objectProps[i]); + } + } + } + }else{ + var objectProps = _getProperties(symbol); + for (var i = 0; i < objectProps.length; i++){ + propsOfLastArg.push(statement + objectProps[i]); + } + } + }else{ + // loop thru globalSymbols looking for a good match + for (var i = 0;i < _globalSymbols.length; i++){ + if (_globalSymbols[i].indexOf(lastSymbol) == 0){ + propsOfLastArg.push(statement.replace(lastSymbol,_globalSymbols[i])); + } + } + } + } + for (var i = 0;i < propsOfLastArg.length; i++){ + result.add(propsOfLastArg[i]); + } + }; + global._onTabComplete = __onTabComplete2; + + + ScriptCraft.core.load = _load; + ScriptCraft.core.reload = _reload; + + for (var f in ScriptCraft.core){ + global[f] = ScriptCraft.core[f]; + } + ScriptCraft.core.initialized = true; + // + // assumes this was loaded from js-plugins/core/ + // + reload(new File(__script).getParentFile().getParentFile()); }()); -