Cool, there is an RSS feed now:\n\n\nI really like this tiddly thing. It is very convenient because it doesn't force you to write a whole page at a time. Just a few lines are enough for a new tiddler. I do see some room for TiddlYmprovements though.
Actions are objects that change the current state of the application. A simple one would be an AssignmentAction containing a ValueReference and a Value. A function can return such an Action as a result and still be purely functional. Actually executing the action is outside the scope of FO's. Calculating the result of executing the Action or a list of Actions is //within// the scope of FO's.\n\nAn application would provide a framework for executing Actions determined by FO's. This means that the framework only needs to decide when to go from the state //before// the action to the state //after// executing it. For example: the user enters a value in a field in a form. After pressing enter the [[World|World]] transitions from the state where the formfield contained the old value to the state where the formfield contains the new value.
config.options.chkHttpReadOnly = false;\n
This is probably the most basic [[Action|Action]] imaginable. It filters the [[World|World]] by returning a new value for a specific ValueReference.
[[Tiddly Tools|]]\n[[DHTML Calendar|]]
FirstThingsFirst\n[[Functional Objects]]
When eclipse seems to need more and more memory sometimes it just doesn't help to enlarge the heap. I actually got into a situation that a 512MB heap was used for only 50MB and I still got OutOfMemoryError's. A good look at the exceptions revealed that it was actually the PermGen heap that was full. I use an eclipse.ini like this and eclipse (loaded with lots of plugins) runs for hours without problems:\n\n-vm\nC:\sProgram Files\sJava\sjdk1.6.0\sbin\sjavaw.exe\n-vmargs\n-Xms128m\n-Xmx128m\n-XX:MaxPermSize=128m
Imagine a model object, an FO of course and a form containing inputs for each property in the model. Each input has it's value bound to the corresponding value in the model using a simple expression.\nIt is a general rule for input fields that they show the bound value unless the user overrides it with input. What is shown is reflected in the 'value' property of the input field.\nIf an external event changes values in the model, the input fields are automatically updated, unless the user has overridden the values.\nWhen the user clicks the OK button, an expression is evaluated that results in an [[Action]] that will update the model to the values that the user put in. In this case the [[Action]] would probably be a List containing [[AssignmentActions|AssignmentAction]] for each property that should change.\n\nThe whole setup could be created using an expression that generates a form using introspection on the model.
Wow! [[This|]] is how debugging browsers should be!
I guess one day it had to happen: my own blog or wiki or whatever. This is it. Enjoy.\n\nBrought to you with the help of my wonderful wife Laura.
This is an idea that is swimming in my mind for more than a year now. It's a merge of Object Oriented Programming and Functional Programming in a dedicated framework that should make UI programming a lot easier.\n\nFO's are objects with only function methods; methods that have no side effects and always return the same result when the parameters and the context are the same. Let's call them //functions// ;-)\n\nSpecifically a FO does //not// have setters or methods that modify the state. A FO //can// have functions that result in an [[Action|Action]]. This way a FO never initiates an action and therefore doesn't get tangled up in the intricacies of executing actions in the right order. That is left to a framework that can deal with such matters in a more generic way.
Yep, that's me.
I hate JSP!\n\nWho came up with this abomination of a language? It takes a version 2.0 to have something resembling subroutines. Of course some production servers don't have that yet so you're stuck with setting variables and including other JSP's. That's fine until you start to do a serious page and the total size of the generated java exceeds something like 100.000 lines and it cannot be compiled anymore.\n\nWhat a disgrace...
Keep It Simple, Stupid, As Long As Possible.\n\nAvoid [[Premature Architecting|Premature architecting]]. Expect to refactor your code. Allow for progressive insight. If you are able to design perfect code right from the start, get a more interesting job.
/***\n|''Name:''|LegacyStrikeThroughPlugin|\n|''Description:''|Support for legacy (pre 2.1) strike through formatting|\n|''Version:''|1.0.1|\n|''Date:''|Jul 21, 2006|\n|''Source:''||\n|''Author:''|MartinBudden (mjbudden (at) gmail (dot) com)|\n|''License:''|[[BSD open source license]]|\n|''CoreVersion:''|2.1.0|\n|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|\n\n***/\n\n//{{{\n\n// Ensure that the LegacyStrikeThrough Plugin is only installed once.\nif(!version.extensions.LegacyStrikeThroughPlugin)\n {\n version.extensions.LegacyStrikeThroughPlugin = true;\n\nconfig.formatters.push(\n{\n name: "legacyStrikeByChar",\n match: "==",\n termRegExp: /(==)/mg,\n element: "strike",\n handler: config.formatterHelpers.createElementAndWikify\n});\n\n} // end of "install only once"\n//}}}\n
FirstThingsFirst\nBookMarks\n[[Functional Objects]]
Type the text for 'New Tiddler'
I came across this project with lots of injection, singletons and other architectural features. What struck me was that none of the created 'degrees of freedom' were actually used. A service is injected into a client object but there is no alternative for that service.\n\nSo instead of simply calling a method, a service is injected through a setter into a service property. That service implements an interface. The service implementation has to be instantiated and referred to from an XML file. The balance is: on one side one method, on the other side a property with setter, an interface, an implementation, and some XML in two places.\n\nIn my experience it is rarely clear right from the start what should be a service or what it's interface should be. Often this insight comes when an alternative service is needed. That usually show quite clearly the boundaries between service provider and user and what the interface should look like.\n\nThe usual argument for using interfaces and injection etc. is the complication of turning the simple method call into a service afterwards. By implementing the functionality as a service right away you do not need to touch that code anymore. Therefore adding the other service and using it should be all new code and only a tiny amount of changed XML (when using injection that way).\n\nThe downside is that you have to get the interface right the first time. If you find out later that it wasn't quite what you need you've done a lot of work for nothing.\n\nI like to start with the simple method. This is very easy to debug and saves me from bothering with injection based problems in the beginning. This way I can fully concentrate on getting the functionality right. Any interface changes are still trivial to make.\n\nModern refactoring tools make it very easy to add injection, an interface and so forth later on when I have gained the insight of implementing the second version of the service. The chances of getting it right are much higher then.
A tiddly waste of time
Guus' TiddlyWiki
Completely contrary to the idea of TiddlyWiki I would like to keep things small:\n- Stylesheets, images, javascript in separate files. Especially javascript could be loaded on demand.\n- Even tiddlers could be loaded on demand.\n- AJAX to save tiddlers right away\n- versioning\n- more object orientation: for example renaming tags or tiddlers without invalidating all references.\n\nOf course these changes would make the offline functionality impossible.\n\nWYSIWYG editing using FCKEditor.\n\nFolding as in .
| !date | !user | !location | !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |\n| 28/1/2007 20:25:31 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 28/1/2007 20:25:58 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 28/1/2007 20:35:33 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 28/1/2007 20:36:36 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 29/1/2007 7:22:45 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 29/1/2007 7:23:11 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 29/1/2007 10:0:10 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 29/1/2007 11:0:59 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 31/1/2007 5:49:36 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 1/2/2007 5:39:34 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 12/2/2007 21:31:42 | YourName | [[guus2.html|file:///home/guus/Desktop/tiddlyspot/guus2.html]] | [[store.cgi|]] | . | index.html | . |\n| 12/2/2007 21:33:12 | YourName | [[guus2.html|file:///home/guus/Desktop/tiddlyspot/guus2.html]] | [[store.cgi|]] | . | index.html | . |\n| 12/2/2007 21:44:35 | YourName | [[guus2.html|file:///home/guus/Desktop/tiddlyspot/guus2.html]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 13/2/2007 5:30:48 | YourName | [[guus2.html|file:///home/guus/Desktop/tiddlyspot/guus2.html]] | [[store.cgi|]] | . | index.html | . |\n| 13/2/2007 7:19:3 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 13/2/2007 14:38:15 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 16/2/2007 13:2:36 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 16/2/2007 14:36:25 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 16/2/2007 15:56:49 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 19/2/2007 13:28:44 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 7/3/2007 9:59:33 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 13/3/2007 11:58:26 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 13/3/2007 12:0:34 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 13/3/2007 13:57:59 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 20/3/2007 9:56:56 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 20/3/2007 9:57:27 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 20/3/2007 11:27:21 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 20/3/2007 11:42:1 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 21/3/2007 8:46:16 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 21/3/2007 8:48:51 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . | Ok |\n| 21/3/2007 10:39:53 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 15/6/2007 9:41:58 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 15/6/2007 9:42:15 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 17/7/2007 20:24:57 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 17/7/2007 20:25:19 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 17/7/2007 20:25:42 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 17/7/2007 20:26:6 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 17/7/2007 20:26:36 | YourName | [[/|]] | [[store.cgi|]] | . | index.html | . |\n| 20/7/2009 12:50:56 | Guus C. Bloemsma | [[/|]] | [[store.cgi|]] | . | index.html | . |
/***\n|''Name:''|UploadPlugin|\n|''Description:''|Save to web a TiddlyWiki|\n|''Version:''|3.4.4|\n|''Date:''|Sep 30, 2006|\n|''Source:''||\n|''Documentation:''||\n|''Author:''|BidiX (BidiX (at) bidix (dot) info)|\n|''License:''|[[BSD open source license| ]]|\n|''~CoreVersion:''|2.0.0|\n|''Browser:''|Firefox 1.5; InternetExplorer 6.0; Safari|\n|''Include:''|config.lib.file; config.lib.log; config.lib.options; PasswordTweak|\n|''Require:''|[[UploadService|]]|\n***/\n//{{{\nversion.extensions.UploadPlugin = {\n major: 3, minor: 4, revision: 4, \n date: new Date(2006,8,30),\n source: '',\n documentation: '',\n author: 'BidiX (BidiX (at) bidix (dot) info',\n license: '[[BSD open source license|]]',\n coreVersion: '2.0.0',\n browser: 'Firefox 1.5; InternetExplorer 6.0; Safari'\n};\n//}}}\n\n////+++!![config.lib.file]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.file) config.lib.file= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\nconfig.lib.file.dirname = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(0, lastpos);\n } else {\n return filePath.substring(0, filePath.lastIndexOf("\s\s"));\n }\n};\nconfig.lib.file.basename = function (filePath) {\n var lastpos;\n if ((lastpos = filePath.lastIndexOf("#")) != -1) \n filePath = filePath.substring(0, lastpos);\n if ((lastpos = filePath.lastIndexOf("/")) != -1) {\n return filePath.substring(lastpos + 1);\n } else\n return filePath.substring(filePath.lastIndexOf("\s\s")+1);\n};\nwindow.basename = function() {return "@@deprecated@@";};\n//}}}\n////===\n\n////+++!![config.lib.log]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.log) config.lib.log= {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 1}, \n date: new Date(2006,8,19)\n};\nconfig.lib.Log = function(tiddlerTitle, logHeader) {\n if (version.major < 2)\n this.tiddler = store.tiddlers[tiddlerTitle];\n else\n this.tiddler = store.getTiddler(tiddlerTitle);\n if (!this.tiddler) {\n this.tiddler = new Tiddler();\n this.tiddler.title = tiddlerTitle;\n this.tiddler.text = "| !date | !user | !location |" + logHeader;\n this.tiddler.created = new Date();\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[tiddlerTitle] = this.tiddler;\n else\n store.addTiddler(this.tiddler);\n }\n return this;\n};\n\nconfig.lib.Log.prototype.newLine = function (line) {\n var now = new Date();\n var newText = "| ";\n newText += now.getDate()+"/"+(now.getMonth()+1)+"/"+now.getFullYear() + " ";\n newText += now.getHours()+":"+now.getMinutes()+":"+now.getSeconds()+" | ";\n newText += config.options.txtUserName + " | ";\n var location = document.location.toString();\n var filename = config.lib.file.basename(location);\n if (!filename) filename = '/';\n newText += "[["+filename+"|"+location + "]] |";\n this.tiddler.text = this.tiddler.text + "\sn" + newText;\n this.addToLine(line);\n};\n\nconfig.lib.Log.prototype.addToLine = function (text) {\n this.tiddler.text = this.tiddler.text + text;\n this.tiddler.modifier = config.options.txtUserName;\n this.tiddler.modified = new Date();\n if (version.major < 2)\n store.tiddlers[this.tiddler.tittle] = this.tiddler;\n else {\n store.addTiddler(this.tiddler);\n story.refreshTiddler(this.tiddler.title);\n store.notify(this.tiddler.title, true);\n }\n if (version.major < 2)\n store.notifyAll(); \n};\n//}}}\n////===\n\n////+++!![config.lib.options]\n\n//{{{\nif (!config.lib) config.lib = {};\nif (!config.lib.options) config.lib.options = {\n author: 'BidiX',\n version: {major: 0, minor: 1, revision: 0}, \n date: new Date(2006,3,9)\n};\n\nconfig.lib.options.init = function (name, defaultValue) {\n if (!config.options[name]) {\n config.options[name] = defaultValue;\n saveOptionCookie(name);\n }\n};\n//}}}\n////===\n\n////+++!![PasswordTweak]\n\n//{{{\nversion.extensions.PasswordTweak = {\n major: 1, minor: 0, revision: 3, date: new Date(2006,8,30),\n type: 'tweak',\n source: ''\n};\n//}}}\n/***\n!!config.macros.option\n***/\n//{{{\nconfig.macros.option.passwordCheckboxLabel = "Save this password on this computer";\nconfig.macros.option.passwordType = "password"; // password | text\n\nconfig.macros.option.onChangeOption = function(e)\n{\n var opt = this.getAttribute("option");\n var elementType,valueField;\n if(opt) {\n switch(opt.substr(0,3)) {\n case "txt":\n elementType = "input";\n valueField = "value";\n break;\n case "pas":\n elementType = "input";\n valueField = "value";\n break;\n case "chk":\n elementType = "input";\n valueField = "checked";\n break;\n }\n config.options[opt] = this[valueField];\n saveOptionCookie(opt);\n var nodes = document.getElementsByTagName(elementType);\n for(var t=0; t<nodes.length; t++) \n {\n var optNode = nodes[t].getAttribute("option");\n if (opt == optNode) \n nodes[t][valueField] = this[valueField];\n }\n }\n return(true);\n};\n\nconfig.macros.option.handler = function(place,macroName,params)\n{\n var opt = params[0];\n if(config.options[opt] === undefined) {\n return;}\n var c;\n switch(opt.substr(0,3)) {\n case "txt":\n c = document.createElement("input");\n c.onkeyup = this.onChangeOption;\n c.setAttribute ("option",opt);\n c.className = "txtOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n break;\n case "pas":\n // input password\n c = document.createElement ("input");\n c.setAttribute("type",config.macros.option.passwordType);\n c.onkeyup = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "pasOptionInput "+opt;\n place.appendChild(c);\n c.value = config.options[opt];\n // checkbox link with this password "save this password on this computer"\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option","chk"+opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options["chk"+opt];\n // text savePasswordCheckboxLabel\n place.appendChild(document.createTextNode(config.macros.option.passwordCheckboxLabel));\n break;\n case "chk":\n c = document.createElement("input");\n c.setAttribute("type","checkbox");\n c.onclick = this.onChangeOption;\n c.setAttribute("option",opt);\n c.className = "chkOptionInput "+opt;\n place.appendChild(c);\n c.checked = config.options[opt];\n break;\n }\n};\n//}}}\n/***\n!! Option cookie stuff\n***/\n//{{{\nwindow.loadOptionsCookie_orig_PasswordTweak = window.loadOptionsCookie;\nwindow.loadOptionsCookie = function()\n{\n var cookies = document.cookie.split(";");\n for(var c=0; c<cookies.length; c++) {\n var p = cookies[c].indexOf("=");\n if(p != -1) {\n var name = cookies[c].substr(0,p).trim();\n var value = cookies[c].substr(p+1).trim();\n switch(name.substr(0,3)) {\n case "txt":\n config.options[name] = unescape(value);\n break;\n case "pas":\n config.options[name] = unescape(value);\n break;\n case "chk":\n config.options[name] = value == "true";\n break;\n }\n }\n }\n};\n\nwindow.saveOptionCookie_orig_PasswordTweak = window.saveOptionCookie;\nwindow.saveOptionCookie = function(name)\n{\n var c = name + "=";\n switch(name.substr(0,3)) {\n case "txt":\n c += escape(config.options[name].toString());\n break;\n case "chk":\n c += config.options[name] ? "true" : "false";\n // is there an option link with this chk ?\n if (config.options[name.substr(3)]) {\n saveOptionCookie(name.substr(3));\n }\n break;\n case "pas":\n if (config.options["chk"+name]) {\n c += escape(config.options[name].toString());\n } else {\n c += "";\n }\n break;\n }\n c += "; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";\n document.cookie = c;\n};\n//}}}\n/***\n!! Initializations\n***/\n//{{{\n// define config.options.pasPassword\nif (!config.options.pasPassword) {\n config.options.pasPassword = 'defaultPassword';\n window.saveOptionCookie('pasPassword');\n}\n// since loadCookies is first called befor password definition\n// we need to reload cookies\nwindow.loadOptionsCookie();\n//}}}\n////===\n\n////+++!![config.macros.upload]\n\n//{{{\nconfig.macros.upload = {\n accessKey: "U",\n formName: "UploadPlugin",\n contentType: "text/html;charset=UTF-8",\n defaultStoreScript: "store.php"\n};\n\n// only this two configs need to be translated\nconfig.macros.upload.messages = {\n aboutToUpload: "About to upload TiddlyWiki to %0",\n backupFileStored: "Previous file backuped in %0",\n crossDomain: "Certainly a cross-domain isue: access to an other site isn't allowed",\n errorDownloading: "Error downloading",\n errorUploadingContent: "Error uploading content",\n fileLocked: "Files is locked: You are not allowed to Upload",\n fileNotFound: "file to upload not found",\n fileNotUploaded: "File %0 NOT uploaded",\n mainFileUploaded: "Main TiddlyWiki file uploaded to %0",\n passwordEmpty: "Unable to upload, your password is empty",\n urlParamMissing: "url param missing",\n rssFileNotUploaded: "RssFile %0 NOT uploaded",\n rssFileUploaded: "Rss File uploaded to %0"\n};\n\nconfig.macros.upload.label = {\n promptOption: "Save and Upload this TiddlyWiki with UploadOptions",\n promptParamMacro: "Save and Upload this TiddlyWiki in %0",\n saveLabel: "save to web", \n saveToDisk: "save to disk",\n uploadLabel: "upload" \n};\n\nconfig.macros.upload.handler = function(place,macroName,params){\n // parameters initialization\n var storeUrl = params[0];\n var toFilename = params[1];\n var backupDir = params[2];\n var uploadDir = params[3];\n var username = params[4];\n var password; // for security reason no password as macro parameter\n var label;\n if (document.location.toString().substr(0,4) == "http")\n label = this.label.saveLabel;\n else\n label = this.label.uploadLabel;\n var prompt;\n if (storeUrl) {\n prompt = this.label.promptParamMacro.toString().format([this.toDirUrl(storeUrl, uploadDir, username)]);\n }\n else {\n prompt = this.label.promptOption;\n }\n createTiddlyButton(place, label, prompt, \n function () {\n config.macros.upload.upload(storeUrl, toFilename, uploadDir, backupDir, username, password); \n return false;}, \n null, null, this.accessKey);\n};\nconfig.macros.upload.UploadLog = function() {\n return new config.lib.Log('UploadLog', " !storeUrl | !uploadDir | !toFilename | !backupdir | !origin |" );\n};\nconfig.macros.upload.UploadLog.prototype = config.lib.Log.prototype;\nconfig.macros.upload.UploadLog.prototype.startUpload = function(storeUrl, toFilename, uploadDir, backupDir) {\n var line = " [[" + config.lib.file.basename(storeUrl) + "|" + storeUrl + "]] | ";\n line += uploadDir + " | " + toFilename + " | " + backupDir + " |";\n this.newLine(line);\n};\nconfig.macros.upload.UploadLog.prototype.endUpload = function() {\n this.addToLine(" Ok |");\n};\nconfig.macros.upload.basename = config.lib.file.basename;\nconfig.macros.upload.dirname = config.lib.file.dirname;\nconfig.macros.upload.toRootUrl = function (storeUrl, username)\n{\n return root = (this.dirname(storeUrl)?this.dirname(storeUrl):this.dirname(document.location.toString()));\n}\nconfig.macros.upload.toDirUrl = function (storeUrl, uploadDir, username)\n{\n var root = this.toRootUrl(storeUrl, username);\n if (uploadDir && uploadDir != '.')\n root = root + '/' + uploadDir;\n return root;\n}\nconfig.macros.upload.toFileUrl = function (storeUrl, toFilename, uploadDir, username)\n{\n return this.toDirUrl(storeUrl, uploadDir, username) + '/' + toFilename;\n}\nconfig.macros.upload.upload = function(storeUrl, toFilename, uploadDir, backupDir, username, password)\n{\n // parameters initialization\n storeUrl = (storeUrl ? storeUrl : config.options.txtUploadStoreUrl);\n toFilename = (toFilename ? toFilename : config.options.txtUploadFilename);\n backupDir = (backupDir ? backupDir : config.options.txtUploadBackupDir);\n uploadDir = (uploadDir ? uploadDir : config.options.txtUploadDir);\n username = (username ? username : config.options.txtUploadUserName);\n password = config.options.pasUploadPassword; // for security reason no password as macro parameter\n if (!password || password === '') {\n alert(config.macros.upload.messages.passwordEmpty);\n return;\n }\n if (storeUrl === '') {\n storeUrl = config.macros.upload.defaultStoreScript;\n }\n if (config.lib.file.dirname(storeUrl) === '') {\n storeUrl = config.lib.file.dirname(document.location.toString())+'/'+storeUrl;\n }\n if (toFilename === '') {\n toFilename = config.lib.file.basename(document.location.toString());\n }\n\n clearMessage();\n // only for forcing the message to display\n if (version.major < 2)\n store.notifyAll();\n if (!storeUrl) {\n alert(config.macros.upload.messages.urlParamMissing);\n return;\n }\n // Check that file is not locked\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n if (BidiX.GroupAuthoring.lock.isLocked() && !BidiX.GroupAuthoring.lock.isMyLock()) {\n alert(config.macros.upload.messages.fileLocked);\n return;\n }\n }\n \n var log = new this.UploadLog();\n log.startUpload(storeUrl, toFilename, uploadDir, backupDir);\n if (document.location.toString().substr(0,5) == "file:") {\n saveChanges();\n }\n var toDir = config.macros.upload.toDirUrl(storeUrl, toFilename, uploadDir, username);\n displayMessage(config.macros.upload.messages.aboutToUpload.format([toDir]), toDir);\n this.uploadChanges(storeUrl, toFilename, uploadDir, backupDir, username, password);\n if(config.options.chkGenerateAnRssFeed) {\n //var rssContent = convertUnicodeToUTF8(generateRss());\n var rssContent = generateRss();\n var rssPath = toFilename.substr(0,toFilename.lastIndexOf(".")) + ".xml";\n this.uploadContent(rssContent, storeUrl, rssPath, uploadDir, '', username, password, \n function (responseText) {\n if (responseText.substring(0,1) != '0') {\n displayMessage(config.macros.upload.messages.rssFileNotUploaded.format([rssPath]));\n }\n else {\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, rssPath, uploadDir, username);\n displayMessage(config.macros.upload.messages.rssFileUploaded.format(\n [toFileUrl]), toFileUrl);\n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n });\n }\n return;\n};\n\nconfig.macros.upload.uploadChanges = function(storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var original;\n if (document.location.toString().substr(0,4) == "http") {\n original =, toFilename, uploadDir, backupDir, username, password);\n return;\n }\n else {\n // standard way : Local file\n \n original = loadFile(getLocalPath(document.location.toString()));\n if(window.Components) {\n // it's a mozilla browser\n try {\n"UniversalXPConnect");\n var converter = Components.classes[""]\n .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);\n converter.charset = "UTF-8";\n original = converter.ConvertToUnicode(original);\n }\n catch(e) {\n }\n }\n }\n //DEBUG alert(original);\n this.uploadChangesFrom(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password);\n};\n\nconfig.macros.upload.uploadChangesFrom = function(original, storeUrl, toFilename, uploadDir, backupDir, \n username, password) {\n var startSaveArea = '<div id="' + 'storeArea">'; // Split up into two so that indexOf() of this source doesn't find it\n var endSaveArea = '</d' + 'iv>';\n // Locate the storeArea div's\n var posOpeningDiv = original.indexOf(startSaveArea);\n var posClosingDiv = original.lastIndexOf(endSaveArea);\n if((posOpeningDiv == -1) || (posClosingDiv == -1))\n {\n alert(config.messages.invalidFileError.format([document.location.toString()]));\n return;\n }\n var revised = original.substr(0,posOpeningDiv + startSaveArea.length) + \n allTiddlersAsHtml() + "\sn\st\st" +\n original.substr(posClosingDiv);\n var newSiteTitle;\n if(version.major < 2){\n newSiteTitle = (getElementText("siteTitle") + " - " + getElementText("siteSubtitle")).htmlEncode();\n } else {\n newSiteTitle = (wikifyPlain ("SiteTitle") + " - " + wikifyPlain ("SiteSubtitle")).htmlEncode();\n }\n\n revised = revised.replaceChunk("<title"+">","</title"+">"," " + newSiteTitle + " ");\n revised = revised.replaceChunk("<!--PRE-HEAD-START--"+">","<!--PRE-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPreHead","") + "\sn");\n revised = revised.replaceChunk("<!--POST-HEAD-START--"+">","<!--POST-HEAD-END--"+">","\sn" + store.getTiddlerText("MarkupPostHead","") + "\sn");\n revised = revised.replaceChunk("<!--PRE-BODY-START--"+">","<!--PRE-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPreBody","") + "\sn");\n revised = revised.replaceChunk("<!--POST-BODY-START--"+">","<!--POST-BODY-END--"+">","\sn" + store.getTiddlerText("MarkupPostBody","") + "\sn");\n\n var response = this.uploadContent(revised, storeUrl, toFilename, uploadDir, backupDir, \n username, password, function (responseText) {\n if (responseText.substring(0,1) != '0') {\n alert(responseText);\n displayMessage(config.macros.upload.messages.fileNotUploaded.format([getLocalPath(document.location.toString())]));\n }\n else {\n if (uploadDir !== '') {\n toFilename = uploadDir + "/" + config.macros.upload.basename(toFilename);\n } else {\n toFilename = config.macros.upload.basename(toFilename);\n }\n var toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n if (responseText.indexOf("destfile:") > 0) {\n var destfile = responseText.substring(responseText.indexOf("destfile:")+9, \n responseText.indexOf("\sn", responseText.indexOf("destfile:")));\n toFileUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + destfile;\n }\n else {\n toFileUrl = config.macros.upload.toFileUrl(storeUrl, toFilename, uploadDir, username);\n }\n displayMessage(config.macros.upload.messages.mainFileUploaded.format(\n [toFileUrl]), toFileUrl);\n if (backupDir && responseText.indexOf("backupfile:") > 0) {\n var backupFile = responseText.substring(responseText.indexOf("backupfile:")+11, \n responseText.indexOf("\sn", responseText.indexOf("backupfile:")));\n toBackupUrl = config.macros.upload.toRootUrl(storeUrl, username) + '/' + backupFile;\n displayMessage(config.macros.upload.messages.backupFileStored.format(\n [toBackupUrl]), toBackupUrl);\n }\n var log = new config.macros.upload.UploadLog();\n log.endUpload();\n store.setDirty(false);\n // erase local lock\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n BidiX.GroupAuthoring.lock.eraseLock();\n // change mtime with new mtime after upload\n var mtime = responseText.substr(responseText.indexOf("mtime:")+6);\n BidiX.GroupAuthoring.lock.mtime = mtime;\n }\n \n \n }\n // for debugging store.php uncomment last line\n //DEBUG alert(responseText);\n }\n );\n};\n\nconfig.macros.upload.uploadContent = function(content, storeUrl, toFilename, uploadDir, backupDir, \n username, password, callbackFn) {\n var boundary = "---------------------------"+"AaB03x"; \n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n if (window.netscape){\n try {\n if (document.location.toString().substr(0,4) != "http") {\n'UniversalBrowserRead');}\n }\n catch (e) {}\n } \n //DEBUG alert("user["+config.options.txtUploadUserName+"] password[" + config.options.pasUploadPassword + "]");\n // compose headers data\n var sheader = "";\n sheader += "--" + boundary + "\sr\snContent-disposition: form-data; name=\s"";\n sheader += config.macros.upload.formName +"\s"\sr\sn\sr\sn";\n sheader += "backupDir="+backupDir\n +";user=" + username \n +";password=" + password\n +";uploaddir=" + uploadDir;\n // add lock attributes to sheader\n if (window.BidiX && BidiX.GroupAuthoring && BidiX.GroupAuthoring.lock) {\n var l = BidiX.GroupAuthoring.lock.myLock;\n sheader += ";lockuser=" + l.user\n + ";mtime=" + l.mtime\n + ";locktime=" + l.locktime;\n }\n sheader += ";;\sr\sn"; \n sheader += "\sr\sn" + "--" + boundary + "\sr\sn";\n sheader += "Content-disposition: form-data; name=\s"userfile\s"; filename=\s""+toFilename+"\s"\sr\sn";\n sheader += "Content-Type: " + config.macros.upload.contentType + "\sr\sn";\n sheader += "Content-Length: " + content.length + "\sr\sn\sr\sn";\n // compose trailer data\n var strailer = new String();\n strailer = "\sr\sn--" + boundary + "--\sr\sn";\n //strailer = "--" + boundary + "--\sr\sn";\n var data;\n data = sheader + content + strailer;\n //"POST", storeUrl, true, username, password);\n try {\n"POST", storeUrl, true); \n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if (request.status == 200)\n callbackFn(request.responseText);\n else\n alert(config.macros.upload.messages.errorUploadingContent + "\snStatus: "+request.status.statusText);\n }\n };\n request.setRequestHeader("Content-Length",data.length);\n request.setRequestHeader("Content-Type","multipart/form-data; boundary="+boundary);\n request.send(data); \n};\n\n\ = function(uploadUrl, uploadToFilename, uploadDir, uploadBackupDir, \n username, password) {\n var request;\n try {\n request = new XMLHttpRequest();\n } \n catch (e) { \n request = new ActiveXObject("Msxml2.XMLHTTP"); \n }\n try {\n if (uploadUrl.substr(0,4) == "http") {\n"UniversalBrowserRead");\n }\n else {\n"UniversalXPConnect");\n }\n } catch (e) { }\n //"GET", document.location.toString(), true, username, password);\n try {\n"GET", document.location.toString(), true);\n }\n catch(e) {\n alert(config.macros.upload.messages.crossDomain + "\snError:" +e);\n exit;\n }\n \n request.onreadystatechange = function () {\n if (request.readyState == 4) {\n if(request.status == 200) {\n config.macros.upload.uploadChangesFrom(request.responseText, uploadUrl, \n uploadToFilename, uploadDir, uploadBackupDir, username, password);\n }\n else\n alert(config.macros.upload.messages.errorDownloading.format(\n [document.location.toString()]) + "\snStatus: "+request.status.statusText);\n }\n };\n request.send(null);\n};\n\n//}}}\n////===\n\n////+++!![Initializations]\n\n//{{{\nconfig.lib.options.init('txtUploadStoreUrl','store.php');\nconfig.lib.options.init('txtUploadFilename','');\nconfig.lib.options.init('txtUploadDir','');\nconfig.lib.options.init('txtUploadBackupDir','');\nconfig.lib.options.init('txtUploadUserName',config.options.txtUserName);\nconfig.lib.options.init('pasUploadPassword','');\nsetStylesheet(\n ".pasOptionInput {width: 11em;}\sn"+\n ".txtOptionInput.txtUploadStoreUrl {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadFilename {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadDir {width: 25em;}\sn"+\n ".txtOptionInput.txtUploadBackupDir {width: 25em;}\sn"+\n "",\n "UploadOptionsStyles");\nconfig.shadowTiddlers.UploadDoc = "[[Full Documentation| ]]\sn"; \nconfig.options.chkAutoSave = false; saveOptionCookie('chkAutoSave');\n\n//}}}\n////===\n\n////+++!![Core Hijacking]\n\n//{{{\nconfig.macros.saveChanges.label_orig_UploadPlugin = config.macros.saveChanges.label;\nconfig.macros.saveChanges.label = config.macros.upload.label.saveToDisk;\n\nconfig.macros.saveChanges.handler_orig_UploadPlugin = config.macros.saveChanges.handler;\n\nconfig.macros.saveChanges.handler = function(place)\n{\n if ((!readOnly) && (document.location.toString().substr(0,4) != "http"))\n createTiddlyButton(place,this.label,this.prompt,this.onClick,null,null,this.accessKey);\n};\n\n//}}}\n////===\n
This document is a ~TiddlyWiki from A ~TiddlyWiki is an electronic notebook that is great for managing todo lists, personal information, and all sorts of things.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //What now?// &nbsp;&nbsp;@@ Before you can save any changes, you need to enter your password in the form below. Then configure privacy and other site settings at your [[control panel|]] (your control panel username is //guus//).\n<<tiddler tiddlyspotControls>>\n@@font-weight:bold;font-size:1.3em;color:#444; //Working online// &nbsp;&nbsp;@@ You can edit this ~TiddlyWiki right now, and save your changes using the "save to web" button in the column on the right.\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Working offline// &nbsp;&nbsp;@@ A fully functioning copy of this ~TiddlyWiki can be saved onto your hard drive or USB stick. You can make changes and save them locally without being connected to the Internet. When you're ready to sync up again, just click "upload" and your ~TiddlyWiki will be saved back to\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Help!// &nbsp;&nbsp;@@ Find out more about ~TiddlyWiki at [[|]]. Also visit [[TiddlyWiki Guides|]] for documentation on learning and using ~TiddlyWiki. New users are especially welcome on the [[TiddlyWiki mailing list|]], which is an excellent place to ask questions and get help. If you have a tiddlyspot related problem email [[tiddlyspot support|]].\n\n@@font-weight:bold;font-size:1.3em;color:#444; //Enjoy :)// &nbsp;&nbsp;@@ We hope you like using your site. Please email [[|]] with any comments or suggestions.
The //world// is the total state of the application. Imagine that all application state can be reached through one global variable called 'world'. Every little change done by the user would result in a complete copy of the state to be made, modified and put into this 'world' variable.\n\nAn actual implementation would probably refer to the old, immutable, world state through an object describing the change the user just made, a 'filter'. Whenever retrieving a value from the world, the implementation would check with the filter first. Obviously this needs to be highly optimized for example by caching the filter results..
I still don't get it. Why do many people want to configure complex apps in XML? There's nothing magical about XML, just a plain text format with some tools available for it. All the fancy features like namespaces are only used in a few advanced applications.\n\nSome apps are configured using a specific XML dialect. This means a parser needs to be written to read the data. Not a trivial task.\n\nOthers are configured using more generic methods like XMLBeans or Spring injection. In both cases the XML closely resembles a series of java calls to constructors and setters to build a set of java objects with a couple of values set in them. The XML file could be replaced with a java method doing just that. The java method would be checked completely during compilation while the XML file will only be checked during runtime. Or did you never have to find out that the package written in a spring.xml file had a minor typo which is completely invisible without an IDE pointing it out for you?\n\nI just had to publish several times to BEA to find all the tiny mistakes I made in a few interrelated XML files (some of them generated through XDoclet). I'm pretty sure that I would have found the same mistakes in a few minutes in a java file where it took me more than half an hour with BEA. Not to mention that most of the mistakes would not have been made in the first place because of autocompletion.\n\nOf course the comparison is not fair: Eclipse has wonderful support for java semantics while it knows preciously little about spring .xml files. But why should it? The question is: why bother with XML files if the support is so much worse than for java files.\n\nOf course the usual reply is that a mere system administrator should be able to change the file and redeploy the application without doing any programming. But in that case a good syntactic and semantic check of that file /before/ deployment is even more important.\n\nIt is true that java doesn't have a good syntax for creating objects. A few improvements (for java 7?) would be named parameters with default values for constructors (and other methods of course). That way a 'super' constructor can be written that can initialize every detail of the object but the caller only needs to specify the parameters for which no good defaults exist or where the value should differ from that default.\n\nThis would make source that initializes a large mesh of connected objects much more readable than it is now.\n\nBTW. I'm not saying that an Injection Container is a bad thing. But you might want to delay it's use until you actually need it. This is also an example of [[KISSALAP|KISSALAP]].
Been playing with yahoo pipes today and it's sheer genius. It is quite amazing what you can do with the DOM and javascript. My gut feeling says that for anything but the most demanding applications traditional GUI frameworks are history. Deployment in a browser is simply too easy nowadays.
| tiddlyspot password:|<<option pasUploadPassword>>|\n| site management:|<<upload index.html . . guus>>//(requires tiddlyspot password)//<<br>>[[control panel|]], [[download (go offline)|]]|\n| links:|[[|]], [[FAQs|]], [[announcements|]], [[blog|]], email [[support|]] & [[feedback|]], [[donate|]]|